Client/Endorser: simpler state with lock
This commit is contained in:
parent
abc7b7338c
commit
d33568464a
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user