From 84a2f1ee2959f8e5a97ca9938525f0c0191b7c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Henry?= Date: Sun, 19 Nov 2017 15:07:59 +0100 Subject: [PATCH] Shell: enforce the maximum number of operation per block --- scripts/alphanet.sh | 1 + scripts/client_lib.inc.sh | 1 + scripts/init-sandboxed-client.sh | 2 +- .../embedded/genesis/client_proto_main.ml | 28 ++++++++++--- src/node/shell/block_validator.ml | 32 +++++++++++++-- src/node/shell/block_validator.mli | 1 + src/node/shell/prevalidator.ml | 13 ++++--- src/proto/genesis/data.ml | 39 +++++++++++++------ src/proto/genesis/main.ml | 19 +++++---- src/proto/genesis/services.ml | 4 +- test/proto_alpha/proto_alpha_helpers.ml | 2 +- test/proto_alpha/test_endorsement.ml | 31 ++++++++------- test/test_basic.sh | 3 ++ test/test_contracts.sh | 3 ++ test/test_utils.sh | 1 + 15 files changed, 129 insertions(+), 51 deletions(-) diff --git a/scripts/alphanet.sh b/scripts/alphanet.sh index 437f84fdd..44fa65420 100755 --- a/scripts/alphanet.sh +++ b/scripts/alphanet.sh @@ -392,6 +392,7 @@ go_alpha_go() { activate \ protocol ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK \ with fitness 1 \ + and passes 1 \ and key dictator } diff --git a/scripts/client_lib.inc.sh b/scripts/client_lib.inc.sh index 1addb1983..9b99b1e22 100644 --- a/scripts/client_lib.inc.sh +++ b/scripts/client_lib.inc.sh @@ -207,6 +207,7 @@ activate_alpha() { -block genesis \ activate protocol ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK \ with fitness 1 \ + and passes 1 \ and key dictator } diff --git a/scripts/init-sandboxed-client.sh b/scripts/init-sandboxed-client.sh index 58a2ecde3..63fd039a3 100755 --- a/scripts/init-sandboxed-client.sh +++ b/scripts/init-sandboxed-client.sh @@ -27,7 +27,7 @@ add_sandboxed_bootstrap_identities | sed -e 's/^/## /' 1>&2 cat </dev/null 2>&1 ; then tezos-client-reset; fi ; alias tezos-client="$client" ; -alias tezos-activate-alpha="$client -block genesis activate protocol ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK with fitness 1 and key dictator" ; +alias tezos-activate-alpha="$client -block genesis activate protocol ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK with fitness 1 and passes 1 and key dictator" ; alias tezos-client-reset="rm -rf \"$client_dir\"; unalias tezos-client tezos-activate-alpha tezos-client-reset" ; alias tezos-autocomplete="source \"$script_dir/bash-completion.sh\"" ; trap tezos-client-reset EXIT ; diff --git a/src/client/embedded/genesis/client_proto_main.ml b/src/client/embedded/genesis/client_proto_main.ml index 2ae50e141..c1d94ae54 100644 --- a/src/client/embedded/genesis/client_proto_main.ml +++ b/src/client/embedded/genesis/client_proto_main.ml @@ -31,7 +31,7 @@ let forge_block let proto_level = match command with | Data.Command.Activate _ -> 1 - | Data.Command.Activate_testnet (_,_) -> 0 in + | Data.Command.Activate_testnet _ -> 0 in call_service1 rpc_config Services.Forge.block block ((net_id, Int32.succ pred.level, proto_level, @@ -43,13 +43,18 @@ let bake rpc_config ?timestamp block command fitness seckey = forge_block rpc_config ?timestamp block bi.net_id command fitness >>=? fun blk -> let signed_blk = Environment.Ed25519.Signature.append seckey blk in - Client_node_rpcs.inject_block rpc_config signed_blk [[]] + Client_node_rpcs.inject_block rpc_config signed_blk [] let int64_parameter = (Cli_entries.parameter (fun _ p -> try return (Int64.of_string p) with _ -> failwith "Cannot read int64")) +let int_parameter = + (Cli_entries.parameter (fun _ p -> + try return (int_of_string p) + with _ -> failwith "Cannot read int")) + let commands () = let open Cli_entries in let args = @@ -71,15 +76,20 @@ let commands () = @@ param ~name:"fitness" ~desc:"Hardcoded fitness of the first block (integer)" int64_parameter + @@ prefixes [ "and" ; "passes" ] + @@ param ~name:"passes" + ~desc:"Hardcoded number of validation passes (integer)" + int_parameter @@ prefixes [ "and" ; "key" ] @@ Client_keys.Secret_key.source_param ~name:"password" ~desc:"Dictator's key" @@ stop) - begin fun timestamp hash fitness seckey cctxt -> + begin fun timestamp hash fitness validation_passes seckey cctxt -> let fitness = Tezos_embedded_raw_protocol_alpha.Fitness_repr.from_int64 fitness in bake cctxt.rpc_config ?timestamp cctxt.config.block - (Activate hash) fitness seckey >>=? fun hash -> + (Activate { protocol = hash ; validation_passes }) + fitness seckey >>=? fun hash -> cctxt.answer "Injected %a" Block_hash.pp_short hash >>= fun () -> return () end ; @@ -92,15 +102,21 @@ let commands () = @@ param ~name:"fitness" ~desc:"Hardcoded fitness of the first block (integer)" int64_parameter + @@ prefixes [ "and" ; "passes" ] + @@ param ~name:"passes" + ~desc:"Hardcoded number of validation passes (integer)" + int_parameter @@ prefixes [ "and" ; "key" ] @@ Environment.Ed25519.Secret_key.param ~name:"password" ~desc:"Dictator's key" @@ stop) - begin fun timestamp hash fitness seckey cctxt -> + begin fun timestamp hash fitness validation_passes seckey cctxt -> let fitness = Tezos_embedded_raw_protocol_alpha.Fitness_repr.from_int64 fitness in bake cctxt.rpc_config ?timestamp cctxt.config.block - (Activate_testnet (hash, Int64.mul 24L 3600L)) + (Activate_testnet { protocol = hash ; + validation_passes ; + delay = Int64.mul 24L 3600L }) fitness seckey >>=? fun hash -> cctxt.answer "Injected %a" Block_hash.pp_short hash >>= fun () -> return () diff --git a/src/node/shell/block_validator.ml b/src/node/shell/block_validator.ml index 4dc69ae48..32408738a 100644 --- a/src/node/shell/block_validator.ml +++ b/src/node/shell/block_validator.ml @@ -50,6 +50,7 @@ type block_error = timestamp: Time.t ; } | Unexpected_number_of_validation_passes of int (* uint8 *) + | Too_many_operations of { pass: int; found: int; max: int } let block_error_encoding = let open Data_encoding in @@ -131,6 +132,18 @@ let block_error_encoding = | Unexpected_number_of_validation_passes n -> Some ((), n) | _ -> None) (fun ((), n) -> Unexpected_number_of_validation_passes n) ; + case + (obj4 + (req "error" (constant "too_many_operations")) + (req "validation_pass" uint8) + (req "found" uint16) + (req "max" uint16)) + (function + | Too_many_operations { pass ; found ; max } -> + Some ((), pass, found, max) + | _ -> None) + (fun ((), pass, found, max) -> + Too_many_operations { pass ; found ; max }) ; ] let pp_block_error ppf = function @@ -183,6 +196,10 @@ let pp_block_error ppf = function Format.fprintf ppf "Invalid number of validation passes (found: %d)" n + | Too_many_operations { pass ; found ; max } -> + Format.fprintf ppf + "Too many operations in validation pass %d (found: %d, max: %d)" + pass found max type error += | Invalid_block of @@ -263,7 +280,8 @@ let () = Inconsistent_operations_hash { block ; expected ; found }) let check_header - (pred_header: Block_header.t) hash (header: Block_header.t) = + (pred: State.Block.t) hash (header: Block_header.t) = + let pred_header = State.Block.header pred in fail_unless (Int32.succ pred_header.shell.level = header.shell.level) (invalid_block hash @@ @@ -276,7 +294,8 @@ let check_header Fitness.(pred_header.shell.fitness < header.shell.fitness) (invalid_block hash Non_increasing_fitness) >>=? fun () -> fail_unless - (header.shell.validation_passes <= 1) (* FIXME to be found in Proto *) + (header.shell.validation_passes = + List.length (State.Block.max_number_of_operations pred)) (invalid_block hash (Unexpected_number_of_validation_passes header.shell.validation_passes) ) >>=? fun () -> @@ -320,7 +339,14 @@ let apply_block operations = let pred_header = State.Block.header pred and pred_hash = State.Block.hash pred in - check_header pred_header hash header >>=? fun () -> + check_header pred hash header >>=? fun () -> + iteri2_p + (fun i ops max -> + fail_unless + (List.length ops <= max) + (invalid_block hash @@ + Too_many_operations { pass = i + 1 ; found = List.length ops ; max })) + operations (State.Block.max_number_of_operations pred) >>=? fun () -> let operation_hashes = List.map (List.map Operation.hash) operations in check_liveness net_state pred hash operation_hashes operations >>=? fun () -> map2_s (map2_s begin fun op_hash raw -> diff --git a/src/node/shell/block_validator.mli b/src/node/shell/block_validator.mli index 5a1ba16d9..f7978cbf3 100644 --- a/src/node/shell/block_validator.mli +++ b/src/node/shell/block_validator.mli @@ -26,6 +26,7 @@ type block_error = timestamp: Time.t ; } | Unexpected_number_of_validation_passes of int (* uint8 *) + | Too_many_operations of { pass: int; found: int; max: int } type error += | Invalid_block of diff --git a/src/node/shell/prevalidator.ml b/src/node/shell/prevalidator.ml index ccb701dca..b9205c9c8 100644 --- a/src/node/shell/prevalidator.ml +++ b/src/node/shell/prevalidator.ml @@ -153,16 +153,17 @@ let create else Lwt.return_none) (Operation_hash.Map.bindings ops) >>= fun rops -> - (Lwt.return !validation_state >>=? fun validation_state -> - (prevalidate validation_state ~sort:true rops >>= return)) >>= function - | Ok (state, r) -> Lwt.return (Ok state, r) + match !validation_state with + | Ok validation_state -> + prevalidate validation_state ~sort:true rops >>= fun (state, r) -> + Lwt.return (Ok state, r) | Error err -> let r = { empty_result with branch_delayed = - Operation_hash.Map.fold - (fun h op m -> Operation_hash.Map.add h (op, err) m) - ops Operation_hash.Map.empty ; } in + List.fold_left + (fun m (h, op) -> Operation_hash.Map.add h (op, err) m) + Operation_hash.Map.empty rops ; } in Lwt.return (!validation_state, r) end >>= fun (state, r) -> let filter_out s m = diff --git a/src/proto/genesis/data.ml b/src/proto/genesis/data.ml index 25820e50d..46631a4b6 100644 --- a/src/proto/genesis/data.ml +++ b/src/proto/genesis/data.ml @@ -11,10 +11,17 @@ module Command = struct type t = (* Activate a protocol *) - | Activate of Protocol_hash.t + | Activate of { + protocol: Protocol_hash.t ; + validation_passes: int ; + } (* Activate a protocol as a testnet *) - | Activate_testnet of Protocol_hash.t * Int64.t + | Activate_testnet of { + protocol: Protocol_hash.t ; + validation_passes: int ; + delay: Int64.t ; + } let mk_case name args = let open Data_encoding in @@ -30,18 +37,28 @@ module Command = struct union ~tag_size:`Uint8 [ case ~tag:0 (mk_case "activate" - (obj1 - (req "hash" Protocol_hash.encoding))) - (function (Activate hash) -> Some hash | _ -> None) - (fun hash -> Activate hash) ; - case ~tag:1 - (mk_case "activate_testnet" (obj2 (req "hash" Protocol_hash.encoding) + (req "validation_passes" uint8) + )) + (function + | Activate { protocol ; validation_passes } -> + Some (protocol, validation_passes) + | _ -> None) + (fun (protocol, validation_passes) -> + Activate { protocol ; validation_passes }) ; + case ~tag:1 + (mk_case "activate_testnet" + (obj3 + (req "hash" Protocol_hash.encoding) + (req "validation_passes" uint8) (req "validity_time" int64))) - (function (Activate_testnet (hash, delay)) -> Some (hash, delay) - | _ -> None) - (fun (hash, delay) -> Activate_testnet (hash, delay)) ; + (function + | Activate_testnet { protocol ; validation_passes ; delay } -> + Some (protocol, validation_passes, delay) + | _ -> None) + (fun (protocol, validation_passes, delay) -> + Activate_testnet { protocol ; validation_passes ; delay }) ; ] let signed_encoding = diff --git a/src/proto/genesis/main.ml b/src/proto/genesis/main.ml index f7c2c42a9..4f3c91ff1 100644 --- a/src/proto/genesis/main.ml +++ b/src/proto/genesis/main.ml @@ -47,7 +47,9 @@ type block = { let max_block_length = Data_encoding.Binary.length Data.Command.encoding - (Activate_testnet (Protocol_hash.hash_bytes [], 0L)) + (Activate_testnet { protocol = Protocol_hash.hash_bytes [] ; + validation_passes = 0 ; + delay = 0L }) + begin match Data_encoding.Binary.fixed_length Ed25519.Signature.encoding with @@ -89,22 +91,25 @@ let begin_application check_signature ctxt block >>=? fun () -> let fitness = raw_block.shell.fitness in match block.command with - | Data.Command.Activate hash -> + | Data.Command.Activate { protocol = hash ; validation_passes } -> let message = Some (Format.asprintf "activate %a" Protocol_hash.pp_short hash) in Updater.activate ctxt hash >>= fun ctxt -> return { Updater.message ; context = ctxt ; fitness ; max_operations_ttl = 0 ; - max_number_of_operations = [] ; + max_number_of_operations = + Array.to_list (Array.make validation_passes 0) ; max_operation_data_length = 0 } - | Activate_testnet (hash, delay) -> + | Activate_testnet { protocol = hash ; validation_passes ; delay } -> let message = Some (Format.asprintf "activate testnet %a" Protocol_hash.pp_short hash) in let expiration = Time.add raw_block.shell.timestamp delay in - Updater.fork_test_network ctxt hash expiration >>= fun ctxt -> + Updater.fork_test_network ctxt ~protocol:hash ~expiration >>= fun ctxt -> return { Updater.message ; context = ctxt ; fitness ; - max_operations_ttl = 0 ; max_operation_data_length = 0 ; - max_number_of_operations = [] } + max_operations_ttl = 0 ; + max_number_of_operations = + Array.to_list (Array.make validation_passes 0) ; + max_operation_data_length = 0 } let begin_construction ~predecessor_context:context diff --git a/src/proto/genesis/services.ml b/src/proto/genesis/services.ml index ab07f8655..51db0c10b 100644 --- a/src/proto/genesis/services.ml +++ b/src/proto/genesis/services.ml @@ -56,7 +56,7 @@ let int64_to_bytes i = b let operations_hash = - Operation_list_list_hash.compute [Operation_list_hash.empty] + Operation_list_list_hash.compute [] let rpc_services : Updater.rpc_context RPC.directory = let dir = RPC.empty in @@ -67,7 +67,7 @@ let rpc_services : Updater.rpc_context RPC.directory = (fun _ctxt ((_net_id, level, proto_level, predecessor, timestamp, fitness), command) -> let shell = { Block_header.level ; proto_level ; predecessor ; - timestamp ; fitness ; validation_passes = 1 ; operations_hash } in + timestamp ; fitness ; validation_passes = 0 ; operations_hash } in let bytes = Data.Command.forge shell command in RPC.Answer.return bytes) in dir diff --git a/test/proto_alpha/proto_alpha_helpers.ml b/test/proto_alpha/proto_alpha_helpers.ml index 894c1fa4c..6898142a3 100644 --- a/test/proto_alpha/proto_alpha_helpers.ml +++ b/test/proto_alpha/proto_alpha_helpers.ml @@ -29,7 +29,7 @@ let activate_alpha () = let fitness = Fitness_repr.from_int64 0L in Client_embedded_genesis.Client_proto_main.bake !rpc_config (`Head 0) - (Activate Client_proto_main.protocol) + (Activate { protocol = Client_proto_main.protocol ; validation_passes = 1}) fitness dictator_sk let init ?(sandbox = "sandbox.json") ?rpc_port () = diff --git a/test/proto_alpha/test_endorsement.ml b/test/proto_alpha/test_endorsement.ml index 73c699d27..67253ee85 100644 --- a/test/proto_alpha/test_endorsement.ml +++ b/test/proto_alpha/test_endorsement.ml @@ -195,44 +195,47 @@ let test_endorsement_rights contract block = let run genesis = + Helpers.Baking.bake genesis b1 [] >>=? fun blk -> + + let block = `Hash blk in test_endorsement_rights - default_account genesis >>=? fun has_right_to_endorse -> + default_account block >>=? fun has_right_to_endorse -> Assert.equal_bool ~msg:__LOC__ has_right_to_endorse false ; - test_endorsement_rights b1 genesis >>=? fun has_right_to_endorse -> + test_endorsement_rights b1 block >>=? fun has_right_to_endorse -> Assert.equal_bool ~msg:__LOC__ has_right_to_endorse true ; - test_endorsement_rights b1 genesis >>=? fun has_right_to_endorse -> + test_endorsement_rights b1 block >>=? fun has_right_to_endorse -> Assert.equal_bool ~msg:__LOC__ has_right_to_endorse true ; Assert.balance_equal - ~block:genesis ~msg:__LOC__ b1 4_000_000_00L >>=? fun () -> + ~block:block ~msg:__LOC__ b1 3_999_000_00L >>=? fun () -> Assert.balance_equal - ~block:genesis ~msg:__LOC__ b2 4_000_000_00L >>=? fun () -> + ~block:block ~msg:__LOC__ b2 4_000_000_00L >>=? fun () -> Assert.balance_equal - ~block:genesis ~msg:__LOC__ b3 4_000_000_00L >>=? fun () -> + ~block:block ~msg:__LOC__ b3 4_000_000_00L >>=? fun () -> Assert.balance_equal - ~block:genesis ~msg:__LOC__ b4 4_000_000_00L >>=? fun () -> + ~block:block ~msg:__LOC__ b4 4_000_000_00L >>=? fun () -> Assert.balance_equal - ~block:genesis ~msg:__LOC__ b5 4_000_000_00L >>=? fun () -> + ~block:block ~msg:__LOC__ b5 4_000_000_00L >>=? fun () -> (* Check Rewards *) - test_endorsement_rewards genesis >>=? fun () -> + test_endorsement_rewards block >>=? fun () -> (* Endorse with a contract with wrong delegate: - contract with no endorsement rights - contract which signs at every available slots *) - test_wrong_delegate ~baker:b1 default_account genesis >>= fun () -> - test_wrong_delegate ~baker:b1 b5 genesis >>= fun () -> + test_wrong_delegate ~baker:b1 default_account block >>= fun () -> + test_wrong_delegate ~baker:b1 b5 block >>= fun () -> (* Endorse with a wrong slot : -1 and max (16) *) - test_invalid_endorsement_slot b3 genesis >>=? fun () -> + test_invalid_endorsement_slot b3 block >>=? fun () -> (* FIXME: Baking.Invalid_signature is still unclassified *) - test_invalid_signature genesis >>=? fun _ -> + test_invalid_signature block >>=? fun _ -> (* FIXME: cannot inject double endorsement operation yet, but the code is still here Double endorsement *) - test_double_endorsement b4 genesis >>=? fun _ -> + test_double_endorsement b4 block >>=? fun _ -> return () diff --git a/test/test_basic.sh b/test/test_basic.sh index a77e66fad..863ec7b21 100755 --- a/test/test_basic.sh +++ b/test/test_basic.sh @@ -8,6 +8,9 @@ source $test_dir/lib/test_lib.inc.sh start_node 1 activate_alpha +sleep 2 +$client bake for bootstrap1 -max-priority 512 + key1=foo key2=bar diff --git a/test/test_contracts.sh b/test/test_contracts.sh index c16ad21a1..7e0093ce5 100755 --- a/test/test_contracts.sh +++ b/test/test_contracts.sh @@ -9,6 +9,9 @@ source $test_dir/lib/test_lib.inc.sh start_node 1 activate_alpha +sleep 2 +$client bake for bootstrap5 -max-priority 512 + key1=foo key2=bar diff --git a/test/test_utils.sh b/test/test_utils.sh index d4f569f31..b6451c8c4 100755 --- a/test/test_utils.sh +++ b/test/test_utils.sh @@ -80,6 +80,7 @@ activate_alpha() { activate \ protocol ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK \ with fitness 1 \ + and passes 1 \ and key edskRhxswacLW6jF6ULavDdzwqnKJVS4UcDTNiCyiH6H8ZNnn2pmNviL7pRNz9kRxxaWQFzEQEcZExGHKbwmuaAcoMegj5T99z \ > /dev/stderr }