diff --git a/CHANGES.rst b/CHANGES.rst index 266e524c3a84e2ba06874d7167abd741ec82c573..27fb9e5078f3abaf7cc040647d9b9def209f53d2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -59,6 +59,21 @@ Client Baker ----- +- Changed the baker default semantics so that it performs a light + validation of operations to classify them instead of fully applying + them. Hence, the block production is now more + time/cpu/disk-efficient. In this mode, application-dependent checks + are disabled. Setting the ``--force-apply`` flag on the command line + restores the previous behavior. (MR :gl:`!7490`) + +- **Breaking Change**: Disabled the verification of signature of + operations in the baker when baking a block. The baker must always + be provided operations with a valid signature, otherwise produced + blocks will be invalid and rejected by local nodes during their + injection. Default setups are not affected but external mempools + should make sure that their operations' signatures are correct. + (MR :gl:`!7490`) + Accuser ------- diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_actions.ml b/src/proto_016_PtMumbai/lib_delegate/baking_actions.ml index 56f56616c1518cb5a86cd02fc7bf9ba59241b6f8..e05821d643bce2bc756ee10fcb8fccd7de84e15f 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_actions.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_actions.ml @@ -306,10 +306,12 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = ~chain_id ~pred_info:predecessor ~timestamp + ~round ~seed_nonce_hash ~payload_round ~liquidity_baking_toggle_vote ~user_activated_upgrades + ~force_apply:state.global_state.config.force_apply state.global_state.config.fees simulation_mode simulation_kind diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_commands.ml b/src/proto_016_PtMumbai/lib_delegate/baking_commands.ml index b827d5b7caddb32dd1275d63b705b5f83824f6f3..33dd83a2bdccaefec59e370055cfeb6b25faa440 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_commands.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_commands.ml @@ -114,6 +114,12 @@ let context_path_arg = 'preapply' RPC." string_parameter +let force_apply_switch_arg = + Tezos_clic.switch + ~long:"force-apply" + ~doc:"Force the baker to not only validate but also apply operations." + () + let endorsement_force_switch_arg = Tezos_clic.switch ~long:"force" @@ -218,11 +224,12 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list command ~group ~desc:"Forge and inject block using the delegates' rights." - (args8 + (args9 minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg minimal_timestamp_switch + force_apply_switch_arg force_switch operations_arg context_path_arg @@ -232,6 +239,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, minimal_timestamp, + force_apply, force, extra_operations, context_path, @@ -245,6 +253,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list ~minimal_timestamp ~minimal_nanotez_per_byte ~minimal_fees + ~force_apply ~force ~monitor_node_mempool:(not do_not_monitor_node_mempool) ?extra_operations @@ -269,11 +278,12 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list command ~group ~desc:"Send a Tenderbake proposal" - (args7 + (args8 minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg minimal_timestamp_switch + force_apply_switch_arg force_switch operations_arg context_path_arg) @@ -282,6 +292,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, minimal_timestamp, + force_apply, force, extra_operations, context_path ) @@ -294,6 +305,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list ~minimal_timestamp ~minimal_nanotez_per_byte ~minimal_fees + ~force_apply ~force ?extra_operations ?context_path @@ -326,11 +338,12 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = command ~group ~desc:"Launch the baker daemon." - (args8 + (args9 pidfile_arg minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg + force_apply_switch_arg keep_alive_arg liquidity_baking_toggle_vote_arg per_block_vote_file_arg @@ -345,6 +358,7 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = minimal_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, + force_apply, keep_alive, liquidity_baking_toggle_vote, per_block_vote_file, @@ -378,6 +392,7 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = ~liquidity_baking_toggle_vote ?per_block_vote_file ?extra_operations + ~force_apply ~chain:cctxt#chain ~context_path ~keep_alive diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_configuration.ml b/src/proto_016_PtMumbai/lib_delegate/baking_configuration.ml index 2b8dca57b0f677def353758a78797b583054f4b6..6db8eccb9f3dca5a40de608853ab244f6d461d96 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_configuration.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_configuration.ml @@ -85,6 +85,7 @@ type t = { liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote; per_block_vote_file : string option; + force_apply : bool; force : bool; state_recorder : state_recorder_config; extra_operations : Operations_source.t option; @@ -113,6 +114,8 @@ let default_liquidity_baking_toggle_vote = let default_force = false +let default_force_apply = false + let default_state_recorder_config = Filesystem let default_extra_operations = None @@ -127,6 +130,7 @@ let default_config = retries_on_failure = default_retries_on_failure_config; user_activated_upgrades = default_user_activated_upgrades; liquidity_baking_toggle_vote = default_liquidity_baking_toggle_vote; + force_apply = default_force_apply; force = default_force; state_recorder = default_state_recorder_config; extra_operations = default_extra_operations; @@ -141,8 +145,9 @@ let make ?(minimal_fees = default_fees_config.minimal_fees) ?(retries_on_failure = default_retries_on_failure_config) ?(user_activated_upgrades = default_user_activated_upgrades) ?(liquidity_baking_toggle_vote = default_liquidity_baking_toggle_vote) - ?per_block_vote_file ?(force = default_force) - ?(state_recorder = default_state_recorder_config) ?extra_operations () = + ?per_block_vote_file ?(force_apply = default_force_apply) + ?(force = default_force) ?(state_recorder = default_state_recorder_config) + ?extra_operations () = let fees = {minimal_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte} in @@ -159,6 +164,7 @@ let make ?(minimal_fees = default_fees_config.minimal_fees) user_activated_upgrades; liquidity_baking_toggle_vote; per_block_vote_file; + force_apply; force; state_recorder; extra_operations; @@ -228,6 +234,8 @@ let liquidity_baking_toggle_vote_config_encoding = let force_config_encoding = Data_encoding.bool +let force_apply_config_encoding = Data_encoding.bool + let state_recorder_config_encoding = let open Data_encoding in union @@ -254,37 +262,28 @@ let encoding : t Data_encoding.t = ~title:"Baking configuration" ~description:"Baking configuration" @@ conv - (fun { - fees; - validation; - nonce; - retries_on_failure; - user_activated_upgrades; - liquidity_baking_toggle_vote; - per_block_vote_file; - force; - state_recorder; - extra_operations; - } -> - ( fees, - validation, - nonce, - retries_on_failure, - user_activated_upgrades, - liquidity_baking_toggle_vote, - per_block_vote_file, - force, - state_recorder, - extra_operations )) - (fun ( fees, - validation, - nonce, - retries_on_failure, - user_activated_upgrades, - liquidity_baking_toggle_vote, - per_block_vote_file, - force, - state_recorder, + (fun c -> + ( ( c.fees, + c.validation, + c.nonce, + c.retries_on_failure, + c.user_activated_upgrades, + c.liquidity_baking_toggle_vote, + c.per_block_vote_file, + c.force_apply, + c.force, + c.state_recorder ), + c.extra_operations )) + (fun ( ( fees, + validation, + nonce, + retries_on_failure, + user_activated_upgrades, + liquidity_baking_toggle_vote, + per_block_vote_file, + force_apply, + force, + state_recorder ), extra_operations ) -> { fees; @@ -294,23 +293,28 @@ let encoding : t Data_encoding.t = user_activated_upgrades; liquidity_baking_toggle_vote; per_block_vote_file; + force_apply; force; state_recorder; extra_operations; }) - (obj10 - (req "fees" fees_config_encoding) - (req "validation" validation_config_encoding) - (req "nonce" nonce_config_encoding) - (req "retries_on_failure" retries_on_failure_config_encoding) - (req "user_activated_upgrades" user_activate_upgrades_config_encoding) - (req - "liquidity_baking_toggle_vote" - liquidity_baking_toggle_vote_config_encoding) - (opt "per_block_vote_file" Data_encoding.string) - (req "force" force_config_encoding) - (req "state_recorder" state_recorder_config_encoding) - (opt "extra_operations" Operations_source.encoding)) + (merge_objs + (obj10 + (req "fees" fees_config_encoding) + (req "validation" validation_config_encoding) + (req "nonce" nonce_config_encoding) + (req "retries_on_failure" retries_on_failure_config_encoding) + (req + "user_activated_upgrades" + user_activate_upgrades_config_encoding) + (req + "liquidity_baking_toggle_vote" + liquidity_baking_toggle_vote_config_encoding) + (opt "per_block_vote_file" Data_encoding.string) + (req "force_apply" force_apply_config_encoding) + (req "force" force_config_encoding) + (req "state_recorder" state_recorder_config_encoding)) + (obj1 (opt "extra_operations" Operations_source.encoding))) let pp fmt t = let json = Data_encoding.Json.construct encoding t in diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_configuration.mli b/src/proto_016_PtMumbai/lib_delegate/baking_configuration.mli index a1908d29dd229c880ddde6d955c552046951b30c..2cd4946e14a80abb4b1f867d34aea3331724d142 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_configuration.mli +++ b/src/proto_016_PtMumbai/lib_delegate/baking_configuration.mli @@ -60,6 +60,7 @@ type t = { liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote; per_block_vote_file : string option; + force_apply : bool; force : bool; state_recorder : state_recorder_config; extra_operations : Operations_source.t option; @@ -78,6 +79,8 @@ val default_user_activated_upgrades : (int32 * Protocol_hash.t) list val default_liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote +val default_force_apply : bool + val default_force : bool val default_state_recorder_config : state_recorder_config @@ -99,6 +102,7 @@ val make : ?liquidity_baking_toggle_vote: Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote -> ?per_block_vote_file:string -> + ?force_apply:bool -> ?force:bool -> ?state_recorder:state_recorder_config -> ?extra_operations:Operations_source.t -> diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_lib.ml b/src/proto_016_PtMumbai/lib_delegate/baking_lib.ml index bb2cb7b3f4ec7e05d4ccbb5b18bbfa0c9aa2fd45..5adbda789e44c39f1c44b0ef3e94acce5ce6ec05 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_lib.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_lib.ml @@ -279,7 +279,7 @@ let endorsement_quorum state = - Yes :: repropose block with right payload and preendorsements for current round - No :: repropose fresh block for current round *) let propose (cctxt : Protocol_client_context.full) ?minimal_fees - ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force + ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force_apply ?force ?(minimal_timestamp = false) ?extra_operations ?context_path delegates = let open Lwt_result_syntax in let* _block_stream, current_proposal = get_current_proposal cctxt in @@ -289,6 +289,7 @@ let propose (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?context_path + ?force_apply ?force ?extra_operations () @@ -490,7 +491,7 @@ let baking_minimal_timestamp state = return_unit let bake (cctxt : Protocol_client_context.full) ?minimal_fees - ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force + ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force_apply ?force ?(minimal_timestamp = false) ?extra_operations ?(monitor_node_mempool = true) ?context_path delegates = let open Lwt_result_syntax in @@ -500,6 +501,7 @@ let bake (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?context_path + ?force_apply ?force ?extra_operations () diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_lib.mli b/src/proto_016_PtMumbai/lib_delegate/baking_lib.mli index e37f01ac7bb52d7b8d899274fcdb629b5f886674..b1de5798925abd8a25a59b4b028780bfc868b27e 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_lib.mli +++ b/src/proto_016_PtMumbai/lib_delegate/baking_lib.mli @@ -32,6 +32,7 @@ val bake : ?minimal_fees:Tez.t -> ?minimal_nanotez_per_gas_unit:Q.t -> ?minimal_nanotez_per_byte:Q.t -> + ?force_apply:bool -> ?force:bool -> ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> @@ -57,6 +58,7 @@ val propose : ?minimal_fees:Tez.t -> ?minimal_nanotez_per_gas_unit:Q.t -> ?minimal_nanotez_per_byte:Q.t -> + ?force_apply:bool -> ?force:bool -> ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_simulator.ml b/src/proto_016_PtMumbai/lib_delegate/baking_simulator.ml index 5ae61e3f47c91eb8cf3568c4c7bd85fbd32c9494..74f2b7519cfb326db4951a70667ad717e5bf49e1 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_simulator.ml +++ b/src/proto_016_PtMumbai/lib_delegate/baking_simulator.ml @@ -54,7 +54,7 @@ let () = type incremental = { predecessor : Baking_state.block_info; context : Tezos_protocol_environment.Context.t; - state : Protocol.validation_state * Protocol.application_state; + state : Protocol.validation_state * Protocol.application_state option; rev_operations : Operation.packed list; header : Tezos_base.Block_header.shell_header; } @@ -76,7 +76,7 @@ let check_context_consistency (abstract_index : Abstract_context_index.t) | true -> return_unit | false -> fail Invalid_context)) -let begin_construction ~timestamp ~protocol_data +let begin_construction ~timestamp ~protocol_data ~force_apply (abstract_index : Abstract_context_index.t) predecessor chain_id = protect (fun () -> let { @@ -119,12 +119,15 @@ let begin_construction ~timestamp ~protocol_data ~predecessor:pred_shell ~cache:`Lazy >>=? fun validation_state -> - Lifted_protocol.begin_application - context - chain_id - mode - ~predecessor:pred_shell - ~cache:`Lazy + (if force_apply then + Lifted_protocol.begin_application + context + chain_id + mode + ~predecessor:pred_shell + ~cache:`Lazy + >>=? return_some + else return_none) >>=? fun application_state -> let state = (validation_state, application_state) in return {predecessor; context; state; rev_operations = []; header}) @@ -140,10 +143,23 @@ let add_operation st (op : Operation.packed) = let validation_state, application_state = st.state in let oph = Operation.hash_packed op in let** validation_state = - Protocol.validate_operation validation_state oph op + Protocol.validate_operation + ~check_signature:false + (* We assume that the operation has already been validated in the + node, therefore the signature has already been checked, but we + still need to validate it again because the context may be + different. *) + validation_state + oph + op in let** application_state, receipt = - Protocol.apply_operation application_state oph op + match application_state with + | Some application_state -> + Protocol.apply_operation application_state oph op + >>=? fun (application_state, receipt) -> + return (Some application_state, Some receipt) + | None -> return (None, None) in let state = (validation_state, application_state) in return ({st with state; rev_operations = op :: st.rev_operations}, receipt)) @@ -153,6 +169,10 @@ let finalize_construction inc = let validation_state, application_state = inc.state in let** () = Protocol.finalize_validation validation_state in let** result = - Protocol.finalize_application application_state (Some inc.header) + match application_state with + | Some application_state -> + Protocol.finalize_application application_state (Some inc.header) + >>=? return_some + | None -> return_none in return result) diff --git a/src/proto_016_PtMumbai/lib_delegate/baking_simulator.mli b/src/proto_016_PtMumbai/lib_delegate/baking_simulator.mli index 0d0c7ac9f4d2fee3a7036c81a7ea04ba7a581ca5..959546fe6d2178f6113776cb4d9872b4a41d5dc0 100644 --- a/src/proto_016_PtMumbai/lib_delegate/baking_simulator.mli +++ b/src/proto_016_PtMumbai/lib_delegate/baking_simulator.mli @@ -29,7 +29,7 @@ open Alpha_context type incremental = { predecessor : Baking_state.block_info; context : Tezos_protocol_environment.Context.t; - state : validation_state * application_state; + state : validation_state * application_state option; rev_operations : Operation.packed list; header : Tezos_base.Block_header.shell_header; } @@ -41,21 +41,40 @@ val load_context : val check_context_consistency : Abstract_context_index.t -> Context_hash.t -> unit tzresult Lwt.t +(** [begin_construction ~timestamp ~protocol_data ~force_apply abstract_context + predecessor chain_id] creates a new [incremental] value with an empty + operation list. A [context] is recovered from the [abstract_index] and the + resulting_context_hash from [predecessor]. This context is used to create a + [validation_state] and an [application_state] (if [force_apply] is set). A + partial [shell_header] is created from [predecessor] information and + [timestamp]. *) val begin_construction : timestamp:Time.Protocol.t -> protocol_data:block_header_data -> + force_apply:bool -> Abstract_context_index.t -> Baking_state.block_info -> Chain_id.t -> incremental tzresult Lwt.t +(** [add_operation incremental op] validates [op] in + [incremental.validation_state] without checking its signature. Indeed, the + operation has already been validated in the node so it has a correct + signature. We still need to validate it again because the context may be + different. [op] is also applied if [incremental] has been created with + [force_apply] set. This function returns an [incremental] with updated + operations list and [validation_state] (and [application_state]). *) val add_operation : incremental -> Operation.packed -> - (incremental * operation_receipt) tzresult Lwt.t + (incremental * operation_receipt option) tzresult Lwt.t +(** [finalize_construction incremental] calls the [finalize_validation] of the + protocol on the [validation_state] from [incremental]. If [incremental] has + been created with [force_apply] set, [finalize_application] is also called + and its results returned. *) val finalize_construction : incremental -> - (Tezos_protocol_environment.validation_result * block_header_metadata) + (Tezos_protocol_environment.validation_result * block_header_metadata) option tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/lib_delegate/block_forge.ml b/src/proto_016_PtMumbai/lib_delegate/block_forge.ml index 062306d6367d7a0c503a6dfa2f283a45716dc282..d3be206ac6b6bae99993ebae1cf6a7ce14616d43 100644 --- a/src/proto_016_PtMumbai/lib_delegate/block_forge.ml +++ b/src/proto_016_PtMumbai/lib_delegate/block_forge.ml @@ -40,6 +40,11 @@ type simulation_kind = type simulation_mode = Local of Context.index | Node +(* [forge_faked_protocol_data ?payload_hash ~payload_round ~seed_nonce_hash + ~liquidity_baking_toggle_vote] forges a fake [block_header_data] with + [payload_hash] ([zero] by default), [payload_round], [seed_nonce_hash], + [liquidity_baking_toggle_vote] and with an empty [proof_of_work_nonce] and a + dummy [signature]. *) let forge_faked_protocol_data ?(payload_hash = Block_payload_hash.zero) ~payload_round ~seed_nonce_hash ~liquidity_baking_toggle_vote () = Block_header. @@ -64,43 +69,37 @@ let convert_operation (op : packed_operation) : Tezos_base.Operation.t = op.protocol_data; } -(* Build the block header : mimics node prevalidation *) -let finalize_block_header shell_header timestamp validation_result - operations_hash predecessor_block_metadata_hash - predecessor_ops_metadata_hash predecessor_resulting_context = - let {Tezos_protocol_environment.context; fitness; message; _} = - validation_result +(* [finalize_block_header ~shell_header ~validation_result ~operations_hash + ~pred_info ~round ~locked_round] updates the [shell_header] that was created + with dummy fields at the beginning of the block construction. It increments + the [level] and sets the actual [operations_hash], [fitness], + [validation_passes], and [context] (the predecessor resulting context hash). + + When the operations from the block have been applied, the [fitness] is simply + retrieved from the [validation_result]. Otherwise, the [fitness] is computed + from the [round] and [locked_round] arguments. *) +let finalize_block_header ~shell_header ~validation_result ~operations_hash + ~(pred_info : Baking_state.block_info) ~round ~locked_round = + let open Lwt_result_syntax in + let* fitness = + match validation_result with + | Some {Tezos_protocol_environment.fitness; _} -> return fitness + | None -> + let*? level = + Environment.wrap_tzresult @@ Raw_level.of_int32 + @@ Int32.succ shell_header.Tezos_base.Block_header.level + in + let*? fitness = + Environment.wrap_tzresult + @@ Fitness.create + ~level + ~round + ~predecessor_round:pred_info.round + ~locked_round + in + return (Fitness.to_raw fitness) in let validation_passes = List.length Main.validation_passes in - (Context_ops.get_test_chain context >>= function - | Not_running -> return context - | Running {expiration; _} -> - if Time.Protocol.(expiration <= timestamp) then - Context_ops.add_test_chain context Not_running >>= fun context -> - return context - else return context - | Forking _ -> assert false) - >>=? fun context -> - (match predecessor_block_metadata_hash with - | Some predecessor_block_metadata_hash -> - Context_ops.add_predecessor_block_metadata_hash - context - predecessor_block_metadata_hash - | None -> Lwt.return context) - >>= fun context -> - (match predecessor_ops_metadata_hash with - | Some predecessor_ops_metadata_hash -> - Context_ops.add_predecessor_ops_metadata_hash - context - predecessor_ops_metadata_hash - | None -> Lwt.return context) - >>= fun context -> - let context = Context_ops.hash ~time:timestamp ?message context in - (* For the time being, we still fully build the block while we build - confidence to fully unplug the baker validation. The resulting - context hash is ignored as it is not necessary to craft a block. - See: https://gitlab.com/tezos/tezos/-/issues/4285 *) - ignore context ; let header = Tezos_base.Block_header. { @@ -109,7 +108,7 @@ let finalize_block_header shell_header timestamp validation_result validation_passes; operations_hash; fitness; - context = predecessor_resulting_context; + context = pred_info.resulting_context_hash; } in return header @@ -120,237 +119,261 @@ let retain_live_operations_only ~live_blocks operation_pool = Block_hash.Set.mem shell.branch live_blocks) operation_pool -let forge (cctxt : #Protocol_client_context.full) ~chain_id ~pred_info - ~timestamp ~liquidity_baking_toggle_vote ~user_activated_upgrades - fees_config ~seed_nonce_hash ~payload_round simulation_mode simulation_kind - constants = - let predecessor_block = (pred_info : Baking_state.block_info) in - let hard_gas_limit_per_block = - constants.Constants.Parametric.hard_gas_limit_per_block - in +(* [check_protocol_changed] checks whether the protocol will change with the current + block. This function returns true if the block is the last of an [adoption] + period. It can also return true if an [user_activated_upgrades] is given. *) +let check_protocol_changed ~user_activated_upgrades ~level + ~(validation_result : Tezos_protocol_environment.validation_result option) + ~(incremental : Baking_simulator.incremental) = + let open Lwt_result_syntax in + match + Tezos_base.Block_header.get_forced_protocol_upgrade + ~user_activated_upgrades + ~level + with + | None -> ( + match validation_result with + | None -> ( + let context = Validate.get_initial_ctxt (fst incremental.state) in + let* voting_period = + Lwt.map + Environment.wrap_tzresult + (Voting_period.get_current context) + in + match voting_period.kind with + | Voting_period.Proposal | Exploration | Cooldown | Promotion -> + return false + | Adoption -> + Lwt.map + Environment.wrap_tzresult + (Voting_period.is_last_block context)) + | Some validation_result -> + let*! next_protocol = + Context_ops.get_protocol validation_result.context + in + return Protocol_hash.(Protocol.hash <> next_protocol)) + | Some next_protocol -> return Protocol_hash.(Protocol.hash <> next_protocol) + +(* [filter_via_node] filters operations using + {!Operation_selection.filter_operations_without_simulation} and then applies + them in a block via {!Node_rpc.preapply_block}. [filter_via_node] returns a + [shell_header], the list of operations that have been applied in the block + and the [payload_hash] corresponding to these operations. *) +let filter_via_node ~chain_id ~fees_config ~hard_gas_limit_per_block + ~faked_protocol_data ~timestamp ~(pred_info : Baking_state.block_info) + ~payload_round ~operation_pool cctxt = + let open Lwt_result_syntax in let chain = `Hash chain_id in - let check_protocol_changed - ~(validation_result : Tezos_protocol_environment.validation_result) = - Context_ops.get_protocol validation_result.context >>= fun next_protocol -> - let next_protocol = - match - Tezos_base.Block_header.get_forced_protocol_upgrade - ~user_activated_upgrades - ~level:(Int32.succ predecessor_block.shell.level) - with - | None -> next_protocol - | Some hash -> hash - in - return Protocol_hash.(Protocol.hash <> next_protocol) + let filtered_operations = + Operation_selection.filter_operations_without_simulation + fees_config + ~hard_gas_limit_per_block + operation_pool in - let filter_via_node ~operation_pool = - let filtered_operations = - Operation_selection.filter_operations_without_simulation - fees_config - ~hard_gas_limit_per_block - operation_pool - in - let faked_protocol_data = - forge_faked_protocol_data - ~payload_round - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - () - in + let* shell_header, preapply_result = Node_rpc.preapply_block cctxt ~chain - ~head:predecessor_block.hash + ~head:pred_info.hash ~timestamp ~protocol_data:faked_protocol_data filtered_operations - >>=? fun (shell_header, preapply_result) -> - (* only retain valid operations *) - let operations = - List.map (fun l -> List.map snd l.Preapply_result.applied) preapply_result - in - let payload_hash = - let operation_hashes = - Stdlib.List.tl operations |> List.flatten - |> List.map Tezos_base.Operation.hash - in - Block_payload.hash - ~predecessor_hash:shell_header.predecessor - ~payload_round - operation_hashes - in - return (shell_header, operations, payload_hash) in - let filter_with_context ~context_index ~operation_pool = - let faked_protocol_data = - forge_faked_protocol_data - ~payload_round - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - () + (* only retain valid operations *) + let operations = + List.map (fun l -> List.map snd l.Preapply_result.applied) preapply_result + in + let payload_hash = + let operation_hashes = + Stdlib.List.tl operations |> List.flatten + |> List.map Tezos_base.Operation.hash in + Block_payload.hash + ~predecessor_hash:shell_header.predecessor + ~payload_round + operation_hashes + in + return (shell_header, operations, payload_hash) + +(* [filter_with_context] filters operations using a local context via + {!Operation_selection.filter_operations_with_simulation} and a fresh state + from {!Baking_simulator.begin_construction}. [finalize_block_header] is then + called and a [shell_header], the list of operations and the corresponding + [payload_hash] are returned. If the block is a transition block, + [filter_via_node] is called to return these values. *) +let filter_with_context ~chain_id ~fees_config ~hard_gas_limit_per_block + ~faked_protocol_data ~user_activated_upgrades ~timestamp + ~(pred_info : Baking_state.block_info) ~force_apply ~round ~context_index + ~payload_round ~operation_pool cctxt = + let open Lwt_result_syntax in + let* incremental = Baking_simulator.begin_construction ~timestamp ~protocol_data:faked_protocol_data + ~force_apply context_index - predecessor_block + pred_info chain_id - >>=? fun incremental -> + in + let* {Operation_selection.operations; validation_result; operations_hash; _} = Operation_selection.filter_operations_with_simulation incremental fees_config ~hard_gas_limit_per_block operation_pool - >>=? fun { - Operation_selection.operations; - validation_result; - operations_hash; - _; - } -> - check_protocol_changed ~validation_result >>=? fun changed -> - if changed then - (* Fallback to processing via node, which knows both old and new protocol. *) - filter_via_node ~operation_pool - else - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.metadata_hash - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_block_metadata_hash -> - return (Some pred_block_metadata_hash)) - >>=? fun pred_block_metadata_hash -> - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.Operation_metadata_hashes.root - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_op_metadata_hash -> return (Some pred_op_metadata_hash)) - >>=? fun pred_op_metadata_hash -> + in + let* changed = + check_protocol_changed + ~level:(Int32.succ pred_info.shell.level) + ~user_activated_upgrades + ~validation_result + ~incremental + in + if changed then + (* Fallback to processing via node, which knows both old and new protocol. *) + filter_via_node + ~chain_id + ~fees_config + ~hard_gas_limit_per_block + ~faked_protocol_data + ~timestamp + ~pred_info + ~payload_round + ~operation_pool + cctxt + else + let* shell_header = finalize_block_header - incremental.header - timestamp - validation_result - operations_hash - pred_block_metadata_hash - pred_op_metadata_hash - predecessor_block.resulting_context_hash - >>=? fun shell_header -> - let operations = List.map (List.map convert_operation) operations in - let payload_hash = - let operation_hashes = - Stdlib.List.tl operations |> List.flatten - |> List.map Tezos_base.Operation.hash - in - Block_payload.hash - ~predecessor_hash:shell_header.predecessor - ~payload_round - operation_hashes + ~shell_header:incremental.header + ~validation_result + ~operations_hash + ~pred_info + ~round + ~locked_round:None + in + let operations = List.map (List.map convert_operation) operations in + let payload_hash = + let operation_hashes = + Stdlib.List.tl operations |> List.flatten + |> List.map Tezos_base.Operation.hash in - return (shell_header, operations, payload_hash) - in - let apply_via_node ~ordered_pool ~payload_hash = - let operations = Operation_pool.ordered_to_list_list ordered_pool in - let faked_protocol_data = - forge_faked_protocol_data - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - ~payload_hash + Block_payload.hash + ~predecessor_hash:shell_header.predecessor ~payload_round - () + operation_hashes in + return (shell_header, operations, payload_hash) + +(* [apply_via_node] applies already filtered and validated operations in a block + via {!Node_rpc.preapply_block}. A [shell_header] is recovered from this call + and returned alongside of the list of operations and the payload_hash. *) +let apply_via_node ~chain_id ~faked_protocol_data ~timestamp + ~(pred_info : Baking_state.block_info) ~ordered_pool ~payload_hash cctxt = + let open Lwt_result_syntax in + let chain = `Hash chain_id in + let operations = Operation_pool.ordered_to_list_list ordered_pool in + let* shell_header, _preapply_result = Node_rpc.preapply_block cctxt ~chain - ~head:predecessor_block.hash + ~head:pred_info.hash ~timestamp ~protocol_data:faked_protocol_data operations - >>=? fun (shell_header, _preapply_result) -> - let operations = List.map (List.map convert_operation) operations in - return (shell_header, operations, payload_hash) in - let apply_with_context ~context_index ~ordered_pool ~payload_hash = - let faked_protocol_data = - forge_faked_protocol_data - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - ~payload_hash - ~payload_round - () - in - Shell_services.Chain.chain_id cctxt ~chain () >>=? fun chain_id -> + let operations = List.map (List.map convert_operation) operations in + return (shell_header, operations, payload_hash) + +(* [apply_with_context] is similar to [filter_with_context] but filters + consensus operations only from an [ordered_pool] via + {!Operation_selection.filter_consensus_operations_only}. *) +let apply_with_context ~chain_id ~faked_protocol_data ~user_activated_upgrades + ~timestamp ~(pred_info : Baking_state.block_info) ~force_apply ~round + ~ordered_pool ~context_index ~payload_hash cctxt = + let open Lwt_result_syntax in + let* incremental = Baking_simulator.begin_construction ~timestamp ~protocol_data:faked_protocol_data + ~force_apply context_index - predecessor_block + pred_info chain_id - >>=? fun incremental -> - (* We still need to filter endorsements. Two endorsements could be - referring to the same slot. *) + in + (* We still need to filter endorsements. Two endorsements could be + referring to the same slot. *) + let* incremental, ordered_pool = Operation_selection.filter_consensus_operations_only incremental ordered_pool - >>=? fun (incremental, ordered_pool) -> - let operations = Operation_pool.ordered_to_list_list ordered_pool in - let operations_hash = - Operation_list_list_hash.compute - (List.map - (fun sl -> - Operation_list_hash.compute (List.map Operation.hash_packed sl)) - operations) - in - (* We need to compute the final [operations_hash] before - finalizing the block because it will be used in the cache's nonce. *) - let incremental = - {incremental with header = {incremental.header with operations_hash}} + in + let operations = Operation_pool.ordered_to_list_list ordered_pool in + let operations_hash = + Operation_list_list_hash.compute + (List.map + (fun sl -> + Operation_list_hash.compute (List.map Operation.hash_packed sl)) + operations) + in + (* We need to compute the final [operations_hash] before + finalizing the block because it will be used in the cache's nonce. *) + let incremental = + {incremental with header = {incremental.header with operations_hash}} + in + let* validation_result = Baking_simulator.finalize_construction incremental in + let validation_result = Option.map fst validation_result in + let* changed = + check_protocol_changed + ~level:(Int32.succ pred_info.shell.level) + ~user_activated_upgrades + ~validation_result + ~incremental + in + if changed then + (* Fallback to processing via node, which knows both old and new protocol. *) + apply_via_node + ~chain_id + ~faked_protocol_data + ~timestamp + ~pred_info + ~ordered_pool + ~payload_hash + cctxt + else + let locked_round_when_no_validation_result = + (* [locked_round] will not be used in [finalize_block_header] if there is + a [validation_result] *) + if Option.is_some validation_result then None + else + List.find_map + (fun {protocol_data = Operation_data protocol_data; _} -> + match protocol_data.contents with + | Single (Preendorsement {round; _}) -> Some round + | _ -> None) + (Option.value (List.hd operations) ~default:[]) in - Baking_simulator.finalize_construction incremental - >>=? fun (validation_result, _) -> - check_protocol_changed ~validation_result >>=? fun changed -> - if changed then - (* Fallback to processing via node, which knows both old and new protocol. *) - apply_via_node ~ordered_pool ~payload_hash - else - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.metadata_hash - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_block_metadata_hash -> - return (Some pred_block_metadata_hash)) - >>=? fun pred_block_metadata_hash -> - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.Operation_metadata_hashes.root - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_op_metadata_hash -> return (Some pred_op_metadata_hash)) - >>=? fun pred_op_metadata_hash -> + let* shell_header = finalize_block_header - incremental.header - timestamp - validation_result - operations_hash - pred_block_metadata_hash - pred_op_metadata_hash - predecessor_block.resulting_context_hash - >>=? fun shell_header -> - let operations = List.map (List.map convert_operation) operations in - return (shell_header, operations, payload_hash) + ~shell_header:incremental.header + ~validation_result + ~operations_hash + ~pred_info + ~round + ~locked_round:locked_round_when_no_validation_result + in + let operations = List.map (List.map convert_operation) operations in + return (shell_header, operations, payload_hash) + +(* [forge] a new [unsigned_block] in accordance with [simulation_kind] and + [simulation_mode] *) +let forge (cctxt : #Protocol_client_context.full) ~chain_id + ~(pred_info : Baking_state.block_info) ~timestamp ~round + ~liquidity_baking_toggle_vote ~user_activated_upgrades fees_config + ~force_apply ~seed_nonce_hash ~payload_round simulation_mode simulation_kind + constants = + let open Lwt_result_syntax in + let hard_gas_limit_per_block = + constants.Constants.Parametric.hard_gas_limit_per_block in let simulation_kind = match simulation_kind with @@ -365,27 +388,100 @@ let forge (cctxt : #Protocol_client_context.full) ~chain_id ~pred_info Filter filtered_pool | Apply _ as x -> x in - (match (simulation_mode, simulation_kind) with - | Baking_state.Node, Filter operation_pool -> filter_via_node ~operation_pool - | Node, Apply {ordered_pool; payload_hash} -> - apply_via_node ~ordered_pool ~payload_hash - | Local context_index, Filter operation_pool -> - filter_with_context ~context_index ~operation_pool - | Local context_index, Apply {ordered_pool; payload_hash} -> - apply_with_context ~context_index ~ordered_pool ~payload_hash) - >>=? fun (shell_header, operations, payload_hash) -> - Baking_pow.mine - ~proof_of_work_threshold:constants.proof_of_work_threshold - shell_header - (fun proof_of_work_nonce -> - { - Block_header.payload_hash; - payload_round; - seed_nonce_hash; - proof_of_work_nonce; - liquidity_baking_toggle_vote; - }) - >>=? fun contents -> + let* shell_header, operations, payload_hash = + match (simulation_mode, simulation_kind) with + | Baking_state.Node, Filter operation_pool -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + filter_via_node + ~chain_id + ~faked_protocol_data + ~fees_config + ~hard_gas_limit_per_block + ~timestamp + ~pred_info + ~payload_round + ~operation_pool + cctxt + | Node, Apply {ordered_pool; payload_hash} -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_hash + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + apply_via_node + ~chain_id + ~faked_protocol_data + ~timestamp + ~pred_info + ~ordered_pool + ~payload_hash + cctxt + | Local context_index, Filter operation_pool -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + filter_with_context + ~chain_id + ~faked_protocol_data + ~fees_config + ~hard_gas_limit_per_block + ~user_activated_upgrades + ~timestamp + ~pred_info + ~force_apply + ~round + ~context_index + ~payload_round + ~operation_pool + cctxt + | Local context_index, Apply {ordered_pool; payload_hash} -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_hash + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + apply_with_context + ~chain_id + ~faked_protocol_data + ~user_activated_upgrades + ~timestamp + ~pred_info + ~force_apply + ~round + ~ordered_pool + ~context_index + ~payload_hash + cctxt + in + let* contents = + Baking_pow.mine + ~proof_of_work_threshold:constants.proof_of_work_threshold + shell_header + (fun proof_of_work_nonce -> + { + Block_header.payload_hash; + payload_round; + seed_nonce_hash; + proof_of_work_nonce; + liquidity_baking_toggle_vote; + }) + in let unsigned_block_header = { Block_header.shell = shell_header; diff --git a/src/proto_016_PtMumbai/lib_delegate/block_forge.mli b/src/proto_016_PtMumbai/lib_delegate/block_forge.mli index b111e27b417c4a67c69c1b2160ee019ae9620e34..77fb619a192f5a54f2b96a1204bab2d2597d0b66 100644 --- a/src/proto_016_PtMumbai/lib_delegate/block_forge.mli +++ b/src/proto_016_PtMumbai/lib_delegate/block_forge.mli @@ -40,22 +40,16 @@ type simulation_kind = type simulation_mode = Local of Context.index | Node -val forge_faked_protocol_data : - ?payload_hash:Block_payload_hash.t -> - payload_round:Round.t -> - seed_nonce_hash:Nonce_hash.t option -> - liquidity_baking_toggle_vote:Liquidity_baking.liquidity_baking_toggle_vote -> - unit -> - block_header_data - val forge : #Protocol_client_context.full -> chain_id:Chain_id.t -> pred_info:Baking_state.block_info -> timestamp:Time.Protocol.t -> + round:Round.t -> liquidity_baking_toggle_vote:Liquidity_baking.liquidity_baking_toggle_vote -> user_activated_upgrades:User_activated.upgrades -> Baking_configuration.fees_config -> + force_apply:bool -> seed_nonce_hash:Nonce_hash.t option -> payload_round:Round.t -> Baking_state.validation_mode -> diff --git a/src/proto_016_PtMumbai/lib_delegate/client_daemon.ml b/src/proto_016_PtMumbai/lib_delegate/client_daemon.ml index afc9f52d6aa775ad86805dd15e433fd01d80724b..a76a8327193894ce0dc976a242efe6850b0d0531 100644 --- a/src/proto_016_PtMumbai/lib_delegate/client_daemon.ml +++ b/src/proto_016_PtMumbai/lib_delegate/client_daemon.ml @@ -70,7 +70,7 @@ module Baker = struct let run (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?liquidity_baking_toggle_vote ?per_block_vote_file ?extra_operations - ~chain ~context_path ~keep_alive delegates = + ?force_apply ~chain ~context_path ~keep_alive delegates = let process () = Config_services.user_activated_upgrades cctxt >>=? fun user_activated_upgrades -> @@ -82,6 +82,7 @@ module Baker = struct ?liquidity_baking_toggle_vote ?per_block_vote_file ?extra_operations + ?force_apply ~context_path ~user_activated_upgrades () diff --git a/src/proto_016_PtMumbai/lib_delegate/client_daemon.mli b/src/proto_016_PtMumbai/lib_delegate/client_daemon.mli index 751db505885762e8ed46ee7c1d52947b4ca4c96a..65119e84040cc1063bd52b95adffbd61a5eb7e40 100644 --- a/src/proto_016_PtMumbai/lib_delegate/client_daemon.mli +++ b/src/proto_016_PtMumbai/lib_delegate/client_daemon.mli @@ -36,6 +36,7 @@ module Baker : sig Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote -> ?per_block_vote_file:string -> ?extra_operations:Baking_configuration.Operations_source.t -> + ?force_apply:bool -> chain:Shell_services.chain -> context_path:string -> keep_alive:bool -> diff --git a/src/proto_016_PtMumbai/lib_delegate/operation_selection.ml b/src/proto_016_PtMumbai/lib_delegate/operation_selection.ml index 9a4fbf4d0b92ed64ffd2016bea743c8cebfca75b..cc33ddbfca71e815d7d409901cb30d0db7a76b1b 100644 --- a/src/proto_016_PtMumbai/lib_delegate/operation_selection.ml +++ b/src/proto_016_PtMumbai/lib_delegate/operation_selection.ml @@ -160,8 +160,8 @@ let prioritize_managers ~hard_gas_limit_per_block ~minimal_fees (** Simulation *) type simulation_result = { - validation_result : Tezos_protocol_environment.validation_result; - block_header_metadata : block_header_metadata; + validation_result : Tezos_protocol_environment.validation_result option; + block_header_metadata : block_header_metadata option; operations : packed_operation list list; operations_hash : Operation_list_list_hash.t; } @@ -171,7 +171,10 @@ let validate_operation inc op = | Error errs -> Events.(emit invalid_operation_filtered) (Operation.hash_packed op, errs) >>= fun () -> Lwt.return_none - | Ok (resulting_state, receipt) -> ( + | Ok (resulting_state, None) -> + (* No receipt if force_apply is not set *) + Lwt.return_some resulting_state + | Ok (resulting_state, Some receipt) -> ( (* Check that the metadata are serializable/deserializable *) let encoding_result = let enc = Protocol.operation_receipt_encoding in @@ -255,9 +258,23 @@ let filter_operations_with_simulation initial_inc fees_config operations) in let inc = {inc with header = {inc.header with operations_hash}} in - Baking_simulator.finalize_construction inc - >>=? fun (validation_result, block_header_metadata) -> - return {validation_result; block_header_metadata; operations; operations_hash} + Baking_simulator.finalize_construction inc >>=? function + | Some (validation_result, block_header_metadata) -> + return + { + validation_result = Some validation_result; + block_header_metadata = Some block_header_metadata; + operations; + operations_hash; + } + | None -> + return + { + validation_result = None; + block_header_metadata = None; + operations; + operations_hash; + } let filter_valid_operations_up_to_quota_without_simulation (ops, quota) = let {Tezos_protocol_environment.max_size; max_op} = quota in diff --git a/src/proto_016_PtMumbai/lib_delegate/operation_selection.mli b/src/proto_016_PtMumbai/lib_delegate/operation_selection.mli index 78caab5c158c3d401e4c0f6d260c19a6e7621340..d83fd0dda258c5c7017567d9cabe24a2e98a714e 100644 --- a/src/proto_016_PtMumbai/lib_delegate/operation_selection.mli +++ b/src/proto_016_PtMumbai/lib_delegate/operation_selection.mli @@ -28,12 +28,21 @@ open Alpha_context open Tezos_protocol_environment type simulation_result = { - validation_result : validation_result; - block_header_metadata : Apply_results.block_metadata; + validation_result : validation_result option; + block_header_metadata : Apply_results.block_metadata option; operations : packed_operation list list; operations_hash : Operation_list_list_hash.t; } +(** [filter_operations_with_simulation incremental fees_config + ~hard_gas_limit_per_block ops] tries to validate prioritized operations (and + apply them if [incremental] has been initialised with an + [application_state]) and filter them regarding the quota of each validation + pass. Manager operations are prioritized based on a weight computed from + their fees/gas/bytes. [filter_operations_with_simulation] function returns a + [simulation_result], containing the validated operation, their resulting + [operations_hash], optional [validation_result] and [block_header_metadata] + if the operations were applied. *) val filter_operations_with_simulation : Baking_simulator.incremental -> Baking_configuration.fees_config -> @@ -41,12 +50,21 @@ val filter_operations_with_simulation : Operation_pool.Prioritized.t -> simulation_result tzresult Lwt.t +(** [filter_operations_without_simulation fees_config ~hard_gas_limit_per_block + ops] is similar to [filter_operations_with_simulation] but does not validate + (and apply) operations from [ops] and returns only the operations instead of + a [simulation_result]. + + Hypothesis: operations from [ops] have previously been validated. *) val filter_operations_without_simulation : Baking_configuration.fees_config -> hard_gas_limit_per_block:Gas.Arith.integral -> Operation_pool.Prioritized.t -> packed_operation list list +(** [filter_consensus_operations_only incremental ops] is similar to + [filter_operations_with_simulation] but only filters consensus operations + from [ops]. *) val filter_consensus_operations_only : Baking_simulator.incremental -> Operation_pool.ordered_pool -> diff --git a/src/proto_alpha/lib_delegate/baking_actions.ml b/src/proto_alpha/lib_delegate/baking_actions.ml index 444566f0e59b0b280a7f81f15d2abd1c1f2b3a2b..c03c099c50d18f77acb62607b74901b752aefb25 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.ml +++ b/src/proto_alpha/lib_delegate/baking_actions.ml @@ -306,10 +306,12 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = ~chain_id ~pred_info:predecessor ~timestamp + ~round ~seed_nonce_hash ~payload_round ~liquidity_baking_toggle_vote ~user_activated_upgrades + ~force_apply:state.global_state.config.force_apply state.global_state.config.fees simulation_mode simulation_kind diff --git a/src/proto_alpha/lib_delegate/baking_commands.ml b/src/proto_alpha/lib_delegate/baking_commands.ml index 4e84a87318dd6046201ad3c6e94511c30591aec0..8484b2191238b0dc29bbba06d88bf7b019268a48 100644 --- a/src/proto_alpha/lib_delegate/baking_commands.ml +++ b/src/proto_alpha/lib_delegate/baking_commands.ml @@ -114,6 +114,12 @@ let context_path_arg = 'preapply' RPC." string_parameter +let force_apply_switch_arg = + Tezos_clic.switch + ~long:"force-apply" + ~doc:"Force the baker to not only validate but also apply operations." + () + let endorsement_force_switch_arg = Tezos_clic.switch ~long:"force" @@ -225,11 +231,12 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list command ~group ~desc:"Forge and inject block using the delegates' rights." - (args9 + (args10 minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg minimal_timestamp_switch + force_apply_switch_arg force_switch operations_arg context_path_arg @@ -240,6 +247,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, minimal_timestamp, + force_apply, force, extra_operations, context_path, @@ -254,6 +262,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list ~minimal_timestamp ~minimal_nanotez_per_byte ~minimal_fees + ~force_apply ~force ~monitor_node_mempool:(not do_not_monitor_node_mempool) ?extra_operations @@ -279,11 +288,12 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list command ~group ~desc:"Send a Tenderbake proposal" - (args7 + (args8 minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg minimal_timestamp_switch + force_apply_switch_arg force_switch operations_arg context_path_arg) @@ -292,6 +302,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, minimal_timestamp, + force_apply, force, extra_operations, context_path ) @@ -304,6 +315,7 @@ let delegate_commands () : Protocol_client_context.full Tezos_clic.command list ~minimal_timestamp ~minimal_nanotez_per_byte ~minimal_fees + ~force_apply ~force ?extra_operations ?context_path @@ -336,11 +348,12 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = command ~group ~desc:"Launch the baker daemon." - (args9 + (args10 pidfile_arg minimal_fees_arg minimal_nanotez_per_gas_unit_arg minimal_nanotez_per_byte_arg + force_apply_switch_arg keep_alive_arg liquidity_baking_toggle_vote_arg per_block_vote_file_arg @@ -356,6 +369,7 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = minimal_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, + force_apply, keep_alive, liquidity_baking_toggle_vote, per_block_vote_file, @@ -391,6 +405,7 @@ let baker_commands () : Protocol_client_context.full Tezos_clic.command list = ?per_block_vote_file ?extra_operations ?dal_node_endpoint + ~force_apply ~chain:cctxt#chain ~context_path ~keep_alive diff --git a/src/proto_alpha/lib_delegate/baking_configuration.ml b/src/proto_alpha/lib_delegate/baking_configuration.ml index c28284d0eec744ed14d3486be5c8fea0f4b1f62e..7895474cbf265f6991b3d479be9c84d0368624c5 100644 --- a/src/proto_alpha/lib_delegate/baking_configuration.ml +++ b/src/proto_alpha/lib_delegate/baking_configuration.ml @@ -85,6 +85,7 @@ type t = { liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote; per_block_vote_file : string option; + force_apply : bool; force : bool; state_recorder : state_recorder_config; extra_operations : Operations_source.t option; @@ -114,6 +115,8 @@ let default_liquidity_baking_toggle_vote = let default_force = false +let default_force_apply = false + let default_state_recorder_config = Filesystem let default_extra_operations = None @@ -128,6 +131,7 @@ let default_config = retries_on_failure = default_retries_on_failure_config; user_activated_upgrades = default_user_activated_upgrades; liquidity_baking_toggle_vote = default_liquidity_baking_toggle_vote; + force_apply = default_force_apply; force = default_force; state_recorder = default_state_recorder_config; extra_operations = default_extra_operations; @@ -143,9 +147,9 @@ let make ?(minimal_fees = default_fees_config.minimal_fees) ?(retries_on_failure = default_retries_on_failure_config) ?(user_activated_upgrades = default_user_activated_upgrades) ?(liquidity_baking_toggle_vote = default_liquidity_baking_toggle_vote) - ?per_block_vote_file ?(force = default_force) - ?(state_recorder = default_state_recorder_config) ?extra_operations - ?dal_node_endpoint () = + ?per_block_vote_file ?(force_apply = default_force_apply) + ?(force = default_force) ?(state_recorder = default_state_recorder_config) + ?extra_operations ?dal_node_endpoint () = let fees = {minimal_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte} in @@ -162,6 +166,7 @@ let make ?(minimal_fees = default_fees_config.minimal_fees) user_activated_upgrades; liquidity_baking_toggle_vote; per_block_vote_file; + force_apply; force; state_recorder; extra_operations; @@ -232,6 +237,8 @@ let liquidity_baking_toggle_vote_config_encoding = let force_config_encoding = Data_encoding.bool +let force_apply_config_encoding = Data_encoding.bool + let state_recorder_config_encoding = let open Data_encoding in union @@ -266,6 +273,7 @@ let encoding : t Data_encoding.t = user_activated_upgrades; liquidity_baking_toggle_vote; per_block_vote_file; + force_apply; force; state_recorder; extra_operations; @@ -278,10 +286,10 @@ let encoding : t Data_encoding.t = user_activated_upgrades, liquidity_baking_toggle_vote, per_block_vote_file, + force_apply, force, - state_recorder, - extra_operations ), - dal_node_endpoint )) + state_recorder ), + (extra_operations, dal_node_endpoint) )) (fun ( ( fees, validation, nonce, @@ -289,10 +297,10 @@ let encoding : t Data_encoding.t = user_activated_upgrades, liquidity_baking_toggle_vote, per_block_vote_file, + force_apply, force, - state_recorder, - extra_operations ), - dal_node_endpoint ) -> + state_recorder ), + (extra_operations, dal_node_endpoint) ) -> { fees; validation; @@ -301,6 +309,7 @@ let encoding : t Data_encoding.t = user_activated_upgrades; liquidity_baking_toggle_vote; per_block_vote_file; + force_apply; force; state_recorder; extra_operations; @@ -319,10 +328,12 @@ let encoding : t Data_encoding.t = "liquidity_baking_toggle_vote" liquidity_baking_toggle_vote_config_encoding) (opt "per_block_vote_file" Data_encoding.string) + (req "force_apply" force_apply_config_encoding) (req "force" force_config_encoding) - (req "state_recorder" state_recorder_config_encoding) - (opt "extra_operations" Operations_source.encoding)) - (obj1 (opt "dal_node_endpoint" Tezos_rpc.Encoding.uri_encoding))) + (req "state_recorder" state_recorder_config_encoding)) + (obj2 + (opt "extra_operations" Operations_source.encoding) + (opt "dal_node_endpoint" Tezos_rpc.Encoding.uri_encoding))) let pp fmt t = let json = Data_encoding.Json.construct encoding t in diff --git a/src/proto_alpha/lib_delegate/baking_configuration.mli b/src/proto_alpha/lib_delegate/baking_configuration.mli index ac5f331b4e8f12e864868aed3d3dfe69bc93fafe..10ea2829e21dc8c26979d066e72ecf288808d822 100644 --- a/src/proto_alpha/lib_delegate/baking_configuration.mli +++ b/src/proto_alpha/lib_delegate/baking_configuration.mli @@ -60,6 +60,7 @@ type t = { liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote; per_block_vote_file : string option; + force_apply : bool; force : bool; state_recorder : state_recorder_config; extra_operations : Operations_source.t option; @@ -79,6 +80,8 @@ val default_user_activated_upgrades : (int32 * Protocol_hash.t) list val default_liquidity_baking_toggle_vote : Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote +val default_force_apply : bool + val default_force : bool val default_state_recorder_config : state_recorder_config @@ -100,6 +103,7 @@ val make : ?liquidity_baking_toggle_vote: Protocol.Alpha_context.Liquidity_baking.liquidity_baking_toggle_vote -> ?per_block_vote_file:string -> + ?force_apply:bool -> ?force:bool -> ?state_recorder:state_recorder_config -> ?extra_operations:Operations_source.t -> diff --git a/src/proto_alpha/lib_delegate/baking_lib.ml b/src/proto_alpha/lib_delegate/baking_lib.ml index dbd7128f05b785be06c7c98504b8aeba8297f45f..c2b68be694902210b73a0ad5239683b43e7da36e 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.ml +++ b/src/proto_alpha/lib_delegate/baking_lib.ml @@ -279,7 +279,7 @@ let endorsement_quorum state = - Yes :: repropose block with right payload and preendorsements for current round - No :: repropose fresh block for current round *) let propose (cctxt : Protocol_client_context.full) ?minimal_fees - ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force + ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force_apply ?force ?(minimal_timestamp = false) ?extra_operations ?context_path delegates = let open Lwt_result_syntax in let* _block_stream, current_proposal = get_current_proposal cctxt in @@ -289,6 +289,7 @@ let propose (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?context_path + ?force_apply ?force ?extra_operations () @@ -500,7 +501,7 @@ let baking_minimal_timestamp state = return_unit let bake (cctxt : Protocol_client_context.full) ?minimal_fees - ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force + ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?force_apply ?force ?(minimal_timestamp = false) ?extra_operations ?(monitor_node_mempool = true) ?context_path ?dal_node_endpoint delegates = let open Lwt_result_syntax in @@ -510,6 +511,7 @@ let bake (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?context_path + ?force_apply ?force ?extra_operations ?dal_node_endpoint diff --git a/src/proto_alpha/lib_delegate/baking_lib.mli b/src/proto_alpha/lib_delegate/baking_lib.mli index b58c2fc9b161512011fd46d86808d4ca4c055f96..8cfb36ced83d406e13e7de81f21148e236760b07 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.mli +++ b/src/proto_alpha/lib_delegate/baking_lib.mli @@ -32,6 +32,7 @@ val bake : ?minimal_fees:Tez.t -> ?minimal_nanotez_per_gas_unit:Q.t -> ?minimal_nanotez_per_byte:Q.t -> + ?force_apply:bool -> ?force:bool -> ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> @@ -58,6 +59,7 @@ val propose : ?minimal_fees:Tez.t -> ?minimal_nanotez_per_gas_unit:Q.t -> ?minimal_nanotez_per_byte:Q.t -> + ?force_apply:bool -> ?force:bool -> ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> diff --git a/src/proto_alpha/lib_delegate/baking_simulator.ml b/src/proto_alpha/lib_delegate/baking_simulator.ml index 5ae61e3f47c91eb8cf3568c4c7bd85fbd32c9494..74f2b7519cfb326db4951a70667ad717e5bf49e1 100644 --- a/src/proto_alpha/lib_delegate/baking_simulator.ml +++ b/src/proto_alpha/lib_delegate/baking_simulator.ml @@ -54,7 +54,7 @@ let () = type incremental = { predecessor : Baking_state.block_info; context : Tezos_protocol_environment.Context.t; - state : Protocol.validation_state * Protocol.application_state; + state : Protocol.validation_state * Protocol.application_state option; rev_operations : Operation.packed list; header : Tezos_base.Block_header.shell_header; } @@ -76,7 +76,7 @@ let check_context_consistency (abstract_index : Abstract_context_index.t) | true -> return_unit | false -> fail Invalid_context)) -let begin_construction ~timestamp ~protocol_data +let begin_construction ~timestamp ~protocol_data ~force_apply (abstract_index : Abstract_context_index.t) predecessor chain_id = protect (fun () -> let { @@ -119,12 +119,15 @@ let begin_construction ~timestamp ~protocol_data ~predecessor:pred_shell ~cache:`Lazy >>=? fun validation_state -> - Lifted_protocol.begin_application - context - chain_id - mode - ~predecessor:pred_shell - ~cache:`Lazy + (if force_apply then + Lifted_protocol.begin_application + context + chain_id + mode + ~predecessor:pred_shell + ~cache:`Lazy + >>=? return_some + else return_none) >>=? fun application_state -> let state = (validation_state, application_state) in return {predecessor; context; state; rev_operations = []; header}) @@ -140,10 +143,23 @@ let add_operation st (op : Operation.packed) = let validation_state, application_state = st.state in let oph = Operation.hash_packed op in let** validation_state = - Protocol.validate_operation validation_state oph op + Protocol.validate_operation + ~check_signature:false + (* We assume that the operation has already been validated in the + node, therefore the signature has already been checked, but we + still need to validate it again because the context may be + different. *) + validation_state + oph + op in let** application_state, receipt = - Protocol.apply_operation application_state oph op + match application_state with + | Some application_state -> + Protocol.apply_operation application_state oph op + >>=? fun (application_state, receipt) -> + return (Some application_state, Some receipt) + | None -> return (None, None) in let state = (validation_state, application_state) in return ({st with state; rev_operations = op :: st.rev_operations}, receipt)) @@ -153,6 +169,10 @@ let finalize_construction inc = let validation_state, application_state = inc.state in let** () = Protocol.finalize_validation validation_state in let** result = - Protocol.finalize_application application_state (Some inc.header) + match application_state with + | Some application_state -> + Protocol.finalize_application application_state (Some inc.header) + >>=? return_some + | None -> return_none in return result) diff --git a/src/proto_alpha/lib_delegate/baking_simulator.mli b/src/proto_alpha/lib_delegate/baking_simulator.mli index 0d0c7ac9f4d2fee3a7036c81a7ea04ba7a581ca5..959546fe6d2178f6113776cb4d9872b4a41d5dc0 100644 --- a/src/proto_alpha/lib_delegate/baking_simulator.mli +++ b/src/proto_alpha/lib_delegate/baking_simulator.mli @@ -29,7 +29,7 @@ open Alpha_context type incremental = { predecessor : Baking_state.block_info; context : Tezos_protocol_environment.Context.t; - state : validation_state * application_state; + state : validation_state * application_state option; rev_operations : Operation.packed list; header : Tezos_base.Block_header.shell_header; } @@ -41,21 +41,40 @@ val load_context : val check_context_consistency : Abstract_context_index.t -> Context_hash.t -> unit tzresult Lwt.t +(** [begin_construction ~timestamp ~protocol_data ~force_apply abstract_context + predecessor chain_id] creates a new [incremental] value with an empty + operation list. A [context] is recovered from the [abstract_index] and the + resulting_context_hash from [predecessor]. This context is used to create a + [validation_state] and an [application_state] (if [force_apply] is set). A + partial [shell_header] is created from [predecessor] information and + [timestamp]. *) val begin_construction : timestamp:Time.Protocol.t -> protocol_data:block_header_data -> + force_apply:bool -> Abstract_context_index.t -> Baking_state.block_info -> Chain_id.t -> incremental tzresult Lwt.t +(** [add_operation incremental op] validates [op] in + [incremental.validation_state] without checking its signature. Indeed, the + operation has already been validated in the node so it has a correct + signature. We still need to validate it again because the context may be + different. [op] is also applied if [incremental] has been created with + [force_apply] set. This function returns an [incremental] with updated + operations list and [validation_state] (and [application_state]). *) val add_operation : incremental -> Operation.packed -> - (incremental * operation_receipt) tzresult Lwt.t + (incremental * operation_receipt option) tzresult Lwt.t +(** [finalize_construction incremental] calls the [finalize_validation] of the + protocol on the [validation_state] from [incremental]. If [incremental] has + been created with [force_apply] set, [finalize_application] is also called + and its results returned. *) val finalize_construction : incremental -> - (Tezos_protocol_environment.validation_result * block_header_metadata) + (Tezos_protocol_environment.validation_result * block_header_metadata) option tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/block_forge.ml b/src/proto_alpha/lib_delegate/block_forge.ml index 062306d6367d7a0c503a6dfa2f283a45716dc282..d3be206ac6b6bae99993ebae1cf6a7ce14616d43 100644 --- a/src/proto_alpha/lib_delegate/block_forge.ml +++ b/src/proto_alpha/lib_delegate/block_forge.ml @@ -40,6 +40,11 @@ type simulation_kind = type simulation_mode = Local of Context.index | Node +(* [forge_faked_protocol_data ?payload_hash ~payload_round ~seed_nonce_hash + ~liquidity_baking_toggle_vote] forges a fake [block_header_data] with + [payload_hash] ([zero] by default), [payload_round], [seed_nonce_hash], + [liquidity_baking_toggle_vote] and with an empty [proof_of_work_nonce] and a + dummy [signature]. *) let forge_faked_protocol_data ?(payload_hash = Block_payload_hash.zero) ~payload_round ~seed_nonce_hash ~liquidity_baking_toggle_vote () = Block_header. @@ -64,43 +69,37 @@ let convert_operation (op : packed_operation) : Tezos_base.Operation.t = op.protocol_data; } -(* Build the block header : mimics node prevalidation *) -let finalize_block_header shell_header timestamp validation_result - operations_hash predecessor_block_metadata_hash - predecessor_ops_metadata_hash predecessor_resulting_context = - let {Tezos_protocol_environment.context; fitness; message; _} = - validation_result +(* [finalize_block_header ~shell_header ~validation_result ~operations_hash + ~pred_info ~round ~locked_round] updates the [shell_header] that was created + with dummy fields at the beginning of the block construction. It increments + the [level] and sets the actual [operations_hash], [fitness], + [validation_passes], and [context] (the predecessor resulting context hash). + + When the operations from the block have been applied, the [fitness] is simply + retrieved from the [validation_result]. Otherwise, the [fitness] is computed + from the [round] and [locked_round] arguments. *) +let finalize_block_header ~shell_header ~validation_result ~operations_hash + ~(pred_info : Baking_state.block_info) ~round ~locked_round = + let open Lwt_result_syntax in + let* fitness = + match validation_result with + | Some {Tezos_protocol_environment.fitness; _} -> return fitness + | None -> + let*? level = + Environment.wrap_tzresult @@ Raw_level.of_int32 + @@ Int32.succ shell_header.Tezos_base.Block_header.level + in + let*? fitness = + Environment.wrap_tzresult + @@ Fitness.create + ~level + ~round + ~predecessor_round:pred_info.round + ~locked_round + in + return (Fitness.to_raw fitness) in let validation_passes = List.length Main.validation_passes in - (Context_ops.get_test_chain context >>= function - | Not_running -> return context - | Running {expiration; _} -> - if Time.Protocol.(expiration <= timestamp) then - Context_ops.add_test_chain context Not_running >>= fun context -> - return context - else return context - | Forking _ -> assert false) - >>=? fun context -> - (match predecessor_block_metadata_hash with - | Some predecessor_block_metadata_hash -> - Context_ops.add_predecessor_block_metadata_hash - context - predecessor_block_metadata_hash - | None -> Lwt.return context) - >>= fun context -> - (match predecessor_ops_metadata_hash with - | Some predecessor_ops_metadata_hash -> - Context_ops.add_predecessor_ops_metadata_hash - context - predecessor_ops_metadata_hash - | None -> Lwt.return context) - >>= fun context -> - let context = Context_ops.hash ~time:timestamp ?message context in - (* For the time being, we still fully build the block while we build - confidence to fully unplug the baker validation. The resulting - context hash is ignored as it is not necessary to craft a block. - See: https://gitlab.com/tezos/tezos/-/issues/4285 *) - ignore context ; let header = Tezos_base.Block_header. { @@ -109,7 +108,7 @@ let finalize_block_header shell_header timestamp validation_result validation_passes; operations_hash; fitness; - context = predecessor_resulting_context; + context = pred_info.resulting_context_hash; } in return header @@ -120,237 +119,261 @@ let retain_live_operations_only ~live_blocks operation_pool = Block_hash.Set.mem shell.branch live_blocks) operation_pool -let forge (cctxt : #Protocol_client_context.full) ~chain_id ~pred_info - ~timestamp ~liquidity_baking_toggle_vote ~user_activated_upgrades - fees_config ~seed_nonce_hash ~payload_round simulation_mode simulation_kind - constants = - let predecessor_block = (pred_info : Baking_state.block_info) in - let hard_gas_limit_per_block = - constants.Constants.Parametric.hard_gas_limit_per_block - in +(* [check_protocol_changed] checks whether the protocol will change with the current + block. This function returns true if the block is the last of an [adoption] + period. It can also return true if an [user_activated_upgrades] is given. *) +let check_protocol_changed ~user_activated_upgrades ~level + ~(validation_result : Tezos_protocol_environment.validation_result option) + ~(incremental : Baking_simulator.incremental) = + let open Lwt_result_syntax in + match + Tezos_base.Block_header.get_forced_protocol_upgrade + ~user_activated_upgrades + ~level + with + | None -> ( + match validation_result with + | None -> ( + let context = Validate.get_initial_ctxt (fst incremental.state) in + let* voting_period = + Lwt.map + Environment.wrap_tzresult + (Voting_period.get_current context) + in + match voting_period.kind with + | Voting_period.Proposal | Exploration | Cooldown | Promotion -> + return false + | Adoption -> + Lwt.map + Environment.wrap_tzresult + (Voting_period.is_last_block context)) + | Some validation_result -> + let*! next_protocol = + Context_ops.get_protocol validation_result.context + in + return Protocol_hash.(Protocol.hash <> next_protocol)) + | Some next_protocol -> return Protocol_hash.(Protocol.hash <> next_protocol) + +(* [filter_via_node] filters operations using + {!Operation_selection.filter_operations_without_simulation} and then applies + them in a block via {!Node_rpc.preapply_block}. [filter_via_node] returns a + [shell_header], the list of operations that have been applied in the block + and the [payload_hash] corresponding to these operations. *) +let filter_via_node ~chain_id ~fees_config ~hard_gas_limit_per_block + ~faked_protocol_data ~timestamp ~(pred_info : Baking_state.block_info) + ~payload_round ~operation_pool cctxt = + let open Lwt_result_syntax in let chain = `Hash chain_id in - let check_protocol_changed - ~(validation_result : Tezos_protocol_environment.validation_result) = - Context_ops.get_protocol validation_result.context >>= fun next_protocol -> - let next_protocol = - match - Tezos_base.Block_header.get_forced_protocol_upgrade - ~user_activated_upgrades - ~level:(Int32.succ predecessor_block.shell.level) - with - | None -> next_protocol - | Some hash -> hash - in - return Protocol_hash.(Protocol.hash <> next_protocol) + let filtered_operations = + Operation_selection.filter_operations_without_simulation + fees_config + ~hard_gas_limit_per_block + operation_pool in - let filter_via_node ~operation_pool = - let filtered_operations = - Operation_selection.filter_operations_without_simulation - fees_config - ~hard_gas_limit_per_block - operation_pool - in - let faked_protocol_data = - forge_faked_protocol_data - ~payload_round - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - () - in + let* shell_header, preapply_result = Node_rpc.preapply_block cctxt ~chain - ~head:predecessor_block.hash + ~head:pred_info.hash ~timestamp ~protocol_data:faked_protocol_data filtered_operations - >>=? fun (shell_header, preapply_result) -> - (* only retain valid operations *) - let operations = - List.map (fun l -> List.map snd l.Preapply_result.applied) preapply_result - in - let payload_hash = - let operation_hashes = - Stdlib.List.tl operations |> List.flatten - |> List.map Tezos_base.Operation.hash - in - Block_payload.hash - ~predecessor_hash:shell_header.predecessor - ~payload_round - operation_hashes - in - return (shell_header, operations, payload_hash) in - let filter_with_context ~context_index ~operation_pool = - let faked_protocol_data = - forge_faked_protocol_data - ~payload_round - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - () + (* only retain valid operations *) + let operations = + List.map (fun l -> List.map snd l.Preapply_result.applied) preapply_result + in + let payload_hash = + let operation_hashes = + Stdlib.List.tl operations |> List.flatten + |> List.map Tezos_base.Operation.hash in + Block_payload.hash + ~predecessor_hash:shell_header.predecessor + ~payload_round + operation_hashes + in + return (shell_header, operations, payload_hash) + +(* [filter_with_context] filters operations using a local context via + {!Operation_selection.filter_operations_with_simulation} and a fresh state + from {!Baking_simulator.begin_construction}. [finalize_block_header] is then + called and a [shell_header], the list of operations and the corresponding + [payload_hash] are returned. If the block is a transition block, + [filter_via_node] is called to return these values. *) +let filter_with_context ~chain_id ~fees_config ~hard_gas_limit_per_block + ~faked_protocol_data ~user_activated_upgrades ~timestamp + ~(pred_info : Baking_state.block_info) ~force_apply ~round ~context_index + ~payload_round ~operation_pool cctxt = + let open Lwt_result_syntax in + let* incremental = Baking_simulator.begin_construction ~timestamp ~protocol_data:faked_protocol_data + ~force_apply context_index - predecessor_block + pred_info chain_id - >>=? fun incremental -> + in + let* {Operation_selection.operations; validation_result; operations_hash; _} = Operation_selection.filter_operations_with_simulation incremental fees_config ~hard_gas_limit_per_block operation_pool - >>=? fun { - Operation_selection.operations; - validation_result; - operations_hash; - _; - } -> - check_protocol_changed ~validation_result >>=? fun changed -> - if changed then - (* Fallback to processing via node, which knows both old and new protocol. *) - filter_via_node ~operation_pool - else - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.metadata_hash - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_block_metadata_hash -> - return (Some pred_block_metadata_hash)) - >>=? fun pred_block_metadata_hash -> - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.Operation_metadata_hashes.root - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_op_metadata_hash -> return (Some pred_op_metadata_hash)) - >>=? fun pred_op_metadata_hash -> + in + let* changed = + check_protocol_changed + ~level:(Int32.succ pred_info.shell.level) + ~user_activated_upgrades + ~validation_result + ~incremental + in + if changed then + (* Fallback to processing via node, which knows both old and new protocol. *) + filter_via_node + ~chain_id + ~fees_config + ~hard_gas_limit_per_block + ~faked_protocol_data + ~timestamp + ~pred_info + ~payload_round + ~operation_pool + cctxt + else + let* shell_header = finalize_block_header - incremental.header - timestamp - validation_result - operations_hash - pred_block_metadata_hash - pred_op_metadata_hash - predecessor_block.resulting_context_hash - >>=? fun shell_header -> - let operations = List.map (List.map convert_operation) operations in - let payload_hash = - let operation_hashes = - Stdlib.List.tl operations |> List.flatten - |> List.map Tezos_base.Operation.hash - in - Block_payload.hash - ~predecessor_hash:shell_header.predecessor - ~payload_round - operation_hashes + ~shell_header:incremental.header + ~validation_result + ~operations_hash + ~pred_info + ~round + ~locked_round:None + in + let operations = List.map (List.map convert_operation) operations in + let payload_hash = + let operation_hashes = + Stdlib.List.tl operations |> List.flatten + |> List.map Tezos_base.Operation.hash in - return (shell_header, operations, payload_hash) - in - let apply_via_node ~ordered_pool ~payload_hash = - let operations = Operation_pool.ordered_to_list_list ordered_pool in - let faked_protocol_data = - forge_faked_protocol_data - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - ~payload_hash + Block_payload.hash + ~predecessor_hash:shell_header.predecessor ~payload_round - () + operation_hashes in + return (shell_header, operations, payload_hash) + +(* [apply_via_node] applies already filtered and validated operations in a block + via {!Node_rpc.preapply_block}. A [shell_header] is recovered from this call + and returned alongside of the list of operations and the payload_hash. *) +let apply_via_node ~chain_id ~faked_protocol_data ~timestamp + ~(pred_info : Baking_state.block_info) ~ordered_pool ~payload_hash cctxt = + let open Lwt_result_syntax in + let chain = `Hash chain_id in + let operations = Operation_pool.ordered_to_list_list ordered_pool in + let* shell_header, _preapply_result = Node_rpc.preapply_block cctxt ~chain - ~head:predecessor_block.hash + ~head:pred_info.hash ~timestamp ~protocol_data:faked_protocol_data operations - >>=? fun (shell_header, _preapply_result) -> - let operations = List.map (List.map convert_operation) operations in - return (shell_header, operations, payload_hash) in - let apply_with_context ~context_index ~ordered_pool ~payload_hash = - let faked_protocol_data = - forge_faked_protocol_data - ~seed_nonce_hash - ~liquidity_baking_toggle_vote - ~payload_hash - ~payload_round - () - in - Shell_services.Chain.chain_id cctxt ~chain () >>=? fun chain_id -> + let operations = List.map (List.map convert_operation) operations in + return (shell_header, operations, payload_hash) + +(* [apply_with_context] is similar to [filter_with_context] but filters + consensus operations only from an [ordered_pool] via + {!Operation_selection.filter_consensus_operations_only}. *) +let apply_with_context ~chain_id ~faked_protocol_data ~user_activated_upgrades + ~timestamp ~(pred_info : Baking_state.block_info) ~force_apply ~round + ~ordered_pool ~context_index ~payload_hash cctxt = + let open Lwt_result_syntax in + let* incremental = Baking_simulator.begin_construction ~timestamp ~protocol_data:faked_protocol_data + ~force_apply context_index - predecessor_block + pred_info chain_id - >>=? fun incremental -> - (* We still need to filter endorsements. Two endorsements could be - referring to the same slot. *) + in + (* We still need to filter endorsements. Two endorsements could be + referring to the same slot. *) + let* incremental, ordered_pool = Operation_selection.filter_consensus_operations_only incremental ordered_pool - >>=? fun (incremental, ordered_pool) -> - let operations = Operation_pool.ordered_to_list_list ordered_pool in - let operations_hash = - Operation_list_list_hash.compute - (List.map - (fun sl -> - Operation_list_hash.compute (List.map Operation.hash_packed sl)) - operations) - in - (* We need to compute the final [operations_hash] before - finalizing the block because it will be used in the cache's nonce. *) - let incremental = - {incremental with header = {incremental.header with operations_hash}} + in + let operations = Operation_pool.ordered_to_list_list ordered_pool in + let operations_hash = + Operation_list_list_hash.compute + (List.map + (fun sl -> + Operation_list_hash.compute (List.map Operation.hash_packed sl)) + operations) + in + (* We need to compute the final [operations_hash] before + finalizing the block because it will be used in the cache's nonce. *) + let incremental = + {incremental with header = {incremental.header with operations_hash}} + in + let* validation_result = Baking_simulator.finalize_construction incremental in + let validation_result = Option.map fst validation_result in + let* changed = + check_protocol_changed + ~level:(Int32.succ pred_info.shell.level) + ~user_activated_upgrades + ~validation_result + ~incremental + in + if changed then + (* Fallback to processing via node, which knows both old and new protocol. *) + apply_via_node + ~chain_id + ~faked_protocol_data + ~timestamp + ~pred_info + ~ordered_pool + ~payload_hash + cctxt + else + let locked_round_when_no_validation_result = + (* [locked_round] will not be used in [finalize_block_header] if there is + a [validation_result] *) + if Option.is_some validation_result then None + else + List.find_map + (fun {protocol_data = Operation_data protocol_data; _} -> + match protocol_data.contents with + | Single (Preendorsement {round; _}) -> Some round + | _ -> None) + (Option.value (List.hd operations) ~default:[]) in - Baking_simulator.finalize_construction incremental - >>=? fun (validation_result, _) -> - check_protocol_changed ~validation_result >>=? fun changed -> - if changed then - (* Fallback to processing via node, which knows both old and new protocol. *) - apply_via_node ~ordered_pool ~payload_hash - else - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.metadata_hash - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_block_metadata_hash -> - return (Some pred_block_metadata_hash)) - >>=? fun pred_block_metadata_hash -> - protect - ~on_error:(fun _ -> return_none) - (fun () -> - Shell_services.Blocks.Operation_metadata_hashes.root - cctxt - ~block:(`Hash (predecessor_block.hash, 0)) - ~chain - () - >>=? fun pred_op_metadata_hash -> return (Some pred_op_metadata_hash)) - >>=? fun pred_op_metadata_hash -> + let* shell_header = finalize_block_header - incremental.header - timestamp - validation_result - operations_hash - pred_block_metadata_hash - pred_op_metadata_hash - predecessor_block.resulting_context_hash - >>=? fun shell_header -> - let operations = List.map (List.map convert_operation) operations in - return (shell_header, operations, payload_hash) + ~shell_header:incremental.header + ~validation_result + ~operations_hash + ~pred_info + ~round + ~locked_round:locked_round_when_no_validation_result + in + let operations = List.map (List.map convert_operation) operations in + return (shell_header, operations, payload_hash) + +(* [forge] a new [unsigned_block] in accordance with [simulation_kind] and + [simulation_mode] *) +let forge (cctxt : #Protocol_client_context.full) ~chain_id + ~(pred_info : Baking_state.block_info) ~timestamp ~round + ~liquidity_baking_toggle_vote ~user_activated_upgrades fees_config + ~force_apply ~seed_nonce_hash ~payload_round simulation_mode simulation_kind + constants = + let open Lwt_result_syntax in + let hard_gas_limit_per_block = + constants.Constants.Parametric.hard_gas_limit_per_block in let simulation_kind = match simulation_kind with @@ -365,27 +388,100 @@ let forge (cctxt : #Protocol_client_context.full) ~chain_id ~pred_info Filter filtered_pool | Apply _ as x -> x in - (match (simulation_mode, simulation_kind) with - | Baking_state.Node, Filter operation_pool -> filter_via_node ~operation_pool - | Node, Apply {ordered_pool; payload_hash} -> - apply_via_node ~ordered_pool ~payload_hash - | Local context_index, Filter operation_pool -> - filter_with_context ~context_index ~operation_pool - | Local context_index, Apply {ordered_pool; payload_hash} -> - apply_with_context ~context_index ~ordered_pool ~payload_hash) - >>=? fun (shell_header, operations, payload_hash) -> - Baking_pow.mine - ~proof_of_work_threshold:constants.proof_of_work_threshold - shell_header - (fun proof_of_work_nonce -> - { - Block_header.payload_hash; - payload_round; - seed_nonce_hash; - proof_of_work_nonce; - liquidity_baking_toggle_vote; - }) - >>=? fun contents -> + let* shell_header, operations, payload_hash = + match (simulation_mode, simulation_kind) with + | Baking_state.Node, Filter operation_pool -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + filter_via_node + ~chain_id + ~faked_protocol_data + ~fees_config + ~hard_gas_limit_per_block + ~timestamp + ~pred_info + ~payload_round + ~operation_pool + cctxt + | Node, Apply {ordered_pool; payload_hash} -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_hash + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + apply_via_node + ~chain_id + ~faked_protocol_data + ~timestamp + ~pred_info + ~ordered_pool + ~payload_hash + cctxt + | Local context_index, Filter operation_pool -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + filter_with_context + ~chain_id + ~faked_protocol_data + ~fees_config + ~hard_gas_limit_per_block + ~user_activated_upgrades + ~timestamp + ~pred_info + ~force_apply + ~round + ~context_index + ~payload_round + ~operation_pool + cctxt + | Local context_index, Apply {ordered_pool; payload_hash} -> + let faked_protocol_data = + forge_faked_protocol_data + ~payload_hash + ~payload_round + ~seed_nonce_hash + ~liquidity_baking_toggle_vote + () + in + apply_with_context + ~chain_id + ~faked_protocol_data + ~user_activated_upgrades + ~timestamp + ~pred_info + ~force_apply + ~round + ~ordered_pool + ~context_index + ~payload_hash + cctxt + in + let* contents = + Baking_pow.mine + ~proof_of_work_threshold:constants.proof_of_work_threshold + shell_header + (fun proof_of_work_nonce -> + { + Block_header.payload_hash; + payload_round; + seed_nonce_hash; + proof_of_work_nonce; + liquidity_baking_toggle_vote; + }) + in let unsigned_block_header = { Block_header.shell = shell_header; diff --git a/src/proto_alpha/lib_delegate/block_forge.mli b/src/proto_alpha/lib_delegate/block_forge.mli index b111e27b417c4a67c69c1b2160ee019ae9620e34..77fb619a192f5a54f2b96a1204bab2d2597d0b66 100644 --- a/src/proto_alpha/lib_delegate/block_forge.mli +++ b/src/proto_alpha/lib_delegate/block_forge.mli @@ -40,22 +40,16 @@ type simulation_kind = type simulation_mode = Local of Context.index | Node -val forge_faked_protocol_data : - ?payload_hash:Block_payload_hash.t -> - payload_round:Round.t -> - seed_nonce_hash:Nonce_hash.t option -> - liquidity_baking_toggle_vote:Liquidity_baking.liquidity_baking_toggle_vote -> - unit -> - block_header_data - val forge : #Protocol_client_context.full -> chain_id:Chain_id.t -> pred_info:Baking_state.block_info -> timestamp:Time.Protocol.t -> + round:Round.t -> liquidity_baking_toggle_vote:Liquidity_baking.liquidity_baking_toggle_vote -> user_activated_upgrades:User_activated.upgrades -> Baking_configuration.fees_config -> + force_apply:bool -> seed_nonce_hash:Nonce_hash.t option -> payload_round:Round.t -> Baking_state.validation_mode -> diff --git a/src/proto_alpha/lib_delegate/client_daemon.ml b/src/proto_alpha/lib_delegate/client_daemon.ml index 0afbd46b8d4e0d49192fcf1d96c17bab96aca482..e5eec8251438e06a22fec6a453c123a9d5e8657c 100644 --- a/src/proto_alpha/lib_delegate/client_daemon.ml +++ b/src/proto_alpha/lib_delegate/client_daemon.ml @@ -70,7 +70,8 @@ module Baker = struct let run (cctxt : Protocol_client_context.full) ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?liquidity_baking_toggle_vote ?per_block_vote_file ?extra_operations - ?dal_node_endpoint ~chain ~context_path ~keep_alive delegates = + ?dal_node_endpoint ?force_apply ~chain ~context_path ~keep_alive delegates + = let process () = Config_services.user_activated_upgrades cctxt >>=? fun user_activated_upgrades -> @@ -83,6 +84,7 @@ module Baker = struct ?per_block_vote_file ?extra_operations ?dal_node_endpoint + ?force_apply ~context_path ~user_activated_upgrades () diff --git a/src/proto_alpha/lib_delegate/client_daemon.mli b/src/proto_alpha/lib_delegate/client_daemon.mli index d8a23802d32b92e1b643f315bdce1ced62be83b4..aabc7620886c8ef72b62c8f57e4a9752cbcccc3a 100644 --- a/src/proto_alpha/lib_delegate/client_daemon.mli +++ b/src/proto_alpha/lib_delegate/client_daemon.mli @@ -37,6 +37,7 @@ module Baker : sig ?per_block_vote_file:string -> ?extra_operations:Baking_configuration.Operations_source.t -> ?dal_node_endpoint:Uri.t -> + ?force_apply:bool -> chain:Shell_services.chain -> context_path:string -> keep_alive:bool -> diff --git a/src/proto_alpha/lib_delegate/operation_selection.ml b/src/proto_alpha/lib_delegate/operation_selection.ml index 9a4fbf4d0b92ed64ffd2016bea743c8cebfca75b..cc33ddbfca71e815d7d409901cb30d0db7a76b1b 100644 --- a/src/proto_alpha/lib_delegate/operation_selection.ml +++ b/src/proto_alpha/lib_delegate/operation_selection.ml @@ -160,8 +160,8 @@ let prioritize_managers ~hard_gas_limit_per_block ~minimal_fees (** Simulation *) type simulation_result = { - validation_result : Tezos_protocol_environment.validation_result; - block_header_metadata : block_header_metadata; + validation_result : Tezos_protocol_environment.validation_result option; + block_header_metadata : block_header_metadata option; operations : packed_operation list list; operations_hash : Operation_list_list_hash.t; } @@ -171,7 +171,10 @@ let validate_operation inc op = | Error errs -> Events.(emit invalid_operation_filtered) (Operation.hash_packed op, errs) >>= fun () -> Lwt.return_none - | Ok (resulting_state, receipt) -> ( + | Ok (resulting_state, None) -> + (* No receipt if force_apply is not set *) + Lwt.return_some resulting_state + | Ok (resulting_state, Some receipt) -> ( (* Check that the metadata are serializable/deserializable *) let encoding_result = let enc = Protocol.operation_receipt_encoding in @@ -255,9 +258,23 @@ let filter_operations_with_simulation initial_inc fees_config operations) in let inc = {inc with header = {inc.header with operations_hash}} in - Baking_simulator.finalize_construction inc - >>=? fun (validation_result, block_header_metadata) -> - return {validation_result; block_header_metadata; operations; operations_hash} + Baking_simulator.finalize_construction inc >>=? function + | Some (validation_result, block_header_metadata) -> + return + { + validation_result = Some validation_result; + block_header_metadata = Some block_header_metadata; + operations; + operations_hash; + } + | None -> + return + { + validation_result = None; + block_header_metadata = None; + operations; + operations_hash; + } let filter_valid_operations_up_to_quota_without_simulation (ops, quota) = let {Tezos_protocol_environment.max_size; max_op} = quota in diff --git a/src/proto_alpha/lib_delegate/operation_selection.mli b/src/proto_alpha/lib_delegate/operation_selection.mli index 78caab5c158c3d401e4c0f6d260c19a6e7621340..d83fd0dda258c5c7017567d9cabe24a2e98a714e 100644 --- a/src/proto_alpha/lib_delegate/operation_selection.mli +++ b/src/proto_alpha/lib_delegate/operation_selection.mli @@ -28,12 +28,21 @@ open Alpha_context open Tezos_protocol_environment type simulation_result = { - validation_result : validation_result; - block_header_metadata : Apply_results.block_metadata; + validation_result : validation_result option; + block_header_metadata : Apply_results.block_metadata option; operations : packed_operation list list; operations_hash : Operation_list_list_hash.t; } +(** [filter_operations_with_simulation incremental fees_config + ~hard_gas_limit_per_block ops] tries to validate prioritized operations (and + apply them if [incremental] has been initialised with an + [application_state]) and filter them regarding the quota of each validation + pass. Manager operations are prioritized based on a weight computed from + their fees/gas/bytes. [filter_operations_with_simulation] function returns a + [simulation_result], containing the validated operation, their resulting + [operations_hash], optional [validation_result] and [block_header_metadata] + if the operations were applied. *) val filter_operations_with_simulation : Baking_simulator.incremental -> Baking_configuration.fees_config -> @@ -41,12 +50,21 @@ val filter_operations_with_simulation : Operation_pool.Prioritized.t -> simulation_result tzresult Lwt.t +(** [filter_operations_without_simulation fees_config ~hard_gas_limit_per_block + ops] is similar to [filter_operations_with_simulation] but does not validate + (and apply) operations from [ops] and returns only the operations instead of + a [simulation_result]. + + Hypothesis: operations from [ops] have previously been validated. *) val filter_operations_without_simulation : Baking_configuration.fees_config -> hard_gas_limit_per_block:Gas.Arith.integral -> Operation_pool.Prioritized.t -> packed_operation list list +(** [filter_consensus_operations_only incremental ops] is similar to + [filter_operations_with_simulation] but only filters consensus operations + from [ops]. *) val filter_consensus_operations_only : Baking_simulator.incremental -> Operation_pool.ordered_pool -> diff --git a/tezt/lib_tezos/baker.ml b/tezt/lib_tezos/baker.ml index aced38b54a471f22740b0264e77389dbc37a5b5b..027dc225af34225cb479a970f189e81938c38924 100644 --- a/tezt/lib_tezos/baker.ml +++ b/tezt/lib_tezos/baker.ml @@ -46,6 +46,7 @@ module Parameters = struct mutable pending_ready : unit option Lwt.u list; votefile : string option; liquidity_baking_toggle_vote : liquidity_baking_vote option; + force_apply : bool; operations_pool : string option; dal_node : Dal_node.t option; } @@ -90,8 +91,8 @@ let liquidity_baking_votefile ?path vote = votefile let create ~protocol ?name ?color ?event_pipe ?runner ?(delegates = []) - ?votefile ?(liquidity_baking_toggle_vote = Some Pass) ?operations_pool - ?dal_node node client = + ?votefile ?(liquidity_baking_toggle_vote = Some Pass) ?(force_apply = false) + ?operations_pool ?dal_node node client = let baker = create ~path:(Protocol.baker protocol) @@ -108,6 +109,7 @@ let create ~protocol ?name ?color ?event_pipe ?runner ?(delegates = []) pending_ready = []; votefile; liquidity_baking_toggle_vote; + force_apply; operations_pool; dal_node; } @@ -135,6 +137,9 @@ let run ?event_level ?event_sections_levels (baker : t) = liquidity_baking_vote_to_string baker.persistent_state.liquidity_baking_toggle_vote in + let force_apply = + Cli_arg.optional_switch "force-apply" baker.persistent_state.force_apply + in let operations_pool = Cli_arg.optional_arg "operations-pool" @@ -163,7 +168,7 @@ let run ?event_level ?event_sections_levels (baker : t) = "node"; Node.data_dir node; ] - @ liquidity_baking_toggle_vote @ votefile @ operations_pool + @ liquidity_baking_toggle_vote @ votefile @ force_apply @ operations_pool @ dal_node_endpoint @ delegates in let on_terminate _ = @@ -197,8 +202,8 @@ let wait_for_ready baker = check_event baker "Baker started." promise let init ~protocol ?name ?color ?event_pipe ?runner ?event_sections_levels - ?(delegates = []) ?votefile ?liquidity_baking_toggle_vote ?operations_pool - ?dal_node node client = + ?(delegates = []) ?votefile ?liquidity_baking_toggle_vote ?force_apply + ?operations_pool ?dal_node node client = let* () = Node.wait_for_ready node in let baker = create @@ -209,6 +214,7 @@ let init ~protocol ?name ?color ?event_pipe ?runner ?event_sections_levels ?runner ?votefile ?liquidity_baking_toggle_vote + ?force_apply ?operations_pool ?dal_node ~delegates diff --git a/tezt/lib_tezos/baker.mli b/tezt/lib_tezos/baker.mli index 14274f76c47f63372256592f7119abe7de33dce8..f29da85b5b91a34feff1a442418ab3d48a57c44f 100644 --- a/tezt/lib_tezos/baker.mli +++ b/tezt/lib_tezos/baker.mli @@ -119,8 +119,8 @@ val liquidity_baking_votefile : ?path:string -> liquidity_baking_vote -> string is not passed. If it is [Some x] then [--liquidity-baking-toggle-vote x] is passed. The default value is [Some Pass]. - [operations_pool] is passed to the baker daemon through the flag - [--operations-pool]. + [operations_pool] and [force_apply] are passed to the baker daemon through + the flag [--operations-pool] and [--force_apply]. If [dal_node] is specified, then it is the DAL node that the baker queries in order to determine the attestations it sends to the L1 node. A @@ -134,6 +134,7 @@ val create : ?delegates:string list -> ?votefile:string -> ?liquidity_baking_toggle_vote:liquidity_baking_vote option -> + ?force_apply:bool -> ?operations_pool:string -> ?dal_node:Dal_node.t -> Node.t -> @@ -170,10 +171,10 @@ val create : baker. This defaults to the empty list, which is a shortcut for "every known account". - [votefile], [liquidity_baking_toggle_vote] respectively + [votefile], [liquidity_baking_toggle_vote], [force_apply] respectively [operations_pool] are passed to the baker daemon through the flags - [--votefile], [--liquidity-baking-toggle-vote] respectively - [--operations-pool]. + [--votefile], [--liquidity-baking-toggle-vote], [--should-apply] + respectively [--operations-pool]. If [dal_node] is specified, then it is the DAL node that the baker queries in order to determine the attestations it sends to the L1 node. A @@ -188,6 +189,7 @@ val init : ?delegates:string list -> ?votefile:string -> ?liquidity_baking_toggle_vote:liquidity_baking_vote option -> + ?force_apply:bool -> ?operations_pool:string -> ?dal_node:Dal_node.t -> Node.t -> diff --git a/tezt/tests/baker_test.ml b/tezt/tests/baker_test.ml index 63c01c506d9d2f30dabc931c38263a4be17e1cef..a2d2f8a19418a4372d526c4021b4da84b46d0ccb 100644 --- a/tezt/tests/baker_test.ml +++ b/tezt/tests/baker_test.ml @@ -30,7 +30,7 @@ Subject: Run the baker while performing a lot of transfers *) -let baker_test protocol ~keys = +let baker_test ?force_apply protocol ~keys = let* parameter_file = Protocol.write_parameter_file ~bootstrap_accounts:(List.map (fun k -> (k, None)) keys) @@ -48,7 +48,7 @@ let baker_test protocol ~keys = in let level_2_promise = Node.wait_for_level node 2 in let level_3_promise = Node.wait_for_level node 3 in - let* baker = Baker.init ~protocol node client in + let* baker = Baker.init ?force_apply ~protocol node client in Log.info "Wait for new head." ; Baker.log_events baker ; let* _ = level_2_promise in @@ -80,6 +80,23 @@ let baker_stresstest = let* () = Client.stresstest ~tps:25 ~transfers:100 client in Lwt.return_unit +(* Force the baker to apply operations after validating them *) +let baker_stresstest_apply = + Protocol.register_test + ~__FILE__ + ~supports:Protocol.(From_protocol (number Mumbai)) + ~title:"baker stresstest with forced application" + ~tags:["node"; "baker"; "stresstest"; "apply"] + @@ fun protocol -> + let* node, client = + Client.init_with_protocol `Client ~protocol () ~timestamp:Now + in + let* _ = Baker.init ~force_apply:true ~protocol node client in + let* _ = Node.wait_for_level node 3 in + (* Use a large tps, to have failing operations too *) + let* () = Client.stresstest ~tps:25 ~transfers:100 client in + unit + let baker_bls_test = Protocol.register_test ~__FILE__ @@ -124,4 +141,5 @@ let baker_bls_test = let register ~protocols = baker_simple_test protocols ; baker_stresstest protocols ; + baker_stresstest_apply protocols ; baker_bls_test protocols