Client/Endorser: simpler state with lock

This commit is contained in:
Mathias 2018-05-22 15:13:03 +02:00 committed by Grégoire Henry
parent abc7b7338c
commit d33568464a

View File

@ -16,70 +16,58 @@ module State : sig
val get_endorsement: val get_endorsement:
#Client_context.wallet -> #Client_context.wallet ->
Raw_level.t -> Signature.Public_key_hash.t ->
int -> Raw_level.t option tzresult Lwt.t
(Block_hash.t * Operation_hash.t) option tzresult Lwt.t
val record_endorsement: val record_endorsement:
#Client_context.wallet -> #Client_context.wallet ->
Signature.Public_key_hash.t ->
Raw_level.t -> Raw_level.t ->
Block_hash.t -> unit tzresult Lwt.t
int -> Operation_hash.t -> unit tzresult Lwt.t
end = struct end = struct
module LevelMap = Map.Make(Raw_level) type t = (string * Raw_level.t) list
type t = (int * Block_hash.t * Operation_hash.t) list LevelMap.t
let encoding : t Data_encoding.t = let encoding : t Data_encoding.t =
let open Data_encoding in Data_encoding.(
conv list (obj2
(fun x -> LevelMap.bindings x) (req "delegate" string)
(fun l -> (req "last_level" Raw_level.encoding)
List.fold_left ))
(fun x (y, z) -> LevelMap.add y z x)
LevelMap.empty l)
(list (obj2
(req "level" Raw_level.encoding)
(req "endorsement"
(list (obj3
(req "slot" int31)
(req "block" Block_hash.encoding)
(req "operation" Operation_hash.encoding))))))
let name = let name =
"endorsements" "endorsements"
let load (wallet : #Client_context.wallet) = let load (wallet : #Client_context.wallet) =
wallet#load name encoding ~default:LevelMap.empty wallet#load name encoding ~default:[]
let save (wallet : #Client_context.wallet) map = let save (wallet : #Client_context.wallet) list =
wallet#write name encoding map wallet#write name list encoding
let lock = Lwt_mutex.create ()
let get_endorsement (wallet : #Client_context.wallet) level slot = let get_endorsement (wallet : #Client_context.wallet) (delegate_key:Signature.public_key_hash) =
Lwt_mutex.with_lock lock wallet#with_lock
(fun () -> (fun () ->
load wallet >>=? fun map -> load wallet >>=? fun l ->
try return (List.assoc_opt (Signature.Public_key_hash.to_short_b58check delegate_key) l)
let _, block, op = )
LevelMap.find level map
|> List.find (fun (slot',_,_) -> slot = slot') in
return (Some (block, op))
with Not_found -> return None)
let record_endorsement (wallet : #Client_context.wallet) level hash slot oph =
Lwt_mutex.with_lock lock
(fun () ->
load wallet >>=? fun map ->
let previous =
try LevelMap.find level map
with Not_found -> [] in
wallet#write name
(LevelMap.add level ((slot, hash, oph) :: previous) map)
encoding)
let record_endorsement (wallet : #Client_context.wallet) (delegate:Signature.public_key_hash) (new_lvl:Raw_level.t) =
begin
wallet#with_lock (fun () ->
begin
load wallet >>=? fun l ->
let delegate_key = Signature.Public_key_hash.to_short_b58check delegate
in
match List.assoc_opt delegate_key l with
| None ->
save wallet ((delegate_key, new_lvl)::l)
| Some _ ->
save wallet ((delegate_key, new_lvl)::
List.remove_assoc delegate_key l)
end)
end
end end
let get_signing_slots cctxt ?(chain = `Main) block delegate level = let get_signing_slots cctxt ?(chain = `Main) block delegate level =
@ -94,7 +82,7 @@ let get_signing_slots cctxt ?(chain = `Main) block delegate level =
let inject_endorsement let inject_endorsement
(cctxt : #Proto_alpha.full) (cctxt : #Proto_alpha.full)
?(chain = `Main) block level ?async ?(chain = `Main) block level ?async
src_sk slots = src_sk slots pkh =
Shell_services.Blocks.hash cctxt ~chain ~block () >>=? fun hash -> Shell_services.Blocks.hash cctxt ~chain ~block () >>=? fun hash ->
Alpha_services.Forge.endorsement cctxt Alpha_services.Forge.endorsement cctxt
(chain, block) (chain, block)
@ -106,25 +94,17 @@ let inject_endorsement
Client_keys.append cctxt Client_keys.append cctxt
src_sk ~watermark:Endorsement bytes >>=? fun signed_bytes -> src_sk ~watermark:Endorsement bytes >>=? fun signed_bytes ->
Shell_services.Injection.operation cctxt ?async ~chain signed_bytes >>=? fun oph -> Shell_services.Injection.operation cctxt ?async ~chain signed_bytes >>=? fun oph ->
iter_s State.record_endorsement cctxt pkh level >>=? fun () ->
(fun slot ->
State.record_endorsement cctxt level hash slot oph)
slots >>=? fun () ->
return oph return oph
let previously_endorsed_slot cctxt level slot = let check_endorsement cctxt level pkh =
State.get_endorsement cctxt level slot >>=? function State.get_endorsement cctxt pkh >>=? function
| None -> return false
| Some _ -> return true
let check_endorsement cctxt level slot =
State.get_endorsement cctxt level slot >>=? function
| None -> return () | None -> return ()
| Some (block, _) -> | Some recorded_level ->
Error_monad.failwith if Raw_level.(level = recorded_level) then
"Already signed block %a at level %a, slot %d" Error_monad.failwith "Level %a already endorsed" Raw_level.pp recorded_level
Block_hash.pp_short block Raw_level.pp level slot else
return ()
let forge_endorsement (cctxt : #Proto_alpha.full) let forge_endorsement (cctxt : #Proto_alpha.full)
?(chain = `Main) block ?(chain = `Main) block
@ -141,11 +121,8 @@ let forge_endorsement (cctxt : #Proto_alpha.full)
| [] -> cctxt#error "No slot found at level %a" Raw_level.pp level | [] -> cctxt#error "No slot found at level %a" Raw_level.pp level
| slots -> return slots | slots -> return slots
end >>=? fun slots -> end >>=? fun slots ->
iter_s (check_endorsement cctxt level) slots >>=? fun () -> check_endorsement cctxt level src_pkh >>=? fun () ->
inject_endorsement cctxt inject_endorsement cctxt ~chain block level src_sk slots src_pkh
~chain block level
src_sk slots
(** Worker *) (** Worker *)
@ -159,7 +136,7 @@ and endorsement = {
time: Time.t ; time: Time.t ;
delegate: public_key_hash ; delegate: public_key_hash ;
block: Client_baking_blocks.block_info ; block: Client_baking_blocks.block_info ;
slot: int; slots: int list;
} }
let create_state delegates best delay = let create_state delegates best delay =
@ -197,64 +174,59 @@ let schedule_endorsements (cctxt : #Proto_alpha.full) state bi =
let b = `Hash (block.hash, 0) in let b = `Hash (block.hash, 0) in
let level = block.level.level in let level = block.level.level in
get_signing_slots cctxt b delegate level >>=? fun slots -> get_signing_slots cctxt b delegate level >>=? fun slots ->
lwt_debug "Found slots for %a/%s (%d)" lwt_debug "Found %d slots for %a/%s"
Block_hash.pp_short block.hash name (List.length slots) >>= fun () -> (List.length slots) Block_hash.pp_short block.hash name >>= fun () ->
iter_p
(fun slot -> if Fitness.compare state.best.fitness block.fitness < 0 then begin
if Fitness.compare state.best.fitness block.fitness < 0 then begin state.best <- block ;
state.best <- block ; drop_old_endorsement ~before:block.fitness state ;
drop_old_endorsement ~before:block.fitness state ; end ;
end ; begin try
previously_endorsed_slot cctxt level slot >>=? function let same_slot endorsement =
| true -> endorsement.block.level = block.level && endorsement.slots = slots in
lwt_debug "slot %d: previously endorsed." slot >>= fun () -> let old = List.find same_slot state.to_endorse in
return () if Fitness.compare old.block.fitness block.fitness < 0
| false -> then begin
try lwt_log_info
let same_slot e = "Schedule endorsement for block %a \
e.block.level = block.level && e.slot = slot in (level %a, slots { %a }, time %a) (replace block %a)"
let old = List.find same_slot state.to_endorse in Block_hash.pp_short block.hash
if Fitness.compare old.block.fitness block.fitness < 0 Raw_level.pp level
then begin (Format.pp_print_list Format.pp_print_int) slots
lwt_log_info Time.pp_hum time
"Schedule endorsement for block %a \ Block_hash.pp_short old.block.hash
(level %a, slot %d, time %a) (replace block %a)" >>= fun () ->
Block_hash.pp_short block.hash state.to_endorse <-
Raw_level.pp level insert
slot { time ; delegate ; block ; slots }
Time.pp_hum time (List.filter
Block_hash.pp_short old.block.hash (fun e -> not (same_slot e))
>>= fun () -> state.to_endorse) ;
state.to_endorse <- return ()
insert end else begin
{ time ; delegate ; block ; slot } lwt_debug
(List.filter "slot { %a } : better pending endorsement"
(fun e -> not (same_slot e)) (Format.pp_print_list Format.pp_print_int) slots >>= fun () ->
state.to_endorse) ; return ()
return () end
end else begin with Not_found ->
lwt_debug lwt_log_info
"slot %d: better pending endorsement" "Schedule endorsement for block %a \
slot >>= fun () -> (level %a, slot { %a }, time %a)"
return () Block_hash.pp_short block.hash
end Raw_level.pp level
with Not_found -> (Format.pp_print_list Format.pp_print_int) slots
lwt_log_info Time.pp_hum time >>= fun () ->
"Schedule endorsement for block %a \ state.to_endorse <-
(level %a, slot %d, time %a)" insert { time ; delegate ; block ; slots } state.to_endorse ;
Block_hash.pp_short block.hash return ()
Raw_level.pp level end
slot in
Time.pp_hum time >>= fun () ->
state.to_endorse <-
insert { time ; delegate ; block ; slot } state.to_endorse ;
return ())
slots in
let time = Time.(add (now ()) state.delay) in let time = Time.(add (now ()) state.delay) in
get_delegates cctxt state >>=? fun delegates -> get_delegates cctxt state >>=? fun delegates ->
iter_p iter_s
(fun delegate -> (fun delegate -> may_endorse bi delegate time)
may_endorse bi delegate time)
delegates delegates
let schedule_endorsements (cctxt : #Proto_alpha.full) state bis = let schedule_endorsements (cctxt : #Proto_alpha.full) state bis =
@ -278,29 +250,24 @@ let pop_endorsements state =
let endorse cctxt state = let endorse cctxt state =
let to_endorse = pop_endorsements state in let to_endorse = pop_endorsements state in
iter_p iter_p (fun { time = _ ; block ; slots ; delegate } ->
(fun { delegate ; block ; slot } -> let hash = block.hash in
let hash = block.hash in let b = `Hash (hash, 0) in
let b = `Hash (hash, 0) in let level = block.level.level in
let level = block.level.level in Client_keys.get_key cctxt delegate >>=? fun (name, pk, sk) ->
previously_endorsed_slot cctxt level slot >>=? function let pkh = Signature.Public_key.hash pk in
| true -> return () lwt_debug "Endorsing %a for %s (slots : { %a } )!"
| false -> Block_hash.pp_short block.hash name
Client_keys.get_key cctxt delegate >>=? fun (name, _pk, sk) -> (Format.pp_print_list Format.pp_print_int) slots >>= fun () ->
lwt_debug "Endorsing %a for %s (slot %d)!" inject_endorsement cctxt b block.level.level sk slots pkh >>=? fun oph ->
Block_hash.pp_short hash name slot >>= fun () -> cctxt#message
inject_endorsement cctxt "Injected endorsement for block '%a' \
b level (level %a, slots { %a }, contract %s) '%a'"
sk [slot] >>=? fun oph -> Block_hash.pp_short hash
cctxt#message Raw_level.pp level
"Injected endorsement for block '%a' \ (Format.pp_print_list Format.pp_print_int) slots name
(level %a, slot %d, contract %s) '%a'" Operation_hash.pp_short oph >>= fun () -> return ()
Block_hash.pp_short hash ) to_endorse
Raw_level.pp level
slot name
Operation_hash.pp_short oph >>= fun () ->
return ())
to_endorse
let compute_timeout state = let compute_timeout state =
match state.to_endorse with match state.to_endorse with