ligo/src/proto_alpha/lib_delegate/client_baking_endorsement.ml

206 lines
6.7 KiB
OCaml
Raw Normal View History

2016-09-08 21:13:10 +04:00
(**************************************************************************)
(* *)
2018-02-06 00:17:03 +04:00
(* Copyright (c) 2014 - 2018. *)
2016-09-08 21:13:10 +04:00
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
open Proto_alpha
open Alpha_context
include Logging.Make(struct let name = "client.endorsement" end)
2016-09-08 21:13:10 +04:00
module State = Daemon_state.Make(struct let name = "endorsement" end)
2016-09-08 21:13:10 +04:00
let get_signing_slots cctxt ?(chain = `Main) block delegate level =
Alpha_services.Delegate.Endorsing_rights.get cctxt
~levels:[level]
~delegates:[delegate]
(chain, block) >>=? function
| [{ slots }] -> return (Some slots)
| _ -> return None
2016-09-08 21:13:10 +04:00
let inject_endorsement
(cctxt : #Proto_alpha.full)
2018-06-14 11:31:10 +04:00
?(chain = `Main) block hash level ?async
2018-05-06 04:08:39 +04:00
src_sk pkh =
2018-04-30 21:06:06 +04:00
Alpha_services.Forge.endorsement cctxt
(chain, block)
~branch:hash
~level:level
2016-09-08 21:13:10 +04:00
() >>=? fun bytes ->
2018-06-17 02:07:58 +04:00
Client_keys.append cctxt
src_sk ~watermark:Endorsement bytes >>=? fun signed_bytes ->
Shell_services.Injection.operation cctxt ?async ~chain signed_bytes >>=? fun oph ->
2018-06-18 14:34:03 +04:00
State.record cctxt pkh level >>=? fun () ->
2016-09-08 21:13:10 +04:00
return oph
let check_endorsement cctxt level pkh =
2018-06-18 14:34:03 +04:00
State.get cctxt pkh >>=? function
2018-06-26 13:07:12 +04:00
| None -> return_unit
| Some recorded_level ->
if Raw_level.(level = recorded_level) then
Error_monad.failwith "Level %a already endorsed" Raw_level.pp recorded_level
else
2018-06-26 13:07:12 +04:00
return_unit
2016-09-08 21:13:10 +04:00
2018-05-22 17:22:38 +04:00
let previously_endorsed_level cctxt pkh new_lvl =
State.get cctxt pkh >>=? function
2018-05-22 17:22:38 +04:00
| None -> return false
| Some last_lvl ->
return (Raw_level.(last_lvl >= new_lvl))
2018-05-22 17:22:38 +04:00
let forge_endorsement (cctxt : #Proto_alpha.full)
2018-05-22 17:22:38 +04:00
?(chain = `Main) block ?async
2018-06-19 04:46:20 +04:00
~src_sk src_pk =
let src_pkh = Signature.Public_key.hash src_pk in
Alpha_block_services.metadata cctxt
~chain ~block () >>=? fun { protocol_data = { level = { level } } } ->
2018-05-22 17:22:38 +04:00
check_endorsement cctxt level src_pkh >>=? fun () ->
previously_endorsed_level cctxt src_pkh level >>=? function
| true ->
cctxt#error "Level %a : previously endorsed."
Raw_level.pp level
| false ->
2018-06-14 11:31:10 +04:00
Shell_services.Blocks.hash cctxt ~chain ~block () >>=? fun hash ->
2018-05-06 04:08:39 +04:00
inject_endorsement cctxt ~chain ?async block hash level src_sk src_pkh >>=? fun oph ->
Client_keys.get_key cctxt src_pkh >>=? fun (name, _pk, _sk) ->
cctxt#message
2018-06-19 04:46:20 +04:00
"Injected endorsement level %a, contract %s '%a'"
Raw_level.pp level
name
2018-06-19 04:46:20 +04:00
Operation_hash.pp_short oph >>= fun () ->
return oph
2016-09-08 21:13:10 +04:00
(** Worker *)
type state = {
delegates: public_key_hash list ;
2018-06-06 17:33:28 +04:00
delay: int64 ;
2018-06-19 04:46:20 +04:00
mutable pending: endorsements option ;
2016-09-08 21:13:10 +04:00
}
2018-06-19 04:46:20 +04:00
and endorsements = {
2016-09-08 21:13:10 +04:00
time: Time.t ;
timeout: unit Lwt.t ;
2018-06-19 04:46:20 +04:00
delegates: public_key_hash list ;
2017-11-01 15:07:33 +04:00
block: Client_baking_blocks.block_info ;
2016-09-08 21:13:10 +04:00
}
let create_state delegates delay =
2018-06-19 04:46:20 +04:00
{ delegates ; delay ; pending = None }
2016-09-08 21:13:10 +04:00
let get_delegates cctxt state = match state.delegates with
| [] ->
Client_keys.get_keys cctxt >>=? fun keys ->
let delegates = List.map (fun (_,pkh,_,_) -> pkh) keys in
return delegates
| (_ :: _) as delegates -> return delegates
2018-06-19 04:46:20 +04:00
let endorse_for_delegate cctxt block delegate =
let { Client_baking_blocks.hash ; level } = block in
let b = `Hash (hash, 0) in
Client_keys.get_key cctxt delegate >>=? fun (name, _pk, sk) ->
lwt_debug "Endorsing %a for %s (level %a)!"
Block_hash.pp_short hash name
Raw_level.pp level >>= fun () ->
inject_endorsement cctxt
2018-06-14 11:31:10 +04:00
b hash level
2018-05-06 04:08:39 +04:00
sk delegate >>=? fun oph ->
2018-06-19 08:02:17 +04:00
lwt_log_notice
"Injected endorsement for block '%a' \
(level %a, contract %s) '%a'"
Block_hash.pp_short hash
Raw_level.pp level
name
Operation_hash.pp_short oph >>= fun () ->
2018-06-26 13:07:12 +04:00
return_unit
2018-06-19 04:46:20 +04:00
let allowed_to_endorse cctxt bi delegate =
Client_keys.Public_key_hash.name cctxt delegate >>=? fun name ->
2018-06-19 04:46:20 +04:00
lwt_debug "Checking if allowed to endorse block %a for %s"
Block_hash.pp_short bi.Client_baking_blocks.hash name >>= fun () ->
let b = `Hash (bi.hash, 0) in
let level = bi.level in
get_signing_slots cctxt b delegate level >>=? function
2018-06-19 04:46:20 +04:00
| None | Some [] ->
lwt_debug "No slot found for %a/%s"
2018-06-19 04:46:20 +04:00
Block_hash.pp_short bi.hash name >>= fun () ->
return false
| Some (_ :: _ as slots) ->
lwt_debug "Found slots for %a/%s (%d)"
2018-06-19 04:46:20 +04:00
Block_hash.pp_short bi.hash name (List.length slots) >>= fun () ->
previously_endorsed_level cctxt delegate level >>=? function
| true ->
lwt_debug "Level %a (or higher) previously endorsed: do not endorse."
Raw_level.pp level >>= fun () ->
2018-06-19 04:46:20 +04:00
return false
| false ->
2018-06-19 04:46:20 +04:00
return true
let prepare_endorsement ~(max_past:int64) () (cctxt : #Proto_alpha.full) state bi =
2018-06-19 04:46:20 +04:00
if Time.diff (Time.now ()) bi.Client_baking_blocks.timestamp > max_past then
lwt_log_info "Ignore block %a: forged too far the past"
Block_hash.pp_short bi.hash >>= fun () ->
2018-06-26 13:07:12 +04:00
return_unit
2018-06-19 04:46:20 +04:00
else
lwt_log_info "Received new block %a"
Block_hash.pp_short bi.hash >>= fun () ->
let time = Time.(add (now ()) state.delay) in
let timeout = Lwt_unix.sleep (Int64.to_float state.delay) in
get_delegates cctxt state >>=? fun delegates ->
filter_p (allowed_to_endorse cctxt bi) delegates >>=? fun delegates ->
2018-06-19 04:46:20 +04:00
state.pending <- Some {
time ;
timeout ;
block = bi ;
delegates ;
} ;
2018-06-26 13:07:12 +04:00
return_unit
2016-09-08 21:13:10 +04:00
let compute_timeout state =
2018-06-19 04:46:20 +04:00
match state.pending with
| None -> Lwt_utils.never_ending ()
| Some { timeout ; block ; delegates } ->
timeout >>= fun () ->
Lwt.return (block, delegates)
let check_error f =
f >>= function
| Ok () -> Lwt.return_unit
| Error errs -> lwt_log_error "Error while endorsing:@\n%a" pp_print_error errs
2018-06-06 17:33:28 +04:00
2018-06-13 10:38:16 +04:00
let create
(cctxt: #Proto_alpha.full)
?(max_past=110L)
~delay
delegates
2018-06-14 10:24:33 +04:00
block_stream
=
let state_maker _ _ =
let state = create_state delegates (Int64.of_int delay) in
return state
in
let timeout_k cctxt state (block, delegates) =
state.pending <- None ;
iter_p (endorse_for_delegate cctxt block) delegates
in
let event_k cctxt state bi =
state.pending <- None ;
prepare_endorsement ~max_past () cctxt state bi
in
Client_baking_scheduling.main
~name:"endorser"
~cctxt
~stream:block_stream
~state_maker
~pre_loop:(prepare_endorsement ~max_past ())
~compute_timeout
~timeout_k
~event_k