From 10b6ae27e34e6b437458d1fdc8f6c027c6b11096 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Thu, 2 May 2024 15:53:15 +0200 Subject: [PATCH 1/3] EVM/Node: refactor private services to take a sequencer rpc module --- etherlink/bin_node/lib_dev/sequencer.ml | 14 +++++++------ etherlink/bin_node/lib_dev/services.ml | 21 ++++++++++++------- .../lib_dev/threshold_encryption_sequencer.ml | 13 +++++++----- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/etherlink/bin_node/lib_dev/sequencer.ml b/etherlink/bin_node/lib_dev/sequencer.ml index 9e8aaa86fc0e..43ae46a11309 100644 --- a/etherlink/bin_node/lib_dev/sequencer.ml +++ b/etherlink/bin_node/lib_dev/sequencer.ml @@ -252,13 +252,13 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt Tezos_crypto.Hashed.Smart_rollup_address.of_string_exn smart_rollup_address in - let module Sequencer = Make (struct + let module Rollup_rpc = Make (struct let smart_rollup_address = smart_rollup_address_typed end) in let* () = Tx_pool.start { - rollup_node = (module Sequencer); + rollup_node = (module Rollup_rpc); smart_rollup_address; mode = Sequencer; tx_timeout_limit = configuration.tx_pool_timeout_limit; @@ -291,9 +291,11 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt ~rollup_node_endpoint () in - + let module Sequencer_rpc = struct + let produce_block = Block_producer.produce_block + end in let directory = - Services.directory configuration ((module Sequencer), smart_rollup_address) + Services.directory configuration ((module Rollup_rpc), smart_rollup_address) in let directory = directory |> Evm_services.register smart_rollup_address_typed @@ -304,8 +306,8 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt let private_directory = Services.private_directory configuration - ((module Sequencer), smart_rollup_address) - ~produce_block:Block_producer.produce_block + ((module Rollup_rpc), smart_rollup_address) + (module Sequencer_rpc) in (private_directory, private_rpc_port)) sequencer_config.private_rpc_port diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index d60d300b3316..d26b86e5dcb0 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -414,9 +414,12 @@ let dispatch_request (config : Configuration.t) in Lwt.return JSONRPC.{value; id} -let dispatch_private_request - (produce_block : - force:bool -> timestamp:Time.Protocol.t -> int tzresult Lwt.t) +module type Sequencer_backend = sig + val produce_block : + force:bool -> timestamp:Time.Protocol.t -> int tzresult Lwt.t +end + +let dispatch_private_request (module Sequencer_rpc : Sequencer_backend) (_config : Configuration.t) ((module Backend_rpc : Services_backend_sig.S), _) ({method_; parameters; id} : JSONRPC.request) : JSONRPC.response Lwt.t = @@ -445,7 +448,9 @@ let dispatch_private_request let f (timestamp : Time.Protocol.t option) = let open Lwt_result_syntax in let timestamp = Option.value timestamp ~default:(Helpers.now ()) in - let* nb_transactions = produce_block ~force:true ~timestamp in + let* nb_transactions = + Sequencer_rpc.produce_block ~force:true ~timestamp + in rpc_ok (Ethereum_types.quantity_of_z @@ Z.of_int nb_transactions) in build ~f module_ parameters @@ -479,13 +484,13 @@ let generic_dispatch config ctx dir path dispatch_request = let dispatch_public config ctx dir = generic_dispatch config ctx dir Path.root dispatch_request -let dispatch_private config ctx ~produce_block dir = +let dispatch_private config ctx sequencer_rpc dir = generic_dispatch config ctx dir Path.(add_suffix root "private") - (dispatch_private_request produce_block) + (dispatch_private_request sequencer_rpc) let directory config ((module Rollup_node_rpc : Services_backend_sig.S), smart_rollup_address) = @@ -496,9 +501,9 @@ let directory config let private_directory config ((module Rollup_node_rpc : Services_backend_sig.S), smart_rollup_address) - ~produce_block = + sequencer_rpc = Directory.empty |> version |> dispatch_private config ((module Rollup_node_rpc : Services_backend_sig.S), smart_rollup_address) - ~produce_block + sequencer_rpc diff --git a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml index 64aa9b4c6b6b..18b986b0af81 100644 --- a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml +++ b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml @@ -242,13 +242,13 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt Tezos_crypto.Hashed.Smart_rollup_address.of_string_exn smart_rollup_address in - let module Sequencer = Make (struct + let module Rollup_rpc = Make (struct let smart_rollup_address = smart_rollup_address_typed end) in let* () = Tx_pool.start { - rollup_node = (module Sequencer); + rollup_node = (module Rollup_rpc); smart_rollup_address; mode = Sequencer; tx_timeout_limit = configuration.tx_pool_timeout_limit; @@ -281,8 +281,11 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt let () = Rollup_node_follower.start ~keep_alive ~proxy:false ~rollup_node_endpoint () in + let module Sequencer_rpc : Services.Sequencer_backend = struct + let produce_block = Threshold_encryption_block_producer.produce_block + end in let directory = - Services.directory configuration ((module Sequencer), smart_rollup_address) + Services.directory configuration ((module Rollup_rpc), smart_rollup_address) in let directory = directory |> Evm_services.register smart_rollup_address_typed @@ -293,8 +296,8 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt let private_directory = Services.private_directory configuration - ((module Sequencer), smart_rollup_address) - ~produce_block:Threshold_encryption_block_producer.produce_block + ((module Rollup_rpc), smart_rollup_address) + (module Sequencer_rpc) in (private_directory, private_rpc_port)) threshold_encryption_sequencer_config.private_rpc_port -- GitLab From a0bd462f642925931a14aaef82792d162415214e Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 6 May 2024 11:24:26 +0200 Subject: [PATCH 2/3] EVM/Sequencer: private `replay_block` RPC --- etherlink/bin_node/lib_dev/replay.ml | 16 ++++++++++++++++ etherlink/bin_node/lib_dev/replay.mli | 3 +++ etherlink/bin_node/lib_dev/rpc_encodings.ml | 17 +++++++++++++++++ etherlink/bin_node/lib_dev/rpc_encodings.mli | 5 +++++ etherlink/bin_node/lib_dev/sequencer.ml | 2 ++ etherlink/bin_node/lib_dev/services.ml | 15 +++++++++++++++ .../lib_dev/threshold_encryption_sequencer.ml | 2 ++ 7 files changed, 60 insertions(+) diff --git a/etherlink/bin_node/lib_dev/replay.ml b/etherlink/bin_node/lib_dev/replay.ml index 008fa7df3032..a0e980ea7a6d 100644 --- a/etherlink/bin_node/lib_dev/replay.ml +++ b/etherlink/bin_node/lib_dev/replay.ml @@ -65,3 +65,19 @@ let main ?profile ?kernel_path ~data_dir ~preimages ~preimages_endpoint return_unit | Apply_failure -> failwith "Could not replay blueprint %a" Ethereum_types.pp_quantity number + +let rpc block_number = + let open Lwt_result_syntax in + let* apply_result = Evm_context.replay block_number in + match apply_result with + | Apply_failure -> + failwith + "Could not replay block %a" + Ethereum_types.pp_quantity + block_number + | Apply_success {block_hash; evm_state; _} -> ( + let path = Durable_storage_path.Block.by_hash block_hash in + let*! bytes = Evm_state.inspect evm_state path in + match bytes with + | None -> failwith "Replayed block not found in the storage" + | Some b -> return @@ Ethereum_types.block_from_rlp b) diff --git a/etherlink/bin_node/lib_dev/replay.mli b/etherlink/bin_node/lib_dev/replay.mli index 8ed49395014e..7cdacb500edd 100644 --- a/etherlink/bin_node/lib_dev/replay.mli +++ b/etherlink/bin_node/lib_dev/replay.mli @@ -16,3 +16,6 @@ val main : ?smart_rollup_address:string -> Ethereum_types.quantity -> unit tzresult Lwt.t + +(** [rpc block_number] replays the block and returns the block. *) +val rpc : Ethereum_types.quantity -> Ethereum_types.block tzresult Lwt.t diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.ml b/etherlink/bin_node/lib_dev/rpc_encodings.ml index 96cb9e439328..06a3bdd8d6e7 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.ml +++ b/etherlink/bin_node/lib_dev/rpc_encodings.ml @@ -710,6 +710,22 @@ module Eth_max_priority_fee_per_gas = struct type ('input, 'output) method_ += Method : (input, output) method_ end +module Replay_block = struct + open Ethereum_types + + type input = Ethereum_types.quantity + + type output = block + + let input_encoding = quantity_encoding + + let output_encoding = block_encoding + + let method_ = "tez_replayBlock" + + type ('input, 'output) method_ += Method : (input, output) method_ +end + type map_result = | Method : ('input, 'output) method_ @@ -754,6 +770,7 @@ let supported_methods : (module METHOD) list = (module Produce_block); (module Durable_state_value); (module Eth_max_priority_fee_per_gas); + (module Replay_block); ] let unsupported_methods : string list = diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.mli b/etherlink/bin_node/lib_dev/rpc_encodings.mli index 6cb1651352e1..e0e55bce1e46 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.mli +++ b/etherlink/bin_node/lib_dev/rpc_encodings.mli @@ -272,6 +272,11 @@ module Durable_state_value : module Eth_max_priority_fee_per_gas : METHOD with type input = unit and type output = Ethereum_types.quantity +module Replay_block : + METHOD + with type input = Ethereum_types.quantity + and type output = Ethereum_types.block + type map_result = | Method : ('input, 'output) method_ diff --git a/etherlink/bin_node/lib_dev/sequencer.ml b/etherlink/bin_node/lib_dev/sequencer.ml index 43ae46a11309..ffedf5989a97 100644 --- a/etherlink/bin_node/lib_dev/sequencer.ml +++ b/etherlink/bin_node/lib_dev/sequencer.ml @@ -293,6 +293,8 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt in let module Sequencer_rpc = struct let produce_block = Block_producer.produce_block + + let replay_block = Replay.rpc end in let directory = Services.directory configuration ((module Rollup_rpc), smart_rollup_address) diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index d26b86e5dcb0..e985104b0346 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -417,6 +417,9 @@ let dispatch_request (config : Configuration.t) module type Sequencer_backend = sig val produce_block : force:bool -> timestamp:Time.Protocol.t -> int tzresult Lwt.t + + val replay_block : + Ethereum_types.quantity -> Ethereum_types.block tzresult Lwt.t end let dispatch_private_request (module Sequencer_rpc : Sequencer_backend) @@ -466,6 +469,18 @@ let dispatch_private_request (module Sequencer_rpc : Sequencer_backend) rpc_ok value in build ~f module_ parameters + | Method (Replay_block.Method, module_) -> + let f block_number = + let open Lwt_result_syntax in + let*? block_number = + Option.to_result + ~none:[error_of_fmt "missing block number"] + block_number + in + let* block = Sequencer_rpc.replay_block block_number in + rpc_ok block + in + build ~f module_ parameters | _ -> Stdlib.failwith "The pattern matching of methods is not exhaustive" in return JSONRPC.{value; id} diff --git a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml index 18b986b0af81..f119af92cd77 100644 --- a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml +++ b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml @@ -283,6 +283,8 @@ let main ~data_dir ?(genesis_timestamp = Helpers.now ()) ~cctxt in let module Sequencer_rpc : Services.Sequencer_backend = struct let produce_block = Threshold_encryption_block_producer.produce_block + + let replay_block = Replay.rpc end in let directory = Services.directory configuration ((module Rollup_rpc), smart_rollup_address) -- GitLab From c79fd19b8c8b2d3e8e4b9fbcb0a92013dcb918f1 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 6 May 2024 13:39:39 +0200 Subject: [PATCH 3/3] EVM/Tests: test replayBLock rpc --- etherlink/tezt/lib/rpc.ml | 18 +++++++++++ etherlink/tezt/lib/rpc.mli | 4 +++ etherlink/tezt/tests/evm_sequencer.ml | 45 ++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/etherlink/tezt/lib/rpc.ml b/etherlink/tezt/lib/rpc.ml index 0b0fcb3ccb68..60b2321ddefa 100644 --- a/etherlink/tezt/lib/rpc.ml +++ b/etherlink/tezt/lib/rpc.ml @@ -129,6 +129,12 @@ module Request = struct let eth_maxPriorityFeePerGas = {method_ = "eth_maxPriorityFeePerGas"; parameters = `Null} + + let replayBlock blockNumber = + { + method_ = "tez_replayBlock"; + parameters = `String (string_of_int blockNumber); + } end let net_version evm_node = @@ -309,3 +315,15 @@ let get_storage_at ~address ?(block = Latest) ~pos evm_node = let get_max_priority_fee_per_gas evm_node = let* json = Evm_node.call_evm_rpc evm_node Request.eth_maxPriorityFeePerGas in return JSON.(json |-> "result" |> as_int32) + +let replay_block blockNumber evm_node = + let* response = + Evm_node.call_evm_rpc + ~private_:true + evm_node + (Request.replayBlock blockNumber) + in + return + @@ decode_or_error + (fun response -> Evm_node.extract_result response |> Block.of_json) + response diff --git a/etherlink/tezt/lib/rpc.mli b/etherlink/tezt/lib/rpc.mli index 6d3980a60c1d..7f61c42508c2 100644 --- a/etherlink/tezt/lib/rpc.mli +++ b/etherlink/tezt/lib/rpc.mli @@ -146,3 +146,7 @@ val get_storage_at : (string, error) result Lwt.t val get_max_priority_fee_per_gas : Evm_node.t -> Int32.t Lwt.t + +(** [replay_block number evm_node] replays the block [number] and returns its + representation. *) +val replay_block : int -> Evm_node.t -> (Block.t, error) result Lwt.t diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 23b81b880a27..75ea81b0cac7 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -3094,6 +3094,48 @@ let test_store_smart_rollup_address = in unit +let test_replay_rpc = + register_both + ~tags:["evm"; "rpc"; "replay"] + ~title:"Sequencer can replay a block" + @@ fun {sc_rollup_node; sequencer; client; proxy; _} _protocol -> + (* Transfer funds to a random address. *) + let address = "0xB7A97043983f24991398E5a82f63F4C58a417185" in + let* transaction_hash = + send_transaction + (Eth_cli.transaction_send + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~to_public_key:address + ~value:(Wei.of_eth_int 10) + ~endpoint:(Evm_node.endpoint sequencer)) + sequencer + in + let*@ {Transaction.blockNumber; _} = + Rpc.get_transaction_by_hash ~transaction_hash sequencer + in + (* Block few levels to ensure we are replaying on an old block. *) + let* () = + repeat 2 (fun () -> + next_evm_level ~evm_node:sequencer ~sc_rollup_node ~client) + in + let* () = bake_until_sync ~sequencer ~sc_rollup_node ~proxy ~client () in + let*@ original_block = + Rpc.get_block_by_number + ~full_tx_objects:false + ~block:(Int32.to_string blockNumber) + sequencer + in + let*@ replayed_block = + Rpc.replay_block (Int32.to_int blockNumber) sequencer + in + (* Checks the block hash is the same. If so, we can assume they have the same + state hash and transactions. *) + Check.( + (original_block.hash = replayed_block.hash) + string + ~error_msg:"Replayed block hash is %R, but the original block has %L") ; + unit + let protocols = Protocol.all let () = @@ -3139,4 +3181,5 @@ let () = test_blueprint_limit_with_delayed_inbox protocols ; test_reset protocols ; test_preimages_endpoint protocols ; - test_store_smart_rollup_address protocols + test_store_smart_rollup_address protocols ; + test_replay_rpc protocols -- GitLab