From 0585cb83ec44912a8827c58fe18f4a6a162f743c Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Mon, 10 Apr 2023 17:40:56 +0200 Subject: [PATCH 1/6] Test/Proto: Solve the PoW baking challenge when producing a block --- .../lib_protocol/test/helpers/block.ml | 125 +++++++++++------- .../lib_protocol/test/helpers/block.mli | 12 +- .../lib_protocol/test/helpers/incremental.ml | 38 +++--- .../test/integration/consensus/test_seed.ml | 5 +- 4 files changed, 105 insertions(+), 75 deletions(-) diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 06291161aa88..47812d99a6d0 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -148,18 +148,39 @@ module Forge = struct let default_proof_of_work_nonce = Bytes.create Constants.proof_of_work_nonce_size - let make_contents ?(proof_of_work_nonce = default_proof_of_work_nonce) - ~payload_hash ~payload_round + let rec naive_pow_miner ~proof_of_work_threshold shell header = + match + Hacl_star.Hacl.RandomBuffer.randombytes + ~size:Constants.proof_of_work_nonce_size + with + | Some proof_of_work_nonce -> + let cand = Block_header.{header with proof_of_work_nonce} in + if + Block_header.Proof_of_work.check_header_proof_of_work_stamp + shell + cand + proof_of_work_threshold + then return cand + else naive_pow_miner ~proof_of_work_threshold shell header + | None -> failwith "Impossible to gather randomness" + + let make_contents + ?(proof_of_work_threshold = + Tezos_protocol_alpha_parameters.Default_parameters.constants_test + .proof_of_work_threshold) ~payload_hash ~payload_round ?(liquidity_baking_toggle_vote = Liquidity_baking.LB_pass) - ~seed_nonce_hash () = - Block_header. - { - payload_hash; - payload_round; - proof_of_work_nonce; - seed_nonce_hash; - liquidity_baking_toggle_vote; - } + ~seed_nonce_hash shell = + naive_pow_miner + ~proof_of_work_threshold + shell + Block_header. + { + payload_hash; + payload_round; + proof_of_work_nonce = default_proof_of_work_nonce; + seed_nonce_hash; + liquidity_baking_toggle_vote; + } let make_shell ~level ~predecessor ~timestamp ~fitness ~operations_hash = Tezos_base.Block_header. @@ -175,9 +196,16 @@ module Forge = struct context = Context_hash.zero; } - let set_seed_nonce_hash seed_nonce_hash + let set_seed_nonce_hash + ?(proof_of_work_threshold = + Tezos_protocol_alpha_parameters.Default_parameters.constants_test + .proof_of_work_threshold) seed_nonce_hash {baker; consensus_key; shell; contents} = - {baker; consensus_key; shell; contents = {contents with seed_nonce_hash}} + naive_pow_miner + ~proof_of_work_threshold + shell + {contents with seed_nonce_hash} + >|=? fun contents -> {baker; consensus_key; shell; contents} let set_baker baker ?(consensus_key = baker) header = {header with baker; consensus_key} @@ -230,7 +258,7 @@ module Forge = struct (Plugin.RPC.current_level ~offset:1l rpc_ctxt pred >|=? function | {expected_commitment = true; _} -> Some (fst (Proto_Nonce.generate ())) | {expected_commitment = false; _} -> None) - >|=? fun seed_nonce_hash -> + >>=? fun seed_nonce_hash -> let hashes = List.map Operation.hash_packed operations in let operations_hash = Operation_list_list_hash.compute [Operation_list_hash.compute hashes] @@ -257,28 +285,31 @@ module Forge = struct ~payload_round hashes in - let contents = - make_contents - ~seed_nonce_hash - ?liquidity_baking_toggle_vote - ~payload_hash - ~payload_round - () - in - {baker = delegate; consensus_key; shell; contents} + make_contents + ~seed_nonce_hash + ?liquidity_baking_toggle_vote + ~payload_hash + ~payload_round + shell + >|=? fun contents -> {baker = delegate; consensus_key; shell; contents} (* compatibility only, needed by incremental *) - let contents ?(proof_of_work_nonce = default_proof_of_work_nonce) - ?seed_nonce_hash + let contents + ?(proof_of_work_threshold = + Tezos_protocol_alpha_parameters.Default_parameters.constants_test + .proof_of_work_threshold) ?seed_nonce_hash ?(liquidity_baking_toggle_vote = Liquidity_baking.LB_pass) ~payload_hash - ~payload_round () = - { - Block_header.proof_of_work_nonce; - seed_nonce_hash; - liquidity_baking_toggle_vote; - payload_hash; - payload_round; - } + ~payload_round shell_header = + naive_pow_miner + ~proof_of_work_threshold + shell_header + { + Block_header.proof_of_work_nonce = default_proof_of_work_nonce; + seed_nonce_hash; + liquidity_baking_toggle_vote; + payload_hash; + payload_round; + } end (********* Genesis creation *************) @@ -408,13 +439,12 @@ let genesis_with_parameters parameters = ~fitness ~operations_hash:Operation_list_list_hash.zero in - let contents = - Forge.make_contents - ~payload_hash:Block_payload_hash.zero - ~payload_round:Round.zero - ~seed_nonce_hash:None - () - in + Forge.make_contents + ~payload_hash:Block_payload_hash.zero + ~payload_round:Round.zero + ~seed_nonce_hash:None + shell + >>=? fun contents -> let open Tezos_protocol_alpha_parameters in let json = Default_parameters.json_of_parameters parameters in let proto_params = @@ -612,14 +642,13 @@ let genesis ?commitments ?consensus_threshold ?min_proposal_quorum constants shell bootstrap_accounts - >|=? fun context -> - let contents = - Forge.make_contents - ~payload_hash:Block_payload_hash.zero - ~payload_round:Round.zero - ~seed_nonce_hash:None - () - in + >>=? fun context -> + Forge.make_contents + ~payload_hash:Block_payload_hash.zero + ~payload_round:Round.zero + ~seed_nonce_hash:None + shell + >|=? fun contents -> { hash; header = {shell; protocol_data = {contents; signature = Signature.zero}}; diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.mli b/src/proto_alpha/lib_protocol/test/helpers/block.mli index 4fdf2a4d1f9b..ac3680b2af52 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/block.mli @@ -70,13 +70,13 @@ val get_round : block -> Round.t tzresult module Forge : sig val contents : - ?proof_of_work_nonce:Bytes.t -> + ?proof_of_work_threshold:Int64.t -> ?seed_nonce_hash:Nonce_hash.t -> ?liquidity_baking_toggle_vote:Liquidity_baking.liquidity_baking_toggle_vote -> payload_hash:Block_payload_hash.t -> payload_round:Round.t -> - unit -> - Block_header.contents + Block_header.shell_header -> + Block_header.contents tzresult Lwt.t type header @@ -95,7 +95,11 @@ module Forge : sig header tzresult Lwt.t (** Sets uniquely seed_nonce_hash of a header *) - val set_seed_nonce_hash : Nonce_hash.t option -> header -> header + val set_seed_nonce_hash : + ?proof_of_work_threshold:int64 -> + Nonce_hash.t option -> + header -> + (header, tztrace) result Lwt.t (** Sets the baker that will sign the header to an arbitrary pkh *) val set_baker : diff --git a/src/proto_alpha/lib_protocol/test/helpers/incremental.ml b/src/proto_alpha/lib_protocol/test/helpers/incremental.ml index 196396137e34..413386c3214a 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/incremental.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/incremental.ml @@ -88,13 +88,24 @@ let begin_construction ?timestamp ?seed_nonce_hash ?(mempool_mode = false) | {expected_commitment = true; _} -> Some (fst (Proto_Nonce.generate ())) | {expected_commitment = false; _} -> None)) >>=? fun seed_nonce_hash -> - let contents = - Block.Forge.contents - ?seed_nonce_hash - ~payload_hash:Block_payload_hash.zero - ~payload_round - () + let shell : Block_header.shell_header = + { + predecessor = predecessor.hash; + proto_level = predecessor.header.shell.proto_level; + validation_passes = predecessor.header.shell.validation_passes; + fitness = predecessor.header.shell.fitness; + timestamp; + level = predecessor.header.shell.level; + context = Context_hash.zero; + operations_hash = Operation_list_list_hash.zero; + } in + Block.Forge.contents + ?seed_nonce_hash + ~payload_hash:Block_payload_hash.zero + ~payload_round + shell + >>=? fun contents -> let mode = if mempool_mode then Partial_construction {predecessor_hash = predecessor.hash; timestamp} @@ -106,20 +117,7 @@ let begin_construction ?timestamp ?seed_nonce_hash ?(mempool_mode = false) {predecessor_hash = predecessor.hash; timestamp; block_header_data} in let header = - { - Block_header.shell = - { - predecessor = predecessor.hash; - proto_level = predecessor.header.shell.proto_level; - validation_passes = predecessor.header.shell.validation_passes; - fitness = predecessor.header.shell.fitness; - timestamp; - level = predecessor.header.shell.level; - context = Context_hash.zero; - operations_hash = Operation_list_list_hash.zero; - }; - protocol_data = {contents; signature = Signature.zero}; - } + {Block_header.shell; protocol_data = {contents; signature = Signature.zero}} in begin_validation_and_application predecessor.context diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml index afcb0a4d6ccb..ccd0a6af169a 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml @@ -106,9 +106,8 @@ let test_no_commitment () = let* b = Block.bake_n (blocks_per_commitment - 2) b in (* Forge a block with empty commitment and apply it *) let* header = Block.Forge.forge_header b in - let* header = - Block.Forge.set_seed_nonce_hash None header |> Block.Forge.sign_header - in + let* header = Block.Forge.set_seed_nonce_hash None header in + let* header = Block.Forge.sign_header header in let*! e = Block.apply header b in Assert.proto_error_with_info ~loc:__LOC__ -- GitLab From f62c1ad98ec8911f7c24601e6a6abcf3d96221bb Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Mon, 3 Apr 2023 18:12:33 +0200 Subject: [PATCH 2/6] Alpha parameters: Require just a bit of PoW to the baker in sandbox&test --- src/lib_store/unix/test/alpha_utils.ml | 7 ++++++- src/lib_store/unix/test/test_snapshots.ml | 1 + src/proto_alpha/lib_parameters/default_parameters.ml | 5 +++-- ...- (mode client) RPC regression tests- misc_protocol.out | 3 ++- ...a- (mode light) RPC regression tests- misc_protocol.out | 3 ++- ...a- (mode proxy) RPC regression tests- misc_protocol.out | 3 ++- ...erver_data_dir) RPC regression tests- misc_protocol.out | 3 ++- ...oxy_server_rpc) RPC regression tests- misc_protocol.out | 3 ++- tezt/tests/precheck.ml | 7 ++++++- 9 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/lib_store/unix/test/alpha_utils.ml b/src/lib_store/unix/test/alpha_utils.ml index 43b5679d2df5..a7b0e4e958d2 100644 --- a/src/lib_store/unix/test/alpha_utils.ml +++ b/src/lib_store/unix/test/alpha_utils.ml @@ -428,7 +428,12 @@ let default_genesis_parameters = let open Tezos_protocol_alpha_parameters in { Default_parameters.( - parameters_of_constants {constants_sandbox with consensus_threshold = 0}) + parameters_of_constants + { + constants_sandbox with + consensus_threshold = 0; + proof_of_work_threshold = -1L; + }) with bootstrap_accounts = default_accounts; } diff --git a/src/lib_store/unix/test/test_snapshots.ml b/src/lib_store/unix/test/test_snapshots.ml index 0a1140ce6e76..bfce21c214da 100644 --- a/src/lib_store/unix/test/test_snapshots.ml +++ b/src/lib_store/unix/test/test_snapshots.ml @@ -595,6 +595,7 @@ let test_drag_after_import export_mode = blocks_per_cycle = 256l; cycles_per_voting_period = 1l; consensus_threshold = 0; + proof_of_work_threshold = -1L; } in let patch_context ctxt = diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index 65f911e5ded8..386ef648c32e 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -323,7 +323,7 @@ let constants_sandbox = nonce_revelation_threshold = 4l; blocks_per_stake_snapshot = 4l; cycles_per_voting_period = 8l; - proof_of_work_threshold = Int64.of_int (-1); + proof_of_work_threshold = Int64.(sub (shift_left 1L 62) 1L); vdf_difficulty = 50_000L; liquidity_baking_subsidy; minimal_block_delay = Period.of_seconds_exn (Int64.of_int block_time); @@ -370,7 +370,8 @@ let constants_test = nonce_revelation_threshold = 4l; blocks_per_stake_snapshot = 4l; cycles_per_voting_period = 2l; - proof_of_work_threshold = Int64.of_int (-1); + proof_of_work_threshold = + Int64.(sub (shift_left 1L 62) 1L) (* 1/4 of nonces are accepted *); vdf_difficulty = 50_000L; liquidity_baking_subsidy; consensus_committee_size; diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out index 9d5cab670056..e6f0bb334c25 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out @@ -12,7 +12,8 @@ "preserved_cycles": 2, "blocks_per_cycle": 8, "blocks_per_commitment": 4, "nonce_revelation_threshold": 4, "blocks_per_stake_snapshot": 4, "cycles_per_voting_period": 8, "hard_gas_limit_per_operation": "1040000", - "hard_gas_limit_per_block": "2600000", "proof_of_work_threshold": "-1", + "hard_gas_limit_per_block": "2600000", + "proof_of_work_threshold": "4611686018427387903", "minimal_stake": "6000000000", "vdf_difficulty": "50000", "seed_nonce_revelation_tip": "125000", "origination_size": 257, "baking_reward_fixed_portion": "333333", diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out index 91b7ce05765e..8d1c33144314 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out @@ -12,7 +12,8 @@ "preserved_cycles": 2, "blocks_per_cycle": 8, "blocks_per_commitment": 4, "nonce_revelation_threshold": 4, "blocks_per_stake_snapshot": 4, "cycles_per_voting_period": 8, "hard_gas_limit_per_operation": "1040000", - "hard_gas_limit_per_block": "2600000", "proof_of_work_threshold": "-1", + "hard_gas_limit_per_block": "2600000", + "proof_of_work_threshold": "4611686018427387903", "minimal_stake": "6000000000", "vdf_difficulty": "50000", "seed_nonce_revelation_tip": "125000", "origination_size": 257, "baking_reward_fixed_portion": "333333", diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out index 66a74b8f74f2..d8a198814f1c 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out @@ -12,7 +12,8 @@ "preserved_cycles": 2, "blocks_per_cycle": 8, "blocks_per_commitment": 4, "nonce_revelation_threshold": 4, "blocks_per_stake_snapshot": 4, "cycles_per_voting_period": 8, "hard_gas_limit_per_operation": "1040000", - "hard_gas_limit_per_block": "2600000", "proof_of_work_threshold": "-1", + "hard_gas_limit_per_block": "2600000", + "proof_of_work_threshold": "4611686018427387903", "minimal_stake": "6000000000", "vdf_difficulty": "50000", "seed_nonce_revelation_tip": "125000", "origination_size": 257, "baking_reward_fixed_portion": "333333", diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out index 4ea3f3d1f912..9ddbb9469f38 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out @@ -12,7 +12,8 @@ "preserved_cycles": 2, "blocks_per_cycle": 8, "blocks_per_commitment": 4, "nonce_revelation_threshold": 4, "blocks_per_stake_snapshot": 4, "cycles_per_voting_period": 8, "hard_gas_limit_per_operation": "1040000", - "hard_gas_limit_per_block": "2600000", "proof_of_work_threshold": "-1", + "hard_gas_limit_per_block": "2600000", + "proof_of_work_threshold": "4611686018427387903", "minimal_stake": "6000000000", "vdf_difficulty": "50000", "seed_nonce_revelation_tip": "125000", "origination_size": 257, "baking_reward_fixed_portion": "333333", diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out index 4ea3f3d1f912..9ddbb9469f38 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out @@ -12,7 +12,8 @@ "preserved_cycles": 2, "blocks_per_cycle": 8, "blocks_per_commitment": 4, "nonce_revelation_threshold": 4, "blocks_per_stake_snapshot": 4, "cycles_per_voting_period": 8, "hard_gas_limit_per_operation": "1040000", - "hard_gas_limit_per_block": "2600000", "proof_of_work_threshold": "-1", + "hard_gas_limit_per_block": "2600000", + "proof_of_work_threshold": "4611686018427387903", "minimal_stake": "6000000000", "vdf_difficulty": "50000", "seed_nonce_revelation_tip": "125000", "origination_size": 257, "baking_reward_fixed_portion": "333333", diff --git a/tezt/tests/precheck.ml b/tezt/tests/precheck.ml index 192112f44bb0..b9c41b80e313 100644 --- a/tezt/tests/precheck.ml +++ b/tezt/tests/precheck.ml @@ -301,7 +301,12 @@ let propagate_precheckable_bad_block_payload = in Log.info "Cluster initialized" ; let* client = Client.(init ~endpoint:(Node node_client) ()) in - let* () = Client.activate_protocol ~protocol client in + let* parameter_file = + Protocol.write_parameter_file + ~base:(Either.Right (protocol, None)) + [(["proof_of_work_threshold"], `String "-1")] + in + let* () = Client.activate_protocol ~parameter_file ~protocol client in let bootstrap1 = Constant.bootstrap1.alias in let* () = List.init blocks_to_bake Fun.id -- GitLab From 4663f4cbc8309942cf406831db7e2ef1c55b505c Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Tue, 28 Mar 2023 18:39:23 +0200 Subject: [PATCH 3/6] Baker/Alpha: Improve Proof of work efficiency --- src/proto_alpha/lib_delegate/baking_pow.ml | 126 ++++++++++++++------- 1 file changed, 86 insertions(+), 40 deletions(-) diff --git a/src/proto_alpha/lib_delegate/baking_pow.ml b/src/proto_alpha/lib_delegate/baking_pow.ml index d5d4d4be293b..5bd6f69d6234 100644 --- a/src/proto_alpha/lib_delegate/baking_pow.ml +++ b/src/proto_alpha/lib_delegate/baking_pow.ml @@ -27,7 +27,7 @@ open Protocol let default_constant = "\x00\x00\x00\x05" -let is_updated_constant = +let with_version_constant = let commit_hash = match Hex.to_string (`Hex Tezos_version.Current_git_info.commit_hash) with | None -> Tezos_version.Current_git_info.commit_hash @@ -36,50 +36,96 @@ let is_updated_constant = if String.length commit_hash >= 4 then String.sub commit_hash 0 4 else default_constant -let is_updated_constant_len = String.length is_updated_constant +let with_version_constant_len = String.length with_version_constant -(* add a version to the pow *) -let init_proof_of_work_nonce () = - let buf = +let proof_of_work_nonce = + let out = Bytes.make Alpha_context.Constants.proof_of_work_nonce_size '\000' in - Bytes.blit_string is_updated_constant 0 buf 0 is_updated_constant_len ; - let max_z_len = - Alpha_context.Constants.proof_of_work_nonce_size - is_updated_constant_len + let () = + Bytes.blit_string with_version_constant 0 out 0 with_version_constant_len in - let rec aux z = - let z_len = (Z.numbits z + 7) / 8 in - if z_len > max_z_len then Seq.Nil - else ( - Bytes.blit_string (Z.to_bits z) 0 buf is_updated_constant_len z_len ; - Seq.Cons (buf, fun () -> aux (Z.succ z))) - in - aux Z.zero + out + +(* [proof_of_work_nonce] will be modified in place so we make a clean copy to expose to the outside *) +let empty_proof_of_work_nonce = Bytes.copy proof_of_work_nonce -(* This was used before November 2018 *) -(* (\* Random proof of work *\) - * let generate_proof_of_work_nonce () = - * Tezos_crypto.Rand.generate Alpha_context.Constants.proof_of_work_nonce_size *) +let max_z_len = + Alpha_context.Constants.proof_of_work_nonce_size - with_version_constant_len -let empty_proof_of_work_nonce = - Bytes.make Constants_repr.proof_of_work_nonce_size '\000' +(* Make a string of zeros to restore [proof_of_work_nonce] to its original value in 1 operation *) +let zeros = String.make max_z_len '\000' let mine ~proof_of_work_threshold shell builder = - let rec loop nonce_seq = - match nonce_seq with - | Seq.Nil -> - failwith - "Client_baking_pow.mine: couldn't find nonce for required proof of \ - work" - | Seq.Cons (nonce, seq) -> - let block = builder nonce in - if - Alpha_context.Block_header.Proof_of_work - .check_header_proof_of_work_stamp - shell - block - proof_of_work_threshold - then return block - else loop (seq ()) - in - loop (init_proof_of_work_nonce ()) + match + Option.bind + (Data_encoding.Binary.fixed_length Block_payload_hash.encoding) + (fun payload -> + Option.map + (fun round -> + let shell = + Data_encoding.Binary.length + Block_header.shell_header_encoding + shell + in + shell + payload + round + with_version_constant_len) + (Data_encoding.Binary.fixed_length Round_repr.encoding)) + (* Where to put the proof of work value is in the bytes of the encoded header *) + with + | None -> failwith "Cannot compute block header offset" + | Some offset -> + let () = + (* Restore proof_of_work_nonce to its original value. *) + Bytes.blit_string + zeros + 0 + proof_of_work_nonce + with_version_constant_len + max_z_len + in + (* Build the binary of the block header with 0 as proof of work and compute its hash. *) + let block_0 = builder proof_of_work_nonce in + let block_header = + Data_encoding.Binary.to_bytes_exn + Alpha_context.Block_header.encoding + Alpha_context.Block_header. + { + shell; + protocol_data = {contents = block_0; signature = Signature.zero}; + } + in + let block_hash = Block_header.hash_raw block_header in + let block_hash_bytes = Block_hash.to_bytes block_hash in + (* The loop edits [block_header] and [block_hash] (by editing its subpart [block_hash_bytes]!) in place. *) + let rec loop z = + let z_len = (Z.numbits z + 7) / 8 in + if z_len > max_z_len then + failwith + "Client_baking_pow.mine: couldn't find nonce for required proof of \ + work" + else ( + Bytes.blit_string (Z.to_bits z) 0 block_header offset z_len ; + (if Hacl_star.AutoConfig2.(has_feature VEC256) then + Hacl_star.Hacl.Blake2b_256.Noalloc.hash + else Hacl_star.Hacl.Blake2b_32.Noalloc.hash) + ~key:Bytes.empty + ~msg:block_header + ~digest:block_hash_bytes ; + if + Alpha_context.Block_header.Proof_of_work.check_hash + block_hash + proof_of_work_threshold + then + let () = + Bytes.blit_string + (Z.to_bits z) + 0 + proof_of_work_nonce + with_version_constant_len + z_len + in + let block = builder proof_of_work_nonce in + return block + else loop (Z.succ z)) + in + loop Z.zero -- GitLab From 80be28399ec2b4db4795e40f06dcfeaaf37ef8c5 Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Tue, 18 Apr 2023 08:50:12 +0200 Subject: [PATCH 4/6] Baker: Backport in active protocols Improve Proof of work efficiency --- .../lib_delegate/baking_pow.ml | 126 ++++++++++++------ .../lib_delegate/baking_pow.ml | 126 ++++++++++++------ 2 files changed, 172 insertions(+), 80 deletions(-) diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_pow.ml b/src/proto_016_PtMumbai/lib_delegate/baking_pow.ml index d5d4d4be293b..5bd6f69d6234 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_pow.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_pow.ml @@ -27,7 +27,7 @@ open Protocol let default_constant = "\x00\x00\x00\x05" -let is_updated_constant = +let with_version_constant = let commit_hash = match Hex.to_string (`Hex Tezos_version.Current_git_info.commit_hash) with | None -> Tezos_version.Current_git_info.commit_hash @@ -36,50 +36,96 @@ let is_updated_constant = if String.length commit_hash >= 4 then String.sub commit_hash 0 4 else default_constant -let is_updated_constant_len = String.length is_updated_constant +let with_version_constant_len = String.length with_version_constant -(* add a version to the pow *) -let init_proof_of_work_nonce () = - let buf = +let proof_of_work_nonce = + let out = Bytes.make Alpha_context.Constants.proof_of_work_nonce_size '\000' in - Bytes.blit_string is_updated_constant 0 buf 0 is_updated_constant_len ; - let max_z_len = - Alpha_context.Constants.proof_of_work_nonce_size - is_updated_constant_len + let () = + Bytes.blit_string with_version_constant 0 out 0 with_version_constant_len in - let rec aux z = - let z_len = (Z.numbits z + 7) / 8 in - if z_len > max_z_len then Seq.Nil - else ( - Bytes.blit_string (Z.to_bits z) 0 buf is_updated_constant_len z_len ; - Seq.Cons (buf, fun () -> aux (Z.succ z))) - in - aux Z.zero + out + +(* [proof_of_work_nonce] will be modified in place so we make a clean copy to expose to the outside *) +let empty_proof_of_work_nonce = Bytes.copy proof_of_work_nonce -(* This was used before November 2018 *) -(* (\* Random proof of work *\) - * let generate_proof_of_work_nonce () = - * Tezos_crypto.Rand.generate Alpha_context.Constants.proof_of_work_nonce_size *) +let max_z_len = + Alpha_context.Constants.proof_of_work_nonce_size - with_version_constant_len -let empty_proof_of_work_nonce = - Bytes.make Constants_repr.proof_of_work_nonce_size '\000' +(* Make a string of zeros to restore [proof_of_work_nonce] to its original value in 1 operation *) +let zeros = String.make max_z_len '\000' let mine ~proof_of_work_threshold shell builder = - let rec loop nonce_seq = - match nonce_seq with - | Seq.Nil -> - failwith - "Client_baking_pow.mine: couldn't find nonce for required proof of \ - work" - | Seq.Cons (nonce, seq) -> - let block = builder nonce in - if - Alpha_context.Block_header.Proof_of_work - .check_header_proof_of_work_stamp - shell - block - proof_of_work_threshold - then return block - else loop (seq ()) - in - loop (init_proof_of_work_nonce ()) + match + Option.bind + (Data_encoding.Binary.fixed_length Block_payload_hash.encoding) + (fun payload -> + Option.map + (fun round -> + let shell = + Data_encoding.Binary.length + Block_header.shell_header_encoding + shell + in + shell + payload + round + with_version_constant_len) + (Data_encoding.Binary.fixed_length Round_repr.encoding)) + (* Where to put the proof of work value is in the bytes of the encoded header *) + with + | None -> failwith "Cannot compute block header offset" + | Some offset -> + let () = + (* Restore proof_of_work_nonce to its original value. *) + Bytes.blit_string + zeros + 0 + proof_of_work_nonce + with_version_constant_len + max_z_len + in + (* Build the binary of the block header with 0 as proof of work and compute its hash. *) + let block_0 = builder proof_of_work_nonce in + let block_header = + Data_encoding.Binary.to_bytes_exn + Alpha_context.Block_header.encoding + Alpha_context.Block_header. + { + shell; + protocol_data = {contents = block_0; signature = Signature.zero}; + } + in + let block_hash = Block_header.hash_raw block_header in + let block_hash_bytes = Block_hash.to_bytes block_hash in + (* The loop edits [block_header] and [block_hash] (by editing its subpart [block_hash_bytes]!) in place. *) + let rec loop z = + let z_len = (Z.numbits z + 7) / 8 in + if z_len > max_z_len then + failwith + "Client_baking_pow.mine: couldn't find nonce for required proof of \ + work" + else ( + Bytes.blit_string (Z.to_bits z) 0 block_header offset z_len ; + (if Hacl_star.AutoConfig2.(has_feature VEC256) then + Hacl_star.Hacl.Blake2b_256.Noalloc.hash + else Hacl_star.Hacl.Blake2b_32.Noalloc.hash) + ~key:Bytes.empty + ~msg:block_header + ~digest:block_hash_bytes ; + if + Alpha_context.Block_header.Proof_of_work.check_hash + block_hash + proof_of_work_threshold + then + let () = + Bytes.blit_string + (Z.to_bits z) + 0 + proof_of_work_nonce + with_version_constant_len + z_len + in + let block = builder proof_of_work_nonce in + return block + else loop (Z.succ z)) + in + loop Z.zero diff --git a/src/proto_017_PtNairob/lib_delegate/baking_pow.ml b/src/proto_017_PtNairob/lib_delegate/baking_pow.ml index d5d4d4be293b..5bd6f69d6234 100644 --- a/src/proto_017_PtNairob/lib_delegate/baking_pow.ml +++ b/src/proto_017_PtNairob/lib_delegate/baking_pow.ml @@ -27,7 +27,7 @@ open Protocol let default_constant = "\x00\x00\x00\x05" -let is_updated_constant = +let with_version_constant = let commit_hash = match Hex.to_string (`Hex Tezos_version.Current_git_info.commit_hash) with | None -> Tezos_version.Current_git_info.commit_hash @@ -36,50 +36,96 @@ let is_updated_constant = if String.length commit_hash >= 4 then String.sub commit_hash 0 4 else default_constant -let is_updated_constant_len = String.length is_updated_constant +let with_version_constant_len = String.length with_version_constant -(* add a version to the pow *) -let init_proof_of_work_nonce () = - let buf = +let proof_of_work_nonce = + let out = Bytes.make Alpha_context.Constants.proof_of_work_nonce_size '\000' in - Bytes.blit_string is_updated_constant 0 buf 0 is_updated_constant_len ; - let max_z_len = - Alpha_context.Constants.proof_of_work_nonce_size - is_updated_constant_len + let () = + Bytes.blit_string with_version_constant 0 out 0 with_version_constant_len in - let rec aux z = - let z_len = (Z.numbits z + 7) / 8 in - if z_len > max_z_len then Seq.Nil - else ( - Bytes.blit_string (Z.to_bits z) 0 buf is_updated_constant_len z_len ; - Seq.Cons (buf, fun () -> aux (Z.succ z))) - in - aux Z.zero + out + +(* [proof_of_work_nonce] will be modified in place so we make a clean copy to expose to the outside *) +let empty_proof_of_work_nonce = Bytes.copy proof_of_work_nonce -(* This was used before November 2018 *) -(* (\* Random proof of work *\) - * let generate_proof_of_work_nonce () = - * Tezos_crypto.Rand.generate Alpha_context.Constants.proof_of_work_nonce_size *) +let max_z_len = + Alpha_context.Constants.proof_of_work_nonce_size - with_version_constant_len -let empty_proof_of_work_nonce = - Bytes.make Constants_repr.proof_of_work_nonce_size '\000' +(* Make a string of zeros to restore [proof_of_work_nonce] to its original value in 1 operation *) +let zeros = String.make max_z_len '\000' let mine ~proof_of_work_threshold shell builder = - let rec loop nonce_seq = - match nonce_seq with - | Seq.Nil -> - failwith - "Client_baking_pow.mine: couldn't find nonce for required proof of \ - work" - | Seq.Cons (nonce, seq) -> - let block = builder nonce in - if - Alpha_context.Block_header.Proof_of_work - .check_header_proof_of_work_stamp - shell - block - proof_of_work_threshold - then return block - else loop (seq ()) - in - loop (init_proof_of_work_nonce ()) + match + Option.bind + (Data_encoding.Binary.fixed_length Block_payload_hash.encoding) + (fun payload -> + Option.map + (fun round -> + let shell = + Data_encoding.Binary.length + Block_header.shell_header_encoding + shell + in + shell + payload + round + with_version_constant_len) + (Data_encoding.Binary.fixed_length Round_repr.encoding)) + (* Where to put the proof of work value is in the bytes of the encoded header *) + with + | None -> failwith "Cannot compute block header offset" + | Some offset -> + let () = + (* Restore proof_of_work_nonce to its original value. *) + Bytes.blit_string + zeros + 0 + proof_of_work_nonce + with_version_constant_len + max_z_len + in + (* Build the binary of the block header with 0 as proof of work and compute its hash. *) + let block_0 = builder proof_of_work_nonce in + let block_header = + Data_encoding.Binary.to_bytes_exn + Alpha_context.Block_header.encoding + Alpha_context.Block_header. + { + shell; + protocol_data = {contents = block_0; signature = Signature.zero}; + } + in + let block_hash = Block_header.hash_raw block_header in + let block_hash_bytes = Block_hash.to_bytes block_hash in + (* The loop edits [block_header] and [block_hash] (by editing its subpart [block_hash_bytes]!) in place. *) + let rec loop z = + let z_len = (Z.numbits z + 7) / 8 in + if z_len > max_z_len then + failwith + "Client_baking_pow.mine: couldn't find nonce for required proof of \ + work" + else ( + Bytes.blit_string (Z.to_bits z) 0 block_header offset z_len ; + (if Hacl_star.AutoConfig2.(has_feature VEC256) then + Hacl_star.Hacl.Blake2b_256.Noalloc.hash + else Hacl_star.Hacl.Blake2b_32.Noalloc.hash) + ~key:Bytes.empty + ~msg:block_header + ~digest:block_hash_bytes ; + if + Alpha_context.Block_header.Proof_of_work.check_hash + block_hash + proof_of_work_threshold + then + let () = + Bytes.blit_string + (Z.to_bits z) + 0 + proof_of_work_nonce + with_version_constant_len + z_len + in + let block = builder proof_of_work_nonce in + return block + else loop (Z.succ z)) + in + loop Z.zero -- GitLab From b786a476548d494140f75e43a184179a11124e50 Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Fri, 14 Apr 2023 16:44:03 +0200 Subject: [PATCH 5/6] Client/Alpha: add a command to bench baking PoW --- manifest/main.ml | 1 + .../lib_delegate/baking_commands.ml | 112 ++++++++++++++++++ src/proto_alpha/lib_delegate/dune | 2 + 3 files changed, 115 insertions(+) diff --git a/manifest/main.ml b/manifest/main.ml index a38cb8546875..a921b634e442 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -5785,6 +5785,7 @@ module Protocol = Protocol octez_base |> open_ ~m:"TzPervasives" |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; main |> open_; + parameters |> if_some |> if_ N.(number >= 18) |> open_; octez_stdlib_unix |> open_; octez_protocol_environment; octez_shell_services |> open_; diff --git a/src/proto_alpha/lib_delegate/baking_commands.ml b/src/proto_alpha/lib_delegate/baking_commands.ml index 5e9108be783e..388038ccf462 100644 --- a/src/proto_alpha/lib_delegate/baking_commands.ml +++ b/src/proto_alpha/lib_delegate/baking_commands.ml @@ -228,6 +228,118 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list {name = "delegate.client"; title = "Tenderbake client commands"} in [ + command + ~group + ~desc:"Benchmark the proof of work challenge resolution" + (args2 + (default_arg + ~doc:"Proof of work threshold" + ~long:"threshold" + ~placeholder:"int" + ~default: + (Int64.to_string + Default_parameters.constants_mainnet.proof_of_work_threshold) + (parameter (fun (cctxt : Protocol_client_context.full) x -> + try return (Int64.of_string x) + with _ -> cctxt#error "Expect an integer"))) + (arg + ~doc:"Random seed" + ~long:"seed" + ~placeholder:"int" + (parameter (fun (cctxt : Protocol_client_context.full) x -> + try return (int_of_string x) + with _ -> cctxt#error "Expect an integer")))) + (prefix "bench" + @@ param + ~name:"nb_draw" + ~desc:"number of draws" + (parameter (fun (cctxt : Protocol_client_context.full) x -> + match int_of_string x with + | x when x >= 1 -> return x + | _ | (exception _) -> + cctxt#error "Expect a strictly positive integer")) + @@ fixed ["baking"; "PoW"; "challenges"]) + (fun (proof_of_work_threshold, seed) nb_draw cctxt -> + let open Lwt_result_syntax in + let*! () = + cctxt#message + "Running %d iterations of proof-of-work challenge..." + nb_draw + in + let rstate = + match seed with + | None -> Random.State.make_self_init () + | Some s -> Random.State.make [|s|] + in + let* all = + List.map_es + (fun i -> + let level = Int32.of_int (Random.State.int rstate (1 lsl 29)) in + let shell_header = + Tezos_base.Block_header. + { + level; + proto_level = 1; + (* uint8 *) + predecessor = Tezos_crypto.Hashed.Block_hash.zero; + timestamp = Time.Protocol.epoch; + validation_passes = 3; + (* uint8 *) + operations_hash = + Tezos_crypto.Hashed.Operation_list_list_hash.zero; + fitness = []; + context = Tezos_crypto.Hashed.Context_hash.zero; + } + in + let now = Time.System.now () in + let* _ = + Baking_pow.mine + ~proof_of_work_threshold + shell_header + (fun proof_of_work_nonce -> + Protocol.Alpha_context. + { + Block_header.payload_hash = + Protocol.Block_payload_hash.zero; + payload_round = Round.zero; + seed_nonce_hash = None; + proof_of_work_nonce; + liquidity_baking_toggle_vote = LB_pass; + }) + in + let _then = Time.System.now () in + let x = Ptime.diff _then now in + let*! () = cctxt#message "%d/%d: %a" i nb_draw Ptime.Span.pp x in + return x) + (1 -- nb_draw) + in + let sum = List.fold_left Ptime.Span.add Ptime.Span.zero all in + let base, tail = Stdlib.List.(hd all, tl all) in + let max = + List.fold_left + (fun x y -> if Ptime.Span.compare x y > 0 then x else y) + base + tail + in + let min = + List.fold_left + (fun x y -> if Ptime.Span.compare x y <= 0 then x else y) + base + tail + in + let div = Ptime.Span.to_float_s sum /. float (List.length all) in + let*! () = + cctxt#message + "%d runs: min: %a, max: %a, average: %a" + nb_draw + Ptime.Span.pp + min + Ptime.Span.pp + max + (Format.pp_print_option Ptime.Span.pp) + (Ptime.Span.of_float_s div) + in + return_unit); command ~group ~desc:"Forge and inject block using the delegates' rights." diff --git a/src/proto_alpha/lib_delegate/dune b/src/proto_alpha/lib_delegate/dune index 779ed88e424b..ada7a99bf5a5 100644 --- a/src/proto_alpha/lib_delegate/dune +++ b/src/proto_alpha/lib_delegate/dune @@ -55,6 +55,7 @@ (libraries tezos-base tezos-protocol-alpha + tezos-protocol-alpha.parameters tezos-stdlib-unix tezos-protocol-environment tezos-shell-services @@ -70,6 +71,7 @@ -open Tezos_base.TzPervasives -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals -open Tezos_protocol_alpha + -open Tezos_protocol_alpha_parameters -open Tezos_stdlib_unix -open Tezos_shell_services -open Tezos_client_base -- GitLab From 7e259b0662bd8f466d00eab41db396ff692dd111 Mon Sep 17 00:00:00 2001 From: Pierre Boutillier Date: Fri, 7 Apr 2023 14:30:38 +0200 Subject: [PATCH 6/6] Changes: add entry for Baker PoW --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 75cccd7c92de..401afa80e7ec 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -69,6 +69,8 @@ Baker - Consensus operations with identical consensus-related contents but different ``branch`` fields are counted only once in the (pre)quorums. (MR :gl: `!8175`) +- Improved efficiency to solve the anti spam proof of work challenge. + Accuser ------- -- GitLab