From 5e7b421ed2720fd3c55f9c25fe4559e15ae7d7f4 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Mon, 10 Dec 2018 18:29:50 +0100 Subject: [PATCH 01/10] Baker: fix small bug in blocks_from_current_cycle --- .../lib_delegate/client_baking_blocks.ml | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_blocks.ml b/src/proto_alpha/lib_delegate/client_baking_blocks.ml index f4c7de12cdab..c78e919b9f02 100644 --- a/src/proto_alpha/lib_delegate/client_baking_blocks.ml +++ b/src/proto_alpha/lib_delegate/client_baking_blocks.ml @@ -176,15 +176,18 @@ let blocks_from_current_cycle cctxt ?(chain = `Main) block ?(offset = 0l) () = return_nil | Error _ as err -> Lwt.return err - | Ok (first, last) -> - let length = Int32.to_int (Int32.sub level (Raw_level.to_int32 first)) in - Shell_services.Blocks.list cctxt ~chain ~heads:[hash] ~length () - >>=? fun blocks -> - let blocks = - List.remove - (length - Int32.to_int (Raw_level.diff last first)) - (List.hd blocks) + | Ok (first, last) -> ( + let length = + 1 + Int32.to_int (Int32.sub level (Raw_level.to_int32 first)) in - if Int32.equal level (Raw_level.to_int32 last) then - return (hash :: blocks) - else return blocks + Shell_services.Blocks.list cctxt ~chain ~heads:[hash] ~length () + >>=? function + | [] -> + return_nil + | blocks -> + let blocks = + List.remove + (length - (1 + Int32.to_int (Raw_level.diff last first))) + (List.hd blocks) + in + return blocks ) -- GitLab From e2ebb820a3e852b633caf7f88c72a1988385e715 Mon Sep 17 00:00:00 2001 From: Vincent Botbol Date: Tue, 9 Apr 2019 14:12:49 +0200 Subject: [PATCH 02/10] Baker: add support for deterministic nonces Co-authored-by: Eugen Zalinescu --- .../lib_delegate/client_baking_forge.ml | 64 +++++++--- .../lib_delegate/client_baking_forge.mli | 16 ++- .../lib_delegate/client_baking_lib.ml | 50 +++++--- .../lib_delegate/client_baking_nonces.ml | 119 +++++++++++++++++- .../lib_delegate/client_baking_nonces.mli | 1 + src/proto_alpha/lib_delegate/logging.ml | 7 ++ src/proto_alpha/lib_delegate/logging.mli | 2 + 7 files changed, 219 insertions(+), 40 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index d36ed6f62072..e46f830652a0 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -105,12 +105,31 @@ let get_delegates cctxt state = | _ -> return state.delegates -let generate_seed_nonce () = - match Nonce.of_bytes (Rand.generate Constants.nonce_length) with - | Error _errs -> - assert false - | Ok nonce -> - nonce +(* Generates a pair (nonce * nonce_hash) either deterministically via + the wallet if it supports it or via a random seed *) +let generate_seed_nonce cctxt delegate level = + Client_keys.get_key cctxt delegate + >>=? fun (_, _, sk) -> + Client_keys.supports_deterministic_nonces sk + >>=? function + | true -> + let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in + Client_keys.deterministic_nonce sk data + >>=? fun bytes -> + Client_keys.deterministic_nonce_hash sk bytes + >>=? fun nonce_hash_bytes -> + let nonce = Nonce.of_bytes bytes in + Lwt.return (Alpha_environment.wrap_error nonce) + >>=? fun nonce -> + let nonce_hash = Nonce_hash.of_bytes_exn nonce_hash_bytes in + return (nonce, nonce_hash) + | false -> + let bytes = Rand.generate Constants.nonce_length in + let nonce = Nonce.of_bytes bytes in + Lwt.return (Alpha_environment.wrap_error nonce) + >>=? fun nonce -> + let nonce_hash = Nonce.hash nonce in + return (nonce, nonce_hash) let forge_block_header (cctxt : #Protocol_client_context.full) ~chain block delegate_sk shell priority seed_nonce_hash = @@ -1277,9 +1296,9 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades assert false (* unreachable *) | Some slot -> return slot ) - >>=? fun slot -> - let seed_nonce = generate_seed_nonce () in - let seed_nonce_hash = Nonce.hash seed_nonce in + >>=? fun ((_, (info, _, delegate)) as slot) -> + generate_seed_nonce cctxt delegate info.level + >>=? fun (seed_nonce, seed_nonce_hash) -> build_block cctxt ~user_activated_upgrades state seed_nonce_hash slot >>=? function | Some (head, priority, shell_header, operations, delegate, seed_nonce_hash) @@ -1340,13 +1359,22 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades -% a operations_tag operations) >>= fun () -> ( if seed_nonce_hash <> None then - cctxt#with_lock (fun () -> - let open Client_baking_nonces in - load cctxt state.nonces_location - >>=? fun nonces -> - let nonces = add nonces block_hash seed_nonce in - save cctxt state.nonces_location nonces) - |> trace_exn (Failure "Error while recording nonce") + Client_keys.get_key cctxt delegate + >>=? fun (_, _, sk) -> + (* Register the nonce only if the user doesn't support + deterministic nonces *) + Client_keys.supports_deterministic_nonces sk + >>=? function + | true -> + return_unit + | false -> + cctxt#with_lock (fun () -> + let open Client_baking_nonces in + load cctxt state.nonces_location + >>=? fun nonces -> + let nonces = add nonces block_hash seed_nonce in + save cctxt state.nonces_location nonces) + |> trace_exn (Failure "Error while recording nonce") else return_unit ) >>=? fun () -> return_unit ) | None -> @@ -1441,7 +1469,7 @@ let compute_best_slot_on_current_level ?max_priority (** [reveal_potential_nonces] reveal registered nonces *) let reveal_potential_nonces (cctxt : #Client_context.full) constants ~chain - ~block = + ~block delegates = cctxt#with_lock (fun () -> Client_baking_files.resolve_location cctxt ~chain `Nonce >>=? fun nonces_location -> @@ -1458,6 +1486,7 @@ let reveal_potential_nonces (cctxt : #Client_context.full) constants ~chain Client_baking_nonces.get_unrevealed_nonces cctxt nonces_location + delegates nonces >>= function | Error err -> @@ -1535,6 +1564,7 @@ let create (cctxt : #Protocol_client_context.full) ~user_activated_upgrades state.constants ~chain ~block:(`Hash (new_head.Client_baking_blocks.hash, 0)) + delegates >>= fun _ignore_nonce_err -> compute_best_slot_on_current_level ?max_priority cctxt state new_head >>=? fun slot -> diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.mli b/src/proto_alpha/lib_delegate/client_baking_forge.mli index c1182e356001..dadb052c50db 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.mli +++ b/src/proto_alpha/lib_delegate/client_baking_forge.mli @@ -26,11 +26,17 @@ open Protocol open Alpha_context -(** [generate_seed_nonce ()] is a random nonce that is typically used - in block headers. When baking, bakers generate random nonces whose - hash is committed in the block they bake. They will typically - reveal the aforementioned nonce during the next cycle. *) -val generate_seed_nonce : unit -> Nonce.t +(** [generate_seed_nonce wallet delegate level] is a nonce that is + typically used in block headers. When baking, bakers generate + random nonces whose hash is committed in the block they bake. They + will typically reveal the aforementioned nonce during the next + cycle. If the wallet supports deterministic nonces, it will instead + generate a deterministic nonce based on the block's level. *) +val generate_seed_nonce : + #Client_context.wallet -> + Signature.Public_key_hash.t -> + Raw_level.t -> + (Nonce.t * Nonce_hash.t) tzresult Lwt.t (** [inject_block cctxt blk ?force ~priority ~timestamp ~fitness ~seed_nonce ~src_sk ops] tries to inject a block in the node. If diff --git a/src/proto_alpha/lib_delegate/client_baking_lib.ml b/src/proto_alpha/lib_delegate/client_baking_lib.ml index 72f1b91a6d81..e5efdba59220 100644 --- a/src/proto_alpha/lib_delegate/client_baking_lib.ml +++ b/src/proto_alpha/lib_delegate/client_baking_lib.ml @@ -39,13 +39,15 @@ let bake_block (cctxt : #Protocol_client_context.full) ?minimal_fees >>=? fun src_sk -> Alpha_services.Helpers.current_level cctxt ~offset:1l (chain, head) >>=? fun level -> - let (seed_nonce, seed_nonce_hash) = - if level.expected_commitment then - let seed_nonce = Client_baking_forge.generate_seed_nonce () in - let seed_nonce_hash = Nonce.hash seed_nonce in - (Some seed_nonce, Some seed_nonce_hash) - else (None, None) - in + ( if level.expected_commitment then + Client_baking_forge.generate_seed_nonce + (cctxt :> #Client_context.wallet) + delegate + level.level + >>=? fun (seed_nonce, seed_nonce_hash) -> + return (Some seed_nonce, Some seed_nonce_hash) + else return (None, None) ) + >>=? fun (seed_nonce, seed_nonce_hash) -> let timestamp = if minimal_timestamp then None else Some Time.System.(to_protocol (Systime_os.now ())) @@ -70,15 +72,26 @@ let bake_block (cctxt : #Protocol_client_context.full) ?minimal_fees | None -> return_unit | Some seed_nonce -> - cctxt#with_lock (fun () -> - let open Client_baking_nonces in - Client_baking_files.resolve_location cctxt ~chain `Nonce - >>=? fun nonces_location -> - load cctxt nonces_location - >>=? fun nonces -> - let nonces = add nonces block_hash seed_nonce in - save cctxt nonces_location nonces) - |> trace_exn (Failure "Error while recording block") ) + if seed_nonce_hash <> None then + let open Client_baking_nonces in + Client_keys.get_key cctxt delegate + >>=? fun (_, _, sk) -> + (* Register the nonce only if the user doesn't support + deterministic nonces *) + Client_keys.supports_deterministic_nonces sk + >>=? function + | true -> + return_unit + | false -> + cctxt#with_lock (fun () -> + Client_baking_files.resolve_location cctxt ~chain `Nonce + >>=? fun nonces_location -> + load cctxt nonces_location + >>=? fun nonces -> + let nonces = add nonces block_hash seed_nonce in + save cctxt nonces_location nonces) + |> trace_exn (Failure "Error while recording block") + else return_unit ) >>=? fun () -> cctxt#message "Injected block %a" Block_hash.pp_short block_hash >>= fun () -> return_unit @@ -155,13 +168,16 @@ let reveal_block_nonces (cctxt : #Protocol_client_context.full) ~chain ~block do_reveal cctxt ~chain ~block nonces let reveal_nonces (cctxt : #Protocol_client_context.full) ~chain ~block () = + Client_keys.get_keys cctxt + >>=? fun keys -> + let delegates = List.map (fun (_, delegate, _, _) -> delegate) keys in let open Client_baking_nonces in cctxt#with_lock (fun () -> Client_baking_files.resolve_location cctxt ~chain `Nonce >>=? fun nonces_location -> load cctxt nonces_location >>=? fun nonces -> - get_unrevealed_nonces cctxt nonces_location nonces + get_unrevealed_nonces cctxt nonces_location delegates nonces >>=? fun nonces_to_reveal -> do_reveal cctxt ~chain ~block nonces_to_reveal >>=? fun () -> diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index 825e8c504a43..5cf5ef9ade73 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -147,7 +147,10 @@ let filter_outdated_nonces cctxt ?constants location nonces = else Lwt.return_unit ) >>= fun () -> return (remove_all nonces outdated_nonces) -let get_unrevealed_nonces cctxt location nonces = +(* May be optimised to check every `blocks_per_commitment` instead of + the whole list (under the hypothesis that blocks_per_commitment + divides blocks_per_cycle) *) +let get_unrevealed_nonces cctxt location delegates nonces = let chain = Client_baking_files.chain location in Client_baking_blocks.blocks_from_current_cycle cctxt @@ -192,3 +195,117 @@ let get_unrevealed_nonces cctxt location nonces = | None -> return_none )) blocks + >>=? fun non_deterministic_nonces -> + map_s + (fun delegate -> + Client_keys.get_key cctxt delegate + >>=? fun keys -> return (delegate, keys)) + delegates + >>=? fun delegates_keys -> + fold_left_s + (fun b (_, (_, _, sk_uri)) -> + Client_keys.supports_deterministic_nonces sk_uri + >>=? fun b' -> return (b || b')) + false + delegates_keys + >>=? fun may_handle_deterministic_nonces -> + if not may_handle_deterministic_nonces then + (* None of the delegate can handle deterministic nonces *) + return non_deterministic_nonces + else + filter_map_s + (fun hash -> + get_block_level_opt cctxt ~chain ~block:(`Hash (hash, 0)) + >>= function + | Some level -> ( + Lwt.return + (Alpha_environment.wrap_error (Raw_level.of_int32 level)) + >>=? fun level -> + Alpha_services.Nonce.get cctxt (chain, `Head 0) level + >>=? function + | Missing included_nonce_hash -> ( + Alpha_block_services.metadata + cctxt + ~chain + ~block:(`Hash (hash, 0)) + () + >>=? fun {Alpha_block_services.protocol_data = {baker}} -> + (* Checks that the baker that included the nonce is one of ours *) + match + List.find_opt + (fun (delegate, (_, _, _)) -> + Signature.Public_key_hash.(delegate = baker)) + delegates_keys + with + | None -> + return_none + | Some (baker_pkh, (_, _, baker_sk)) -> ( + (* It should supports deterministic nonces if it + was not written in the file *) + Client_keys.supports_deterministic_nonces baker_sk + >>=? function + | false -> + return_none + | true -> ( + let data = + Data_encoding.Binary.to_bytes_exn + Raw_level.encoding + level + in + Client_keys.deterministic_nonce_hash baker_sk data + >>=? fun computed_nonce_hash_bytes -> + let computed_nonce_hash = + Data_encoding.Binary.of_bytes_exn + Nonce_hash.encoding + computed_nonce_hash_bytes + in + if + not + Nonce_hash.( + computed_nonce_hash = included_nonce_hash) + then + lwt_log_error + Tag.DSL.( + fun f -> + f + "Inconsistent hash found when computing a \ + deterministic nonce - expected : %a, got %a" + -% t + event + "inconsistent_deterministic_nonce_hash" + -% a Logging.nonce_hash_tag included_nonce_hash + -% a Logging.nonce_hash_tag included_nonce_hash) + >>= fun () -> return_none + else + Client_keys.deterministic_nonce baker_sk data + >>= function + | Ok nonce -> + let nonce = + Data_encoding.Binary.of_bytes_exn + Nonce.encoding + nonce + in + return_some (level, nonce) + | Error _err -> + lwt_log_error + Tag.DSL.( + fun f -> + f + "Cannot create a deterministic nonce \ + for %a" + -% t + event + "unsupported_deterministic_nonce" + -% a + Signature.Public_key_hash.Logging.tag + baker_pkh) + >>= fun () -> return_none ) ) ) + | Forgotten -> + return_none + | Revealed _ -> + return_none ) + | None -> + return_none) + blocks + >>=? fun deterministic_nonces -> + return (non_deterministic_nonces @ deterministic_nonces) diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.mli b/src/proto_alpha/lib_delegate/client_baking_nonces.mli index b5c18d3b6a68..38505b3eaed5 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.mli +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.mli @@ -77,5 +77,6 @@ val filter_outdated_nonces : val get_unrevealed_nonces : #Protocol_client_context.full -> [`Nonce] Client_baking_files.location -> + Signature.Public_key_hash.t list -> t -> (Raw_level.t * Nonce.t) list tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/logging.ml b/src/proto_alpha/lib_delegate/logging.ml index aaa7172e0375..5efae398fe2f 100644 --- a/src/proto_alpha/lib_delegate/logging.ml +++ b/src/proto_alpha/lib_delegate/logging.ml @@ -117,6 +117,13 @@ let nonce_tag = Data_encoding.Json.( fun ppf nonce -> pp ppf (construct Nonce.encoding nonce)) +let nonce_hash_tag = + Tag.def + ~doc:"Nonce hash" + "nonce_hash" + Data_encoding.Json.( + fun ppf nonce -> pp ppf (construct Nonce_hash.encoding nonce)) + let chain_tag = Tag.def ~doc:"Chain selector" diff --git a/src/proto_alpha/lib_delegate/logging.mli b/src/proto_alpha/lib_delegate/logging.mli index 544b2ef25007..2e37b4764979 100644 --- a/src/proto_alpha/lib_delegate/logging.mli +++ b/src/proto_alpha/lib_delegate/logging.mli @@ -68,6 +68,8 @@ val level_tag : Raw_level.t Tag.def val nonce_tag : Nonce.t Tag.def +val nonce_hash_tag : Nonce_hash.t Tag.def + val chain_tag : Block_services.chain Tag.def val block_tag : Block_services.block Tag.def -- GitLab From d35de80a94f6e2e227b5dfa34908f80c48336023 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Thu, 18 Apr 2019 12:20:08 +0200 Subject: [PATCH 03/10] Baker: fix level used when generating nonces --- src/proto_alpha/lib_delegate/client_baking_forge.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index e46f830652a0..1275f5d6d0da 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -1297,7 +1297,8 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades | Some slot -> return slot ) >>=? fun ((_, (info, _, delegate)) as slot) -> - generate_seed_nonce cctxt delegate info.level + let next_level = Raw_level.succ info.level in + generate_seed_nonce cctxt delegate next_level >>=? fun (seed_nonce, seed_nonce_hash) -> build_block cctxt ~user_activated_upgrades state seed_nonce_hash slot >>=? function -- GitLab From b57c5e8fe34b91442ea7827f5e56d2bca990e9c6 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Sun, 14 Apr 2019 13:39:48 +0200 Subject: [PATCH 04/10] Baker: call only deterministic_nonce_hash when committing to a nonce --- .../lib_delegate/client_baking_forge.ml | 37 +-- .../lib_delegate/client_baking_forge.mli | 2 +- .../lib_delegate/client_baking_lib.ml | 46 ++-- .../lib_delegate/client_baking_nonces.ml | 257 +++++++++--------- .../lib_delegate/client_baking_nonces.mli | 8 + 5 files changed, 181 insertions(+), 169 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index 1275f5d6d0da..9ae16be144bd 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -114,22 +114,27 @@ let generate_seed_nonce cctxt delegate level = >>=? function | true -> let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in - Client_keys.deterministic_nonce sk data - >>=? fun bytes -> - Client_keys.deterministic_nonce_hash sk bytes + Client_keys.deterministic_nonce_hash sk data >>=? fun nonce_hash_bytes -> - let nonce = Nonce.of_bytes bytes in - Lwt.return (Alpha_environment.wrap_error nonce) - >>=? fun nonce -> let nonce_hash = Nonce_hash.of_bytes_exn nonce_hash_bytes in - return (nonce, nonce_hash) + lwt_log_notice + Tag.DSL.( + fun f -> + f + "Client_baking_forge.inject_block: generated nonce_hash %a for \ + level %a" + -% t event "generate_seed_nonce" + -% a nonce_hash_tag nonce_hash + -% a level_tag level) + >>= fun () -> + return (Client_baking_nonces.Hash_of_deterministic_nonce nonce_hash) | false -> let bytes = Rand.generate Constants.nonce_length in let nonce = Nonce.of_bytes bytes in - Lwt.return (Alpha_environment.wrap_error nonce) + Lwt.return (Environment.wrap_error nonce) >>=? fun nonce -> let nonce_hash = Nonce.hash nonce in - return (nonce, nonce_hash) + return (Client_baking_nonces.Hash_of_random_nonce (nonce, nonce_hash)) let forge_block_header (cctxt : #Protocol_client_context.full) ~chain block delegate_sk shell priority seed_nonce_hash = @@ -1299,7 +1304,8 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades >>=? fun ((_, (info, _, delegate)) as slot) -> let next_level = Raw_level.succ info.level in generate_seed_nonce cctxt delegate next_level - >>=? fun (seed_nonce, seed_nonce_hash) -> + >>=? fun baker_nonce_hash -> + let seed_nonce_hash = Client_baking_nonces.get_nonce_hash baker_nonce_hash in build_block cctxt ~user_activated_upgrades state seed_nonce_hash slot >>=? function | Some (head, priority, shell_header, operations, delegate, seed_nonce_hash) @@ -1360,17 +1366,14 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades -% a operations_tag operations) >>= fun () -> ( if seed_nonce_hash <> None then - Client_keys.get_key cctxt delegate - >>=? fun (_, _, sk) -> (* Register the nonce only if the user doesn't support deterministic nonces *) - Client_keys.supports_deterministic_nonces sk - >>=? function - | true -> + let open Client_baking_nonces in + match baker_nonce_hash with + | Hash_of_deterministic_nonce _seed_nonce_hash -> return_unit - | false -> + | Hash_of_random_nonce (seed_nonce, _seed_nonce_hash) -> cctxt#with_lock (fun () -> - let open Client_baking_nonces in load cctxt state.nonces_location >>=? fun nonces -> let nonces = add nonces block_hash seed_nonce in diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.mli b/src/proto_alpha/lib_delegate/client_baking_forge.mli index dadb052c50db..44fa87f4dc94 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.mli +++ b/src/proto_alpha/lib_delegate/client_baking_forge.mli @@ -36,7 +36,7 @@ val generate_seed_nonce : #Client_context.wallet -> Signature.Public_key_hash.t -> Raw_level.t -> - (Nonce.t * Nonce_hash.t) tzresult Lwt.t + Client_baking_nonces.baker_nonce_hash tzresult Lwt.t (** [inject_block cctxt blk ?force ~priority ~timestamp ~fitness ~seed_nonce ~src_sk ops] tries to inject a block in the node. If diff --git a/src/proto_alpha/lib_delegate/client_baking_lib.ml b/src/proto_alpha/lib_delegate/client_baking_lib.ml index e5efdba59220..d5fcf1ef012a 100644 --- a/src/proto_alpha/lib_delegate/client_baking_lib.ml +++ b/src/proto_alpha/lib_delegate/client_baking_lib.ml @@ -44,10 +44,13 @@ let bake_block (cctxt : #Protocol_client_context.full) ?minimal_fees (cctxt :> #Client_context.wallet) delegate level.level - >>=? fun (seed_nonce, seed_nonce_hash) -> - return (Some seed_nonce, Some seed_nonce_hash) + >>=? fun baker_nonce_hash -> + let seed_nonce_hash = + Client_baking_nonces.get_nonce_hash baker_nonce_hash + in + return (Some baker_nonce_hash, Some seed_nonce_hash) else return (None, None) ) - >>=? fun (seed_nonce, seed_nonce_hash) -> + >>=? fun (baker_nonce_hash, seed_nonce_hash) -> let timestamp = if minimal_timestamp then None else Some Time.System.(to_protocol (Systime_os.now ())) @@ -68,30 +71,23 @@ let bake_block (cctxt : #Protocol_client_context.full) ?minimal_fees ~delegate_sk:src_sk head >>=? fun block_hash -> - ( match seed_nonce with + let open Client_baking_nonces in + ( match baker_nonce_hash with | None -> return_unit - | Some seed_nonce -> - if seed_nonce_hash <> None then - let open Client_baking_nonces in - Client_keys.get_key cctxt delegate - >>=? fun (_, _, sk) -> - (* Register the nonce only if the user doesn't support - deterministic nonces *) - Client_keys.supports_deterministic_nonces sk - >>=? function - | true -> - return_unit - | false -> - cctxt#with_lock (fun () -> - Client_baking_files.resolve_location cctxt ~chain `Nonce - >>=? fun nonces_location -> - load cctxt nonces_location - >>=? fun nonces -> - let nonces = add nonces block_hash seed_nonce in - save cctxt nonces_location nonces) - |> trace_exn (Failure "Error while recording block") - else return_unit ) + | Some (Hash_of_deterministic_nonce _seed_nonce_hash) -> + return_unit + | Some (Hash_of_random_nonce (seed_nonce, _seed_nonce_hash)) -> + (* Register the nonce only if the user doesn't support + deterministic nonces *) + cctxt#with_lock (fun () -> + Client_baking_files.resolve_location cctxt ~chain `Nonce + >>=? fun nonces_location -> + load cctxt nonces_location + >>=? fun nonces -> + let nonces = add nonces block_hash seed_nonce in + save cctxt nonces_location nonces) + |> trace_exn (Failure "Error while recording block") ) >>=? fun () -> cctxt#message "Injected block %a" Block_hash.pp_short block_hash >>= fun () -> return_unit diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index 5cf5ef9ade73..b7c31b0286d5 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -25,6 +25,7 @@ open Protocol open Alpha_context +module Alpha_block_services = Block_services.Make (Protocol) (Protocol) include Internal_event.Legacy_logging.Make_semantic (struct let name = Protocol.name ^ ".baking.nonces" @@ -32,6 +33,17 @@ end) type t = Nonce.t Block_hash.Map.t +type baker_nonce_hash = + | Hash_of_random_nonce of Nonce.t * Nonce_hash.t (* old method *) + | Hash_of_deterministic_nonce of Nonce_hash.t + +(* new method *) + +let get_nonce_hash = function + | Hash_of_random_nonce (_, nonce_hash) + | Hash_of_deterministic_nonce nonce_hash -> + nonce_hash + let empty = Block_hash.Map.empty let encoding = @@ -147,6 +159,122 @@ let filter_outdated_nonces cctxt ?constants location nonces = else Lwt.return_unit ) >>= fun () -> return (remove_all nonces outdated_nonces) +let get_random_nonce_for_block cctxt chain nonces block_hash = + match find_opt nonces block_hash with + | None -> + return_none + | Some nonce -> ( + get_block_level_opt cctxt ~chain ~block:(`Hash (block_hash, 0)) + >>= function + | Some level -> ( + Lwt.return (Environment.wrap_error (Raw_level.of_int32 level)) + >>=? fun level -> + Alpha_services.Nonce.get cctxt (chain, `Head 0) level + >>=? function + | Missing nonce_hash when Nonce.check_hash nonce nonce_hash -> + lwt_log_notice + Tag.DSL.( + fun f -> + f "Found nonce to reveal for %a (level: %a)" + -% t event "found_nonce" + -% a Block_hash.Logging.tag block_hash + -% a Logging.level_tag level) + >>= fun () -> return_some (level, nonce) + | Missing _nonce_hash -> + lwt_log_error + Tag.DSL.( + fun f -> + f "Incoherent nonce for level %a" + -% t event "bad_nonce" -% a Logging.level_tag level) + >>= fun () -> return_none + | Forgotten -> + return_none + | Revealed _ -> + return_none ) + | None -> + return_none ) + +let get_deterministic_nonce_for_hash cctxt chain delegates_keys block_hash = + get_block_level_opt cctxt ~chain ~block:(`Hash (block_hash, 0)) + >>= function + | None -> + return_none + | Some level -> ( + Lwt.return (Environment.wrap_error (Raw_level.of_int32 level)) + >>=? fun level -> + Alpha_services.Nonce.get cctxt (chain, `Head 0) level + >>=? function + | Forgotten -> + return_none + | Revealed _ -> + return_none + | Missing included_nonce_hash -> ( + Alpha_block_services.metadata + cctxt + ~chain + ~block:(`Hash (block_hash, 0)) + () + >>=? fun {Alpha_block_services.protocol_data = {baker; _}; _} -> + (* Check that the baker that included the nonce is one of ours *) + match + List.find_opt + (fun (delegate, (_, _, _)) -> + Signature.Public_key_hash.(delegate = baker)) + delegates_keys + with + | None -> + return_none + | Some (baker_pkh, (_, _, baker_sk)) -> ( + (* It should support deterministic nonces if it + was not written in the file *) + Client_keys.supports_deterministic_nonces baker_sk + >>=? function + | false -> + return_none + | true -> ( + let data = + Data_encoding.Binary.to_bytes_exn Raw_level.encoding level + in + Client_keys.deterministic_nonce_hash baker_sk data + >>=? fun computed_nonce_hash_bytes -> + let computed_nonce_hash = + Nonce_hash.of_bytes_exn computed_nonce_hash_bytes + in + if not Nonce_hash.(computed_nonce_hash = included_nonce_hash) + then + lwt_log_error + Tag.DSL.( + fun f -> + f + "Inconsistent hash found when computing a \ + deterministic nonce for level %a - expected : \ + %a, got %a" + -% t event "inconsistent_deterministic_nonce_hash" + -% a Logging.level_tag level + -% a Logging.nonce_hash_tag computed_nonce_hash + -% a Logging.nonce_hash_tag included_nonce_hash) + >>= fun () -> return_none + else + Client_keys.deterministic_nonce baker_sk data + >>= function + | Ok nonce -> + let nonce = + Data_encoding.Binary.of_bytes_exn + Nonce.encoding + nonce + in + return_some (level, nonce) + | Error _err -> + lwt_log_error + Tag.DSL.( + fun f -> + f "Cannot create a deterministic nonce for %a" + -% t event "unsupported_deterministic_nonce" + -% a + Signature.Public_key_hash.Logging.tag + baker_pkh) + >>= fun () -> return_none ) ) ) ) + (* May be optimised to check every `blocks_per_commitment` instead of the whole list (under the hypothesis that blocks_per_commitment divides blocks_per_cycle) *) @@ -160,40 +288,7 @@ let get_unrevealed_nonces cctxt location delegates nonces = () >>=? fun blocks -> filter_map_s - (fun hash -> - match find_opt nonces hash with - | None -> - return_none - | Some nonce -> ( - get_block_level_opt cctxt ~chain ~block:(`Hash (hash, 0)) - >>= function - | Some level -> ( - Lwt.return (Environment.wrap_error (Raw_level.of_int32 level)) - >>=? fun level -> - Alpha_services.Nonce.get cctxt (chain, `Head 0) level - >>=? function - | Missing nonce_hash when Nonce.check_hash nonce nonce_hash -> - lwt_log_notice - Tag.DSL.( - fun f -> - f "Found nonce to reveal for %a (level: %a)" - -% t event "found_nonce" - -% a Block_hash.Logging.tag hash - -% a Logging.level_tag level) - >>= fun () -> return_some (level, nonce) - | Missing _nonce_hash -> - lwt_log_error - Tag.DSL.( - fun f -> - f "Incoherent nonce for level %a" - -% t event "bad_nonce" -% a Logging.level_tag level) - >>= fun () -> return_none - | Forgotten -> - return_none - | Revealed _ -> - return_none ) - | None -> - return_none )) + (fun hash -> get_random_nonce_for_block cctxt chain nonces hash) blocks >>=? fun non_deterministic_nonces -> map_s @@ -210,102 +305,12 @@ let get_unrevealed_nonces cctxt location delegates nonces = delegates_keys >>=? fun may_handle_deterministic_nonces -> if not may_handle_deterministic_nonces then - (* None of the delegate can handle deterministic nonces *) + (* None of the delegates can handle deterministic nonces *) return non_deterministic_nonces else filter_map_s (fun hash -> - get_block_level_opt cctxt ~chain ~block:(`Hash (hash, 0)) - >>= function - | Some level -> ( - Lwt.return - (Alpha_environment.wrap_error (Raw_level.of_int32 level)) - >>=? fun level -> - Alpha_services.Nonce.get cctxt (chain, `Head 0) level - >>=? function - | Missing included_nonce_hash -> ( - Alpha_block_services.metadata - cctxt - ~chain - ~block:(`Hash (hash, 0)) - () - >>=? fun {Alpha_block_services.protocol_data = {baker}} -> - (* Checks that the baker that included the nonce is one of ours *) - match - List.find_opt - (fun (delegate, (_, _, _)) -> - Signature.Public_key_hash.(delegate = baker)) - delegates_keys - with - | None -> - return_none - | Some (baker_pkh, (_, _, baker_sk)) -> ( - (* It should supports deterministic nonces if it - was not written in the file *) - Client_keys.supports_deterministic_nonces baker_sk - >>=? function - | false -> - return_none - | true -> ( - let data = - Data_encoding.Binary.to_bytes_exn - Raw_level.encoding - level - in - Client_keys.deterministic_nonce_hash baker_sk data - >>=? fun computed_nonce_hash_bytes -> - let computed_nonce_hash = - Data_encoding.Binary.of_bytes_exn - Nonce_hash.encoding - computed_nonce_hash_bytes - in - if - not - Nonce_hash.( - computed_nonce_hash = included_nonce_hash) - then - lwt_log_error - Tag.DSL.( - fun f -> - f - "Inconsistent hash found when computing a \ - deterministic nonce - expected : %a, got %a" - -% t - event - "inconsistent_deterministic_nonce_hash" - -% a Logging.nonce_hash_tag included_nonce_hash - -% a Logging.nonce_hash_tag included_nonce_hash) - >>= fun () -> return_none - else - Client_keys.deterministic_nonce baker_sk data - >>= function - | Ok nonce -> - let nonce = - Data_encoding.Binary.of_bytes_exn - Nonce.encoding - nonce - in - return_some (level, nonce) - | Error _err -> - lwt_log_error - Tag.DSL.( - fun f -> - f - "Cannot create a deterministic nonce \ - for %a" - -% t - event - "unsupported_deterministic_nonce" - -% a - Signature.Public_key_hash.Logging.tag - baker_pkh) - >>= fun () -> return_none ) ) ) - | Forgotten -> - return_none - | Revealed _ -> - return_none ) - | None -> - return_none) + get_deterministic_nonce_for_hash cctxt chain delegates_keys hash) blocks >>=? fun deterministic_nonces -> return (non_deterministic_nonces @ deterministic_nonces) diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.mli b/src/proto_alpha/lib_delegate/client_baking_nonces.mli index 38505b3eaed5..a7bdda478463 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.mli +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.mli @@ -28,6 +28,14 @@ open Alpha_context type t = Nonce.t Block_hash.Map.t +type baker_nonce_hash = + | Hash_of_random_nonce of Nonce.t * Nonce_hash.t (* old method *) + | Hash_of_deterministic_nonce of Nonce_hash.t + +(* new method *) + +val get_nonce_hash : baker_nonce_hash -> Nonce_hash.t + val encoding : t Data_encoding.t val empty : t -- GitLab From 9080dc5e7dc81d8ef7007a98c17af2e6f0f3755b Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Fri, 19 Apr 2019 12:42:34 +0200 Subject: [PATCH 05/10] Baker: try only one method when revealing nonces --- .../lib_delegate/client_baking_nonces.ml | 254 +++++++++--------- 1 file changed, 130 insertions(+), 124 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index b7c31b0286d5..1ea06d7c8a49 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -159,42 +159,102 @@ let filter_outdated_nonces cctxt ?constants location nonces = else Lwt.return_unit ) >>= fun () -> return (remove_all nonces outdated_nonces) -let get_random_nonce_for_block cctxt chain nonces block_hash = - match find_opt nonces block_hash with - | None -> +let get_random_nonce_for_block cctxt chain block_hash level nonce = + Alpha_services.Nonce.get cctxt (chain, `Head 0) level + >>=? function + | Missing nonce_hash when Nonce.check_hash nonce nonce_hash -> + lwt_log_notice + Tag.DSL.( + fun f -> + f "Found nonce to reveal for %a (level: %a)" + -% t event "found_nonce" + -% a Block_hash.Logging.tag block_hash + -% a Logging.level_tag level) + >>= fun () -> return_some (level, nonce) + | Missing _nonce_hash -> + lwt_log_error + Tag.DSL.( + fun f -> + f "Incoherent nonce for level %a" + -% t event "bad_nonce" -% a Logging.level_tag level) + >>= fun () -> return_none + | Forgotten -> return_none - | Some nonce -> ( - get_block_level_opt cctxt ~chain ~block:(`Hash (block_hash, 0)) - >>= function - | Some level -> ( - Lwt.return (Environment.wrap_error (Raw_level.of_int32 level)) - >>=? fun level -> - Alpha_services.Nonce.get cctxt (chain, `Head 0) level + | Revealed _ -> + return_none + +let get_deterministic_nonce_for_hash cctxt chain delegates_keys block_hash + level = + Alpha_services.Nonce.get cctxt (chain, `Head 0) level + >>=? function + | Forgotten -> + return_none + | Revealed _ -> + return_none + | Missing included_nonce_hash -> ( + Alpha_block_services.metadata + cctxt + ~chain + ~block:(`Hash (block_hash, 0)) + () + >>=? fun {Alpha_block_services.protocol_data = {baker; _}; _} -> + (* Check that the baker that included the nonce is one of ours *) + match + List.find_opt + (fun (delegate, (_, _, _)) -> + Signature.Public_key_hash.(delegate = baker)) + delegates_keys + with + | None -> + return_none + | Some (baker_pkh, (_, _, baker_sk)) -> ( + (* It should support deterministic nonces if it + was not written in the file *) + Client_keys.supports_deterministic_nonces baker_sk >>=? function - | Missing nonce_hash when Nonce.check_hash nonce nonce_hash -> - lwt_log_notice - Tag.DSL.( - fun f -> - f "Found nonce to reveal for %a (level: %a)" - -% t event "found_nonce" - -% a Block_hash.Logging.tag block_hash - -% a Logging.level_tag level) - >>= fun () -> return_some (level, nonce) - | Missing _nonce_hash -> - lwt_log_error - Tag.DSL.( - fun f -> - f "Incoherent nonce for level %a" - -% t event "bad_nonce" -% a Logging.level_tag level) - >>= fun () -> return_none - | Forgotten -> + | false -> return_none - | Revealed _ -> - return_none ) - | None -> - return_none ) + | true -> ( + let data = + Data_encoding.Binary.to_bytes_exn Raw_level.encoding level + in + Client_keys.deterministic_nonce_hash baker_sk data + >>=? fun computed_nonce_hash_bytes -> + let computed_nonce_hash = + Nonce_hash.of_bytes_exn computed_nonce_hash_bytes + in + if not Nonce_hash.(computed_nonce_hash = included_nonce_hash) + then + lwt_log_error + Tag.DSL.( + fun f -> + f + "Inconsistent hash found when computing a \ + deterministic nonce for level %a - expected : %a, \ + got %a" + -% t event "inconsistent_deterministic_nonce_hash" + -% a Logging.level_tag level + -% a Logging.nonce_hash_tag computed_nonce_hash + -% a Logging.nonce_hash_tag included_nonce_hash) + >>= fun () -> return_none + else + Client_keys.deterministic_nonce baker_sk data + >>= function + | Ok nonce -> + let nonce = + Data_encoding.Binary.of_bytes_exn Nonce.encoding nonce + in + return_some (level, nonce) + | Error _err -> + lwt_log_error + Tag.DSL.( + fun f -> + f "Cannot create a deterministic nonce for %a" + -% t event "unsupported_deterministic_nonce" + -% a Signature.Public_key_hash.Logging.tag baker_pkh) + >>= fun () -> return_none ) ) ) -let get_deterministic_nonce_for_hash cctxt chain delegates_keys block_hash = +let get_nonce_for_block cctxt chain nonces delegates_keys block_hash = get_block_level_opt cctxt ~chain ~block:(`Hash (block_hash, 0)) >>= function | None -> @@ -202,78 +262,42 @@ let get_deterministic_nonce_for_hash cctxt chain delegates_keys block_hash = | Some level -> ( Lwt.return (Environment.wrap_error (Raw_level.of_int32 level)) >>=? fun level -> - Alpha_services.Nonce.get cctxt (chain, `Head 0) level - >>=? function - | Forgotten -> - return_none - | Revealed _ -> - return_none - | Missing included_nonce_hash -> ( - Alpha_block_services.metadata + match find_opt nonces block_hash with + | Some nonce -> + get_random_nonce_for_block cctxt chain block_hash level nonce + | None -> + get_deterministic_nonce_for_hash cctxt - ~chain - ~block:(`Hash (block_hash, 0)) - () - >>=? fun {Alpha_block_services.protocol_data = {baker; _}; _} -> - (* Check that the baker that included the nonce is one of ours *) - match - List.find_opt - (fun (delegate, (_, _, _)) -> - Signature.Public_key_hash.(delegate = baker)) - delegates_keys - with - | None -> - return_none - | Some (baker_pkh, (_, _, baker_sk)) -> ( - (* It should support deterministic nonces if it - was not written in the file *) - Client_keys.supports_deterministic_nonces baker_sk - >>=? function - | false -> - return_none - | true -> ( - let data = - Data_encoding.Binary.to_bytes_exn Raw_level.encoding level - in - Client_keys.deterministic_nonce_hash baker_sk data - >>=? fun computed_nonce_hash_bytes -> - let computed_nonce_hash = - Nonce_hash.of_bytes_exn computed_nonce_hash_bytes - in - if not Nonce_hash.(computed_nonce_hash = included_nonce_hash) - then - lwt_log_error - Tag.DSL.( - fun f -> - f - "Inconsistent hash found when computing a \ - deterministic nonce for level %a - expected : \ - %a, got %a" - -% t event "inconsistent_deterministic_nonce_hash" - -% a Logging.level_tag level - -% a Logging.nonce_hash_tag computed_nonce_hash - -% a Logging.nonce_hash_tag included_nonce_hash) - >>= fun () -> return_none - else - Client_keys.deterministic_nonce baker_sk data - >>= function - | Ok nonce -> - let nonce = - Data_encoding.Binary.of_bytes_exn - Nonce.encoding - nonce - in - return_some (level, nonce) - | Error _err -> - lwt_log_error - Tag.DSL.( - fun f -> - f "Cannot create a deterministic nonce for %a" - -% t event "unsupported_deterministic_nonce" - -% a - Signature.Public_key_hash.Logging.tag - baker_pkh) - >>= fun () -> return_none ) ) ) ) + chain + delegates_keys + block_hash + level ) + +(* let get_unrevealed_nonces cctxt location delegates nonces = + * let chain = Client_baking_files.chain location in + * Client_baking_blocks.blocks_from_current_cycle cctxt + * ~chain (`Head 0) + * ~offset:(-1l) () >>=? fun blocks -> + * filter_map_s + * (fun hash -> get_random_nonce_for_block cctxt chain nonces hash) + * blocks >>=? fun non_deterministic_nonces -> + * map_s + * (fun delegate -> + * Client_keys.get_key cctxt delegate >>=? fun keys -> + * return (delegate, keys)) + * delegates >>=? fun delegates_keys -> + * fold_left_s (fun b (_, (_, _, sk_uri)) -> + * Client_keys.supports_deterministic_nonces sk_uri >>=? fun b' -> + * return (b || b') + * ) false delegates_keys >>=? fun may_handle_deterministic_nonces -> + * if not may_handle_deterministic_nonces then + * (\* None of the delegates can handle deterministic nonces *\) + * return non_deterministic_nonces + * else + * filter_map_s (fun hash -> + * get_deterministic_nonce_for_hash cctxt chain delegates_keys hash + * ) blocks >>=? fun deterministic_nonces -> + * return (non_deterministic_nonces @ deterministic_nonces) *) (* May be optimised to check every `blocks_per_commitment` instead of the whole list (under the hypothesis that blocks_per_commitment @@ -287,30 +311,12 @@ let get_unrevealed_nonces cctxt location delegates nonces = ~offset:(-1l) () >>=? fun blocks -> - filter_map_s - (fun hash -> get_random_nonce_for_block cctxt chain nonces hash) - blocks - >>=? fun non_deterministic_nonces -> map_s (fun delegate -> Client_keys.get_key cctxt delegate >>=? fun keys -> return (delegate, keys)) delegates >>=? fun delegates_keys -> - fold_left_s - (fun b (_, (_, _, sk_uri)) -> - Client_keys.supports_deterministic_nonces sk_uri - >>=? fun b' -> return (b || b')) - false - delegates_keys - >>=? fun may_handle_deterministic_nonces -> - if not may_handle_deterministic_nonces then - (* None of the delegates can handle deterministic nonces *) - return non_deterministic_nonces - else - filter_map_s - (fun hash -> - get_deterministic_nonce_for_hash cctxt chain delegates_keys hash) - blocks - >>=? fun deterministic_nonces -> - return (non_deterministic_nonces @ deterministic_nonces) + filter_map_s + (fun hash -> get_nonce_for_block cctxt chain nonces delegates_keys hash) + blocks -- GitLab From ee30b7dbe5bae23c35f0f33b9b71bda02d7fc81d Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Fri, 19 Apr 2019 13:16:21 +0200 Subject: [PATCH 06/10] Baker: generate nonce_hash only at commit level --- .../lib_delegate/client_baking_forge.ml | 42 ++++++++++++------- .../lib_delegate/client_baking_nonces.ml | 26 ------------ 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index 9ae16be144bd..691e0e4867d8 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -1102,16 +1102,11 @@ let fetch_operations (cctxt : #Protocol_client_context.full) ~chain with consistent operations that went through the client-side validation *) let build_block cctxt ~user_activated_upgrades state seed_nonce_hash - ((slot_timestamp, (bi, priority, delegate)) as slot) = - let chain = `Hash bi.Client_baking_blocks.chain_id in - let block = `Hash (bi.hash, 0) in - Alpha_services.Helpers.current_level cctxt ~offset:1l (chain, block) - >>=? fun next_level -> - let seed_nonce_hash = - if next_level.Level.expected_commitment then Some seed_nonce_hash else None - in + ((slot_timestamp, (bi, priority, delegate)) as slot) next_level = Client_keys.Public_key_hash.name cctxt delegate >>=? fun name -> + let chain = `Hash bi.Client_baking_blocks.chain_id in + let block = `Hash (bi.hash, 0) in lwt_debug Tag.DSL.( fun f -> @@ -1302,11 +1297,26 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades | Some slot -> return slot ) >>=? fun ((_, (info, _, delegate)) as slot) -> - let next_level = Raw_level.succ info.level in - generate_seed_nonce cctxt delegate next_level - >>=? fun baker_nonce_hash -> - let seed_nonce_hash = Client_baking_nonces.get_nonce_hash baker_nonce_hash in - build_block cctxt ~user_activated_upgrades state seed_nonce_hash slot + let chain_id = `Hash info.Client_baking_blocks.chain_id in + let block = `Hash (info.hash, 0) in + Alpha_services.Helpers.current_level cctxt ~offset:1l (chain_id, block) + >>=? fun next_level -> + ( if next_level.Level.expected_commitment then + generate_seed_nonce cctxt delegate next_level.level + >>=? fun baker_nonce_hash -> + let seed_nonce_hash = + Client_baking_nonces.get_nonce_hash baker_nonce_hash + in + return (Some baker_nonce_hash, Some seed_nonce_hash) + else return (None, None) ) + >>=? fun (baker_nonce_hash, seed_nonce_hash) -> + build_block + cctxt + ~user_activated_upgrades + state + seed_nonce_hash + slot + next_level >>=? function | Some (head, priority, shell_header, operations, delegate, seed_nonce_hash) -> ( @@ -1370,9 +1380,11 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades deterministic nonces *) let open Client_baking_nonces in match baker_nonce_hash with - | Hash_of_deterministic_nonce _seed_nonce_hash -> + | None -> + return_unit + | Some (Hash_of_deterministic_nonce _seed_nonce_hash) -> return_unit - | Hash_of_random_nonce (seed_nonce, _seed_nonce_hash) -> + | Some (Hash_of_random_nonce (seed_nonce, _seed_nonce_hash)) -> cctxt#with_lock (fun () -> load cctxt state.nonces_location >>=? fun nonces -> diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index 1ea06d7c8a49..0f954d95ddd1 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -273,32 +273,6 @@ let get_nonce_for_block cctxt chain nonces delegates_keys block_hash = block_hash level ) -(* let get_unrevealed_nonces cctxt location delegates nonces = - * let chain = Client_baking_files.chain location in - * Client_baking_blocks.blocks_from_current_cycle cctxt - * ~chain (`Head 0) - * ~offset:(-1l) () >>=? fun blocks -> - * filter_map_s - * (fun hash -> get_random_nonce_for_block cctxt chain nonces hash) - * blocks >>=? fun non_deterministic_nonces -> - * map_s - * (fun delegate -> - * Client_keys.get_key cctxt delegate >>=? fun keys -> - * return (delegate, keys)) - * delegates >>=? fun delegates_keys -> - * fold_left_s (fun b (_, (_, _, sk_uri)) -> - * Client_keys.supports_deterministic_nonces sk_uri >>=? fun b' -> - * return (b || b') - * ) false delegates_keys >>=? fun may_handle_deterministic_nonces -> - * if not may_handle_deterministic_nonces then - * (\* None of the delegates can handle deterministic nonces *\) - * return non_deterministic_nonces - * else - * filter_map_s (fun hash -> - * get_deterministic_nonce_for_hash cctxt chain delegates_keys hash - * ) blocks >>=? fun deterministic_nonces -> - * return (non_deterministic_nonces @ deterministic_nonces) *) - (* May be optimised to check every `blocks_per_commitment` instead of the whole list (under the hypothesis that blocks_per_commitment divides blocks_per_cycle) *) -- GitLab From bf21faa16211224cb37cee1c9bc0f6e0fdb62ddf Mon Sep 17 00:00:00 2001 From: Vincent Botbol Date: Thu, 25 Apr 2019 17:07:25 -0400 Subject: [PATCH 07/10] Baker: moves nonce generation to client_baking_nonces --- .../lib_delegate/client_baking_forge.ml | 33 +--------------- .../lib_delegate/client_baking_forge.mli | 12 ------ .../lib_delegate/client_baking_lib.ml | 2 +- .../lib_delegate/client_baking_nonces.ml | 39 +++++++++++++++++++ .../lib_delegate/client_baking_nonces.mli | 12 ++++++ 5 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index 691e0e4867d8..193d50a4885e 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -105,37 +105,6 @@ let get_delegates cctxt state = | _ -> return state.delegates -(* Generates a pair (nonce * nonce_hash) either deterministically via - the wallet if it supports it or via a random seed *) -let generate_seed_nonce cctxt delegate level = - Client_keys.get_key cctxt delegate - >>=? fun (_, _, sk) -> - Client_keys.supports_deterministic_nonces sk - >>=? function - | true -> - let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in - Client_keys.deterministic_nonce_hash sk data - >>=? fun nonce_hash_bytes -> - let nonce_hash = Nonce_hash.of_bytes_exn nonce_hash_bytes in - lwt_log_notice - Tag.DSL.( - fun f -> - f - "Client_baking_forge.inject_block: generated nonce_hash %a for \ - level %a" - -% t event "generate_seed_nonce" - -% a nonce_hash_tag nonce_hash - -% a level_tag level) - >>= fun () -> - return (Client_baking_nonces.Hash_of_deterministic_nonce nonce_hash) - | false -> - let bytes = Rand.generate Constants.nonce_length in - let nonce = Nonce.of_bytes bytes in - Lwt.return (Environment.wrap_error nonce) - >>=? fun nonce -> - let nonce_hash = Nonce.hash nonce in - return (Client_baking_nonces.Hash_of_random_nonce (nonce, nonce_hash)) - let forge_block_header (cctxt : #Protocol_client_context.full) ~chain block delegate_sk shell priority seed_nonce_hash = Client_baking_pow.mine cctxt chain block shell (fun proof_of_work_nonce -> @@ -1302,7 +1271,7 @@ let bake (cctxt : #Protocol_client_context.full) ~user_activated_upgrades Alpha_services.Helpers.current_level cctxt ~offset:1l (chain_id, block) >>=? fun next_level -> ( if next_level.Level.expected_commitment then - generate_seed_nonce cctxt delegate next_level.level + Client_baking_nonces.generate_nonce cctxt delegate next_level.level >>=? fun baker_nonce_hash -> let seed_nonce_hash = Client_baking_nonces.get_nonce_hash baker_nonce_hash diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.mli b/src/proto_alpha/lib_delegate/client_baking_forge.mli index 44fa87f4dc94..9a5ec153ad15 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.mli +++ b/src/proto_alpha/lib_delegate/client_baking_forge.mli @@ -26,18 +26,6 @@ open Protocol open Alpha_context -(** [generate_seed_nonce wallet delegate level] is a nonce that is - typically used in block headers. When baking, bakers generate - random nonces whose hash is committed in the block they bake. They - will typically reveal the aforementioned nonce during the next - cycle. If the wallet supports deterministic nonces, it will instead - generate a deterministic nonce based on the block's level. *) -val generate_seed_nonce : - #Client_context.wallet -> - Signature.Public_key_hash.t -> - Raw_level.t -> - Client_baking_nonces.baker_nonce_hash tzresult Lwt.t - (** [inject_block cctxt blk ?force ~priority ~timestamp ~fitness ~seed_nonce ~src_sk ops] tries to inject a block in the node. If [?force] is set, the fitness check will be bypassed. [priority] diff --git a/src/proto_alpha/lib_delegate/client_baking_lib.ml b/src/proto_alpha/lib_delegate/client_baking_lib.ml index d5fcf1ef012a..df1c1feb1f44 100644 --- a/src/proto_alpha/lib_delegate/client_baking_lib.ml +++ b/src/proto_alpha/lib_delegate/client_baking_lib.ml @@ -40,7 +40,7 @@ let bake_block (cctxt : #Protocol_client_context.full) ?minimal_fees Alpha_services.Helpers.current_level cctxt ~offset:1l (chain, head) >>=? fun level -> ( if level.expected_commitment then - Client_baking_forge.generate_seed_nonce + Client_baking_nonces.generate_nonce (cctxt :> #Client_context.wallet) delegate level.level diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index 0f954d95ddd1..d4180e2ba81b 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -39,6 +39,45 @@ type baker_nonce_hash = (* new method *) +(* Generates a pair (nonce * nonce_hash) either deterministically via + the wallet if it supports it or via a random seed *) +let generate_nonce cctxt delegate level = + Client_keys.get_key cctxt delegate + >>=? fun (_, _, sk) -> + Client_keys.supports_deterministic_nonces sk + >>=? function + | true -> + let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in + Client_keys.deterministic_nonce_hash sk data + >>=? fun nonce_hash_bytes -> + let nonce_hash = Nonce_hash.of_bytes_exn nonce_hash_bytes in + lwt_log_notice + Tag.DSL.( + fun f -> + f + "Client_baking_forge.inject_block: generated deterministic \ + nonce hash %a for level %a" + -% t event "generate_deterministic_nonce" + -% a Logging.nonce_hash_tag nonce_hash + -% a Logging.level_tag level) + >>= fun () -> return (Hash_of_deterministic_nonce nonce_hash) + | false -> + let bytes = Rand.generate Constants.nonce_length in + let nonce = Nonce.of_bytes bytes in + Lwt.return (Environment.wrap_error nonce) + >>=? fun nonce -> + let nonce_hash = Nonce.hash nonce in + lwt_log_notice + Tag.DSL.( + fun f -> + f + "Client_baking_forge.inject_block: generated seed nonce %a for \ + level %a" + -% t event "generate_seed_nonce" + -% a Logging.nonce_hash_tag nonce_hash + -% a Logging.level_tag level) + >>= fun () -> return (Hash_of_random_nonce (nonce, nonce_hash)) + let get_nonce_hash = function | Hash_of_random_nonce (_, nonce_hash) | Hash_of_deterministic_nonce nonce_hash -> diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.mli b/src/proto_alpha/lib_delegate/client_baking_nonces.mli index a7bdda478463..cd996ba7592b 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.mli +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.mli @@ -34,6 +34,18 @@ type baker_nonce_hash = (* new method *) +(** [generate_seed_nonce wallet delegate level] is a nonce that is + typically used in block headers. When baking, bakers generate + random nonces whose hash is commited in the block they bake. They + will typically reveal the aforementionned nonce during the next + cycle. If the wallet supports deterministic nonces, it will instead + generate a deterministic nonce based on the block's level. *) +val generate_nonce : + #Client_context.wallet -> + Signature.Public_key_hash.t -> + Raw_level.t -> + baker_nonce_hash tzresult Lwt.t + val get_nonce_hash : baker_nonce_hash -> Nonce_hash.t val encoding : t Data_encoding.t -- GitLab From 1a53ba58903aaba828bade478bf7b8ed2596fbac Mon Sep 17 00:00:00 2001 From: Vincent Botbol Date: Thu, 25 Apr 2019 17:11:28 -0400 Subject: [PATCH 08/10] Baker: limit the requests to the signer --- .../lib_delegate/client_baking_nonces.ml | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/proto_alpha/lib_delegate/client_baking_nonces.ml b/src/proto_alpha/lib_delegate/client_baking_nonces.ml index d4180e2ba81b..4535d1a948a7 100644 --- a/src/proto_alpha/lib_delegate/client_baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/client_baking_nonces.ml @@ -257,41 +257,38 @@ let get_deterministic_nonce_for_hash cctxt chain delegates_keys block_hash let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in - Client_keys.deterministic_nonce_hash baker_sk data - >>=? fun computed_nonce_hash_bytes -> - let computed_nonce_hash = - Nonce_hash.of_bytes_exn computed_nonce_hash_bytes - in - if not Nonce_hash.(computed_nonce_hash = included_nonce_hash) - then - lwt_log_error - Tag.DSL.( - fun f -> - f - "Inconsistent hash found when computing a \ - deterministic nonce for level %a - expected : %a, \ - got %a" - -% t event "inconsistent_deterministic_nonce_hash" - -% a Logging.level_tag level - -% a Logging.nonce_hash_tag computed_nonce_hash - -% a Logging.nonce_hash_tag included_nonce_hash) - >>= fun () -> return_none - else - Client_keys.deterministic_nonce baker_sk data - >>= function - | Ok nonce -> - let nonce = - Data_encoding.Binary.of_bytes_exn Nonce.encoding nonce - in - return_some (level, nonce) - | Error _err -> + Client_keys.deterministic_nonce baker_sk data + >>= function + | Ok nonce -> + let nonce = + Data_encoding.Binary.of_bytes_exn + Nonce.encoding + (Bigstring.to_bytes nonce) + in + let computed_nonce_hash = Nonce.hash nonce in + if Nonce_hash.(computed_nonce_hash = included_nonce_hash) + then return_some (level, nonce) + else lwt_log_error Tag.DSL.( fun f -> - f "Cannot create a deterministic nonce for %a" - -% t event "unsupported_deterministic_nonce" - -% a Signature.Public_key_hash.Logging.tag baker_pkh) - >>= fun () -> return_none ) ) ) + f + "Inconsistent hash found when computing a \ + deterministic nonce for level %a - expected : \ + %a, got %a" + -% t event "inconsistent_deterministic_nonce_hash" + -% a Logging.level_tag level + -% a Logging.nonce_hash_tag computed_nonce_hash + -% a Logging.nonce_hash_tag included_nonce_hash) + >>= fun () -> return_none + | Error _err -> + lwt_log_error + Tag.DSL.( + fun f -> + f "Cannot create a deterministic nonce for %a" + -% t event "unsupported_deterministic_nonce" + -% a Signature.Public_key_hash.Logging.tag baker_pkh) + >>= fun () -> return_none ) ) ) let get_nonce_for_block cctxt chain nonces delegates_keys block_hash = get_block_level_opt cctxt ~chain ~block:(`Hash (block_hash, 0)) -- GitLab From ef757d7160655a81e373cb52a91d039f21eac784 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Wed, 6 May 2020 13:14:11 +0200 Subject: [PATCH 09/10] Tests/Python: add tests for baker using deterministic nonces --- tests_python/client/client.py | 39 ++++ tests_python/launchers/sandbox.py | 24 ++- .../tests/ledger/test_deterministic_nonces.py | 122 ++++++++++++ .../multibranch/test_deterministic_nonces.py | 174 ++++++++++++++++++ 4 files changed, 351 insertions(+), 8 deletions(-) create mode 100644 tests_python/tests/ledger/test_deterministic_nonces.py create mode 100644 tests_python/tests/multibranch/test_deterministic_nonces.py diff --git a/tests_python/client/client.py b/tests_python/client/client.py index 8195aa38ffc6..60c34cf3ad2e 100644 --- a/tests_python/client/client.py +++ b/tests_python/client/client.py @@ -495,6 +495,45 @@ class Client: rpc_res = self.get_checkpoint() return rpc_res['caboose'] + def get_cycle(self, params: List[str] = None) -> str: + rpc_res = self.rpc('get', '/chains/main/blocks/head/metadata', + params=params) + return rpc_res['level']['cycle'] + + def get_nonces_from_file(self): + nonces_file = os.path.join(self.base_dir, "nonces") + if os.path.isfile(nonces_file): + with open(nonces_file) as json_file: + return json.load(json_file) + else: + return [] + + def get_nonces_status(self, cycle): + rpc_res = self.rpc( + 'get', '/chains/main/blocks/head/context/raw/json/cycle/' + f'{cycle}/nonces?depth=1') + revealed = [] + unrevealed = [] + for info in rpc_res: + level = info[0] + if isinstance(info[1], list): + assert len(info[1]) == 4 + # at position 1, we find the baker's address + unrevealed.append((level, info[1][1])) + else: + revealed.append(level) + return {"revealed": revealed, "unrevealed": unrevealed} + + def get_baker(self, level): + rpc_res = self.rpc('get', + f'/chains/main/blocks/genesis+{level}/metadata') + return rpc_res['baker'] + + def get_hash(self, level): + rpc_res = self.rpc('get', + f'/chains/main/blocks/genesis+{level}/header') + return rpc_res['hash'] + def wait_for_inclusion(self, operation_hash: str, branch: str = None, diff --git a/tests_python/launchers/sandbox.py b/tests_python/launchers/sandbox.py index 44ec06b48d8a..1231648e22c4 100644 --- a/tests_python/launchers/sandbox.py +++ b/tests_python/launchers/sandbox.py @@ -539,24 +539,32 @@ class SandboxMultiBranch(Sandbox): error_msg = f'{binaries_path}/{branch} not a dir' assert os.path.isdir(f'{binaries_path}/{branch}'), error_msg - def add_baker(self, + def add_baker(self, # pylint: disable=arguments-differ node_id: int, account: str, proto: str, params: List[str] = None, - branch: str = "") -> None: - """branch is overridden by branch_map""" - branch = self._branch_map[node_id] + branch: str = "", + map_id: int = None) -> None: + """branch is overridden by branch_map + map_id is used to override the map association""" + if map_id is None: + map_id = node_id + branch = self._branch_map[map_id] super().add_baker(node_id, account, proto, params, branch) - def add_endorser(self, + def add_endorser(self, # pylint: disable=arguments-differ node_id: int, account: str, proto: str, endorsement_delay: float = 0., - branch: str = "") -> None: - """branchs is overridden by branch_map""" - branch = self._branch_map[node_id] + branch: str = "", + map_id: int = None) -> None: + """branch is overridden by branch_map + map_id is used to override the map association""" + if map_id is None: + map_id = node_id + branch = self._branch_map[map_id] super().add_endorser(node_id, account, proto, endorsement_delay, branch) diff --git a/tests_python/tests/ledger/test_deterministic_nonces.py b/tests_python/tests/ledger/test_deterministic_nonces.py new file mode 100644 index 000000000000..75f32e931f7d --- /dev/null +++ b/tests_python/tests/ledger/test_deterministic_nonces.py @@ -0,0 +1,122 @@ +r"""The main idea of this test: +- in cycle n, there's only one baker (say alice) that bakes + (therefore, only this baker generates and commits to nonces) +- in cycle n+1, by default, alice will reveal all its nonces for cycle n + +By setting the constants "blocks_per_cycle" to `2 * n` and +"blocks_per_commitment" to `2`, alice will reveal `n` nonces. +Then we can look up how much time this takes. + +NB: setting "blocks_per_commitment" to `1` gives the following error +when baking the last block of cycle 1: +Error: + Storage error: + Missing key 'cycle/0/nonces/1'. +This is probably related to the fact that the activation block is +somehow special. + +For this, we do the following: + tezos-client list connected ledgers + tezos-client import secret key alice "ledger://.../ed25519/0h/0h" + tezos-client set ledger high water mark for alice to 0 + tezos-client setup ledger to bake for alice + tezos-client register key alice as delegate + +NB: The user needs to confirm these steps manually on the ledger! + +""" + +import time +import pytest +from tools import constants + +BAKE_ARGS = ['--minimal-timestamp', '--max-priority', '512'] + + +def get_ledger_key(string): + for line in string.split('\n'): + if line.find("ed25519") != -1: + pos = line.find("ledger:") + return line[pos:-1] + return "" + + +@pytest.mark.slow +@pytest.mark.incremental +class TestDeterministicNoncesWithLedger: + """Test generation of nonces with ledger""" + + params = dict(constants.PARAMETERS) + + params["initial_endorsers"] = 0 # so we can bake every second + params["preserved_cycles"] = 1 + params["blocks_per_cycle"] = 8 + params["blocks_per_commitment"] = 1 + + baker = 'alice-ledger' + + def test_setup(self, sandbox): + sandbox.add_node(0, params=constants.NODE_PARAMS) + + client = sandbox.client(0) + client.activate_protocol_json(constants.ALPHA, self.params) + + res = client.run(['list', 'connected', 'ledgers']) + + ledger_key = get_ledger_key(res) + + client.import_secret_key(self.baker, ledger_key) + + client.run(['set', 'ledger', 'high', 'water', 'mark', + 'for', self.baker, 'to', '0']) + client.run(['setup', 'ledger', 'to', 'bake', 'for', self.baker]) + + def test_add_new_baker(self, sandbox): + client = sandbox.client(0) + client.transfer(1000000, "bootstrap1", self.baker, + ['--burn-cap', '0.257']) + client.bake('bootstrap1', BAKE_ARGS) + client.register_delegate(self.baker) + + def test_bake(self, sandbox): + client = sandbox.client(0) + + # bake with some boostrap account till the new baker becomes active + for cycle in range(self.params["preserved_cycles"] + 2): + for level in range(self.params['blocks_per_cycle']): + client.bake('bootstrap1', BAKE_ARGS) + if (cycle == self.params["preserved_cycles"] + 1 and + level == self.params['blocks_per_cycle'] - 3): + break + + # bake with the new baker account for a cycle + # so only this baker will commit nonces + for _level in range(self.params['blocks_per_cycle']): + client.bake(self.baker, BAKE_ARGS) + + client.bake(self.baker, BAKE_ARGS) # first block in new cycle + + # NB: In the first level of the next cycle, the baker can + # reveal all the nonces! Revealing the nonces before baking + # this block would not help, as we are not yet in the new + # cycle; this is way revelations appear in the second block of + # a cycle... + + def test_reveal_nonces(self, sandbox): + client = sandbox.client(0) + + # method 1, use the client + # client.run(['reveal', 'nonces']) + + # method 2, use the baker daemon + cycle = client.get_cycle() + print(client.get_nonces_status(cycle - 1)) + + sandbox.add_baker(0, self.baker, proto=constants.ALPHA_DAEMON) + # wait a few seconds for the daemon to bake a block + # NB: Waiting for 5 seconds was not enough... + time.sleep(10) + + # the first baked block should include all nonces + nonces_status = client.get_nonces_status(cycle - 1) + assert nonces_status["unrevealed"] == [] diff --git a/tests_python/tests/multibranch/test_deterministic_nonces.py b/tests_python/tests/multibranch/test_deterministic_nonces.py new file mode 100644 index 000000000000..f3666189be1c --- /dev/null +++ b/tests_python/tests/multibranch/test_deterministic_nonces.py @@ -0,0 +1,174 @@ +r"""There are two methods used by the baker to generate nonces for seed +generation: +- the "old method" generates a random number and stores it in the + client 'nonces' file +- the "new method" asks the signer to generate it and the nonce is not + stored, the baker just asks again the signer again for the nonce + +This test checks that the update from the old method to the new method +works properly. + +We run 5 nodes and 5 bakers: +- 3 bakers use the old method +- one baker uses the new method +- one baker starts using the old method, and it is restarted using the + new method + +Note that the nodes and clients in the two methods are the same, only +the baker changes. + +Note: the test relies on `TEZOS_BINARIES`. The test will be be skipped +if the environment variable isn't defined. +""" + +import time +import pytest +from tools import utils, constants + +OLDM_BRANCH = "master" +NEWM_BRANCH = "eugenz@baker_with_deterministic_nonces" +# TODO: when the branch is merged, to be replaced with commit hashes + +# If the TEZOS_BINARIES is set to, say 'bin/', then the directories +# bin/master and bin/eugenz@baker_with_deterministic_nonces need to +# exist and to contain the relevant binaries + +PROTO_HASH = "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK" +PROTO = "alpha" + +TBB = 2 # time_between_blocks + +# the number of nodes equals the number of bakers +NUM_NODES = len(constants.PARAMETERS["bootstrap_accounts"]) +IDX_NEWM = NUM_NODES - 1 # the index of the baker that runs the new method +IDX_BOTH = NUM_NODES - 2 # the index of the baker that changes method + +MAP = {i: OLDM_BRANCH if i < IDX_NEWM else NEWM_BRANCH + for i in range(NUM_NODES)} + + +def get_baker_index(identity): + for i in range(NUM_NODES): + if identity == constants.IDENTITIES[f'bootstrap{i+1}']['identity']: + return i + return NUM_NODES # or fail? + + +def nonce_for_block(nonces, block_hash): + for entry in nonces: + if entry['block'] == block_hash: + return True + return False + + +@pytest.mark.parametrize('sandbox_multibranch', [MAP], indirect=True) +@pytest.mark.baker +@pytest.mark.multinode +@pytest.mark.slow +@pytest.mark.multibranch +@pytest.mark.incremental +class TestUpgradeToDeterministicNonce: + parameters = dict(constants.PARAMETERS) + + def test_setup_network(self, sandbox_multibranch): + params = ['--connections', f'{NUM_NODES}', '--history-mode', 'archive'] + for i in range(NUM_NODES): + sandbox_multibranch.add_node(i, params=params) + + self.parameters["time_between_blocks"] = [str(TBB), "0"] + self.parameters["blocks_per_cycle"] = 8 + self.parameters["preserved_cycles"] = 2 + self.parameters["blocks_per_roll_snapshot"] = 4 + self.parameters["blocks_per_commitment"] = 4 + + sandbox_multibranch.client(0).activate_protocol_json(PROTO_HASH, + self.parameters) + + for i in range(IDX_NEWM): + sandbox_multibranch.add_baker(i, f'bootstrap{i+1}', proto=PROTO) + sandbox_multibranch.add_baker(IDX_NEWM, f'bootstrap{IDX_NEWM+1}', + proto=PROTO) + + def test_wait_for_alpha(self, sandbox_multibranch): + clients = sandbox_multibranch.all_clients() + for client in clients: + assert utils.check_protocol(client, PROTO_HASH) + + def test_nonces_created(self, sandbox_multibranch): + # wait for some cycles: + time.sleep(2 * self.parameters["blocks_per_cycle"] * TBB) + + clients = sandbox_multibranch.all_clients() + + # we assume enough time has passed that each baker baked a + # block with a commitment + + # we check that there are nonces in the nonce file for bakers + # using the old method and that there are no nonces in the + # nonce file for the baker using the new method + all_nonces = [] + print() + for i in range(NUM_NODES): + nonces = clients[i].get_nonces_from_file() + print("The nonces of ", f'bootstrap{i+1}', ": ", nonces) + all_nonces.append(nonces) + if i != IDX_NEWM: + assert all_nonces[i] # not empty + else: + # the baker using the new method + assert not all_nonces[i] # empty + + # we also check that each block at a commitment level has an + # entry in the nonce file of the baker that created the block + crt_level = clients[0].get_level() + for level in range(4, crt_level, + int(self.parameters["blocks_per_commitment"])): + baker = clients[0].get_baker(level) + baker_index = get_baker_index(baker) + print("for level", level, "the baker is", baker, "that is,", + f'bootstrap{baker_index+1}') + if baker_index != IDX_NEWM: + # we should find the block hash in the nonces file + block_hash = clients[0].get_hash(level) + assert nonce_for_block(all_nonces[baker_index], block_hash) + + def test_restart_baker(self, sandbox_multibranch): + sandbox_multibranch.rm_baker(IDX_BOTH, proto=PROTO) + sandbox_multibranch.add_baker(IDX_BOTH, f'bootstrap{IDX_BOTH+1}', + proto=PROTO, map_id=IDX_NEWM) + + def test_nonces_deleted(self, sandbox_multibranch): + # wait for enough cycles + cycles_to_wait = int(self.parameters["preserved_cycles"]) + time.sleep((2 + cycles_to_wait) * + self.parameters["blocks_per_cycle"] * TBB) + + clients = sandbox_multibranch.all_clients() + + all_nonces = [] + print() + for i in range(NUM_NODES): + nonces = clients[i].get_nonces_from_file() + print("The nonces of ", f'bootstrap{i+1}', ": ", nonces) + all_nonces.append(nonces) + if i < IDX_BOTH: + assert all_nonces[i] # not empty + else: + assert not all_nonces[i] # empty + + crt_level = clients[0].get_level() + crt_cycle = crt_level // int(self.parameters["blocks_per_cycle"]) + min_level = (int(self.parameters["blocks_per_cycle"]) * + (crt_cycle - cycles_to_wait)) + print("Current level is", crt_level, "(cycle", crt_cycle, + "). Oldest level with stored nonces is", min_level) + for level in range(min_level, crt_level, + int(self.parameters["blocks_per_commitment"])): + baker = clients[0].get_baker(level) + baker_index = get_baker_index(baker) + print("for level", level, "the baker is", baker, + "that is, ", f'bootstrap{baker_index+1}') + if baker_index < IDX_BOTH: + # we should find the block hash in the nonces file + block_hash = clients[0].get_hash(level) + assert nonce_for_block(all_nonces[baker_index], block_hash) -- GitLab From 0c010ba5c74e857f18315fdec3926ed9f1720ec5 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Wed, 6 May 2020 09:08:10 +0200 Subject: [PATCH 10/10] Baker: fixup: add log message --- src/proto_alpha/lib_delegate/client_baking_forge.ml | 6 ++++++ src/proto_alpha/lib_delegate/logging.ml | 6 ++++++ src/proto_alpha/lib_delegate/logging.mli | 2 ++ 3 files changed, 14 insertions(+) diff --git a/src/proto_alpha/lib_delegate/client_baking_forge.ml b/src/proto_alpha/lib_delegate/client_baking_forge.ml index 193d50a4885e..b528919308fa 100644 --- a/src/proto_alpha/lib_delegate/client_baking_forge.ml +++ b/src/proto_alpha/lib_delegate/client_baking_forge.ml @@ -1485,6 +1485,12 @@ let reveal_potential_nonces (cctxt : #Client_context.full) constants ~chain | Ok [] -> return_unit | Ok nonces_to_reveal -> ( + lwt_log_notice + Tag.DSL.( + fun f -> + f "Revealing %d nonces" -% t event "reveal_nonces" + -% s Logging.num_nonces_tag (List.length nonces_to_reveal)) + >>= fun () -> Client_baking_revelation.inject_seed_nonce_revelation cctxt ~chain diff --git a/src/proto_alpha/lib_delegate/logging.ml b/src/proto_alpha/lib_delegate/logging.ml index 5efae398fe2f..28bb77a93686 100644 --- a/src/proto_alpha/lib_delegate/logging.ml +++ b/src/proto_alpha/lib_delegate/logging.ml @@ -159,3 +159,9 @@ let conflicting_endorsements_tag = (Operation.hash a) Operation_hash.pp (Operation.hash b)) + +let num_nonces_tag = + Tag.def + ~doc:"Number of revealed nonces" + "number_revealed_nonces" + Format.pp_print_int diff --git a/src/proto_alpha/lib_delegate/logging.mli b/src/proto_alpha/lib_delegate/logging.mli index 2e37b4764979..310f206b4457 100644 --- a/src/proto_alpha/lib_delegate/logging.mli +++ b/src/proto_alpha/lib_delegate/logging.mli @@ -80,3 +80,5 @@ val block_header_tag : Block_header.t Tag.def val conflicting_endorsements_tag : (Kind.endorsement operation * Kind.endorsement operation) Tag.def + +val num_nonces_tag : int Tag.def -- GitLab