From d68a52884d4b6e8635ca56feadf02bb62498086d Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 27 May 2024 11:15:41 +0200 Subject: [PATCH 1/4] EVM/Node: tracer errors --- .../bin_node/lib_dev/encodings/tracer_types.ml | 6 ++++++ .../bin_node/lib_dev/encodings/tracer_types.mli | 6 ++++++ etherlink/bin_node/lib_dev/rpc_errors.ml | 13 +++++++++++++ 3 files changed, 25 insertions(+) diff --git a/etherlink/bin_node/lib_dev/encodings/tracer_types.ml b/etherlink/bin_node/lib_dev/encodings/tracer_types.ml index b84ed46fe7b4..2583c8e18325 100644 --- a/etherlink/bin_node/lib_dev/encodings/tracer_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/tracer_types.ml @@ -5,6 +5,12 @@ (* *) (*****************************************************************************) +type error += + | Not_supported + | Transaction_not_found of Ethereum_types.hash + | Block_not_found of Ethereum_types.quantity + | Trace_not_found + type tracer_config = { enable_return_data : bool; enable_memory : bool; diff --git a/etherlink/bin_node/lib_dev/encodings/tracer_types.mli b/etherlink/bin_node/lib_dev/encodings/tracer_types.mli index fcaa47442295..30875e849ff8 100644 --- a/etherlink/bin_node/lib_dev/encodings/tracer_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/tracer_types.mli @@ -5,6 +5,12 @@ (* *) (*****************************************************************************) +type error += + | Not_supported + | Transaction_not_found of Ethereum_types.hash + | Block_not_found of Ethereum_types.quantity + | Trace_not_found + type tracer_config = { enable_return_data : bool; enable_memory : bool; diff --git a/etherlink/bin_node/lib_dev/rpc_errors.ml b/etherlink/bin_node/lib_dev/rpc_errors.ml index b158b32b9038..373bbdace1fb 100644 --- a/etherlink/bin_node/lib_dev/rpc_errors.ml +++ b/etherlink/bin_node/lib_dev/rpc_errors.ml @@ -85,3 +85,16 @@ let limit_exceeded reason hash = let json_rpc_version_not_supported reason = JSONRPC.{code = -32006; message = reason; data = None} + +let trace_transaction_not_found hash = + internal_error + (Format.asprintf "Transaction %a not found" Ethereum_types.pp_hash hash) + +let trace_block_not_found block_number = + resource_unavailable + (Format.asprintf + "Block %a unavailable for replay" + Ethereum_types.pp_quantity + block_number) + +let trace_not_found = internal_error "Trace not available" -- GitLab From 35c8eaf3cd21d49a86172e8284e61066bc531a98 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Tue, 14 May 2024 17:11:54 +0200 Subject: [PATCH 2/4] EVM/Node: trace implementation Co-authored-by: Rodi-Can Bozman --- .../bin_node/lib_dev/durable_storage_path.ml | 4 +++ .../bin_node/lib_dev/durable_storage_path.mli | 5 ++++ .../lib_dev/encodings/tracer_types.ml | 15 +++++++++- .../lib_dev/encodings/tracer_types.mli | 2 ++ etherlink/bin_node/lib_dev/evm_context.ml | 6 ++-- etherlink/bin_node/lib_dev/evm_context.mli | 1 + etherlink/bin_node/lib_dev/tracer.ml | 28 +++++++++++++++++++ etherlink/bin_node/lib_dev/tracer.mli | 15 ++++++++++ 8 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 etherlink/bin_node/lib_dev/tracer.ml create mode 100644 etherlink/bin_node/lib_dev/tracer.mli diff --git a/etherlink/bin_node/lib_dev/durable_storage_path.ml b/etherlink/bin_node/lib_dev/durable_storage_path.ml index 20d5bd6048db..5666b2e3d035 100644 --- a/etherlink/bin_node/lib_dev/durable_storage_path.ml +++ b/etherlink/bin_node/lib_dev/durable_storage_path.ml @@ -115,3 +115,7 @@ module Evm_events = struct let nth_event i = events ^ "/" ^ string_of_int i end + +module Trace_transaction = struct + let input = EVM.make "/trace/input" +end diff --git a/etherlink/bin_node/lib_dev/durable_storage_path.mli b/etherlink/bin_node/lib_dev/durable_storage_path.mli index aa43e85e90cc..0bf93a8353e6 100644 --- a/etherlink/bin_node/lib_dev/durable_storage_path.mli +++ b/etherlink/bin_node/lib_dev/durable_storage_path.mli @@ -91,3 +91,8 @@ module Evm_events : sig (** Path to the nth event of the kernel. *) val nth_event : int -> path end + +module Trace_transaction : sig + (** Path where is stored the input of the tracer. *) + val input : path +end diff --git a/etherlink/bin_node/lib_dev/encodings/tracer_types.ml b/etherlink/bin_node/lib_dev/encodings/tracer_types.ml index 2583c8e18325..29d82b3963b7 100644 --- a/etherlink/bin_node/lib_dev/encodings/tracer_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/tracer_types.ml @@ -1,7 +1,7 @@ (*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) -(* Copyright (c) 2023 Nomadic Labs *) +(* Copyright (c) 2024 Nomadic Labs *) (* *) (*****************************************************************************) @@ -101,3 +101,16 @@ let input_encoding = Ethereum_types.hash_encoding config_encoding default_config + +let input_rlp_encoder hash config = + let open Rlp in + (* See bool encoding for RLP: https://docs.rs/ethereum-rlp/latest/src/rlp/impls.rs.html#36-44 *) + let bool_encoding b = + if b then Value (Bytes.make 1 '\001') else Value Bytes.empty + in + let hash = Value (Ethereum_types.hash_to_bytes hash |> Bytes.of_string) in + let return_data = bool_encoding config.tracer_config.enable_return_data in + let memory = bool_encoding config.tracer_config.enable_memory in + let stack = bool_encoding config.tracer_config.disable_stack in + let storage = bool_encoding config.tracer_config.disable_storage in + List [hash; return_data; memory; stack; storage] |> encode |> Bytes.to_string diff --git a/etherlink/bin_node/lib_dev/encodings/tracer_types.mli b/etherlink/bin_node/lib_dev/encodings/tracer_types.mli index 30875e849ff8..920280e1582d 100644 --- a/etherlink/bin_node/lib_dev/encodings/tracer_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/tracer_types.mli @@ -40,3 +40,5 @@ val config_encoding : config Data_encoding.t type input = Ethereum_types.hash * config val input_encoding : (Ethereum_types.hash * config) Data_encoding.t + +val input_rlp_encoder : Ethereum_types.hash -> config -> string diff --git a/etherlink/bin_node/lib_dev/evm_context.ml b/etherlink/bin_node/lib_dev/evm_context.ml index 939b71247969..4cfb85f86f3b 100644 --- a/etherlink/bin_node/lib_dev/evm_context.ml +++ b/etherlink/bin_node/lib_dev/evm_context.ml @@ -1276,8 +1276,8 @@ let new_last_known_l1_level l = let delayed_inbox_hashes () = worker_wait_for_request Delayed_inbox_hashes -let replay ?profile ?(alter_evm_state = Lwt_result_syntax.return) - (Ethereum_types.Qty number) = +let replay ?(log_file = "replay") ?profile + ?(alter_evm_state = Lwt_result_syntax.return) (Ethereum_types.Qty number) = let open Lwt_result_syntax in let* evm_state = worker_wait_for_request (Evm_state_after (Number (Qty Z.(pred number)))) @@ -1288,7 +1288,7 @@ let replay ?profile ?(alter_evm_state = Lwt_result_syntax.return) let*! data_dir, config = execution_config in let* blueprint = get_blueprint (Qty number) in Evm_state.apply_blueprint - ~log_file:"replay" + ~log_file ?profile ~data_dir ~config diff --git a/etherlink/bin_node/lib_dev/evm_context.mli b/etherlink/bin_node/lib_dev/evm_context.mli index a075015c751e..97a40b932f78 100644 --- a/etherlink/bin_node/lib_dev/evm_context.mli +++ b/etherlink/bin_node/lib_dev/evm_context.mli @@ -125,6 +125,7 @@ val delayed_inbox_hashes : unit -> Ethereum_types.hash list tzresult Lwt.t Note: this function only goes through the worker to fetch the correct context. *) val replay : + ?log_file:string -> ?profile:bool -> ?alter_evm_state:(Evm_state.t -> Evm_state.t tzresult Lwt.t) -> Ethereum_types.quantity -> diff --git a/etherlink/bin_node/lib_dev/tracer.ml b/etherlink/bin_node/lib_dev/tracer.ml new file mode 100644 index 000000000000..bd0266a613c0 --- /dev/null +++ b/etherlink/bin_node/lib_dev/tracer.ml @@ -0,0 +1,28 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +let trace_transaction ~block_number ~transaction_hash ~config = + let open Lwt_result_syntax in + let input = Tracer_types.input_rlp_encoder transaction_hash config in + let set_input state = + let*! state = + Evm_state.modify + ~key:Durable_storage_path.Trace_transaction.input + ~value:input + state + in + return state + in + let* apply_result = + Evm_context.replay + ~log_file:"trace_transaction" + ~alter_evm_state:set_input + block_number + in + match apply_result with + | Apply_failure -> tzfail Tracer_types.Trace_not_found + | Apply_success _ -> return_unit diff --git a/etherlink/bin_node/lib_dev/tracer.mli b/etherlink/bin_node/lib_dev/tracer.mli new file mode 100644 index 000000000000..f93f06510aeb --- /dev/null +++ b/etherlink/bin_node/lib_dev/tracer.mli @@ -0,0 +1,15 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(** [trace_transaction ~block_number ~transaction ~config] replays the block + [block_number] and traces [transaction_hash] in it, with the given + [config]. *) +val trace_transaction : + block_number:Ethereum_types.quantity -> + transaction_hash:Ethereum_types.hash -> + config:Tracer_types.config -> + unit tzresult Lwt.t -- GitLab From e9bbdcbcb16ba3d757c11932a1f3cae17d2bbdbd Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Wed, 15 May 2024 14:05:55 +0200 Subject: [PATCH 3/4] EVM/Node: tracer service implementation --- etherlink/bin_node/lib_dev/observer.ml | 2 ++ etherlink/bin_node/lib_dev/rollup_node.ml | 5 +++ etherlink/bin_node/lib_dev/sequencer.ml | 2 ++ etherlink/bin_node/lib_dev/services.ml | 27 +++++++++------ .../bin_node/lib_dev/services_backend_sig.ml | 15 +++++++++ .../lib_dev/threshold_encryption_sequencer.ml | 2 ++ etherlink/bin_node/lib_dev/tracer_sig.ml | 33 +++++++++++++++++++ 7 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 etherlink/bin_node/lib_dev/tracer_sig.ml diff --git a/etherlink/bin_node/lib_dev/observer.ml b/etherlink/bin_node/lib_dev/observer.ml index fd454d1f3838..7911cece0c95 100644 --- a/etherlink/bin_node/lib_dev/observer.ml +++ b/etherlink/bin_node/lib_dev/observer.ml @@ -101,6 +101,8 @@ end) : Services_backend_sig.Backend = struct let block_param_to_block_number = Evm_context.block_param_to_block_number + module Tracer = Tracer + let smart_rollup_address = Tezos_crypto.Hashed.Smart_rollup_address.to_string Ctxt.smart_rollup_address end diff --git a/etherlink/bin_node/lib_dev/rollup_node.ml b/etherlink/bin_node/lib_dev/rollup_node.ml index 68591fba4c51..864ebd8bbc6a 100644 --- a/etherlink/bin_node/lib_dev/rollup_node.ml +++ b/etherlink/bin_node/lib_dev/rollup_node.ml @@ -160,6 +160,11 @@ end) : Services_backend_sig.Backend = struct Ethereum_types.pp_block_hash hash) + module Tracer = struct + let trace_transaction ~block_number:_ ~transaction_hash:_ ~config:_ = + Lwt_result_syntax.tzfail Tracer_types.Not_supported + end + let smart_rollup_address = Base.smart_rollup_address end diff --git a/etherlink/bin_node/lib_dev/sequencer.ml b/etherlink/bin_node/lib_dev/sequencer.ml index d1babfff7b86..4817ef5bf29e 100644 --- a/etherlink/bin_node/lib_dev/sequencer.ml +++ b/etherlink/bin_node/lib_dev/sequencer.ml @@ -29,6 +29,8 @@ end) : Services_backend_sig.Backend = struct assert false end + module Tracer = Tracer + module SimulatorBackend = struct let simulate_and_read ?block ~input () = let open Lwt_result_syntax in diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index 74b0b7e81942..dc26baac3082 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -406,17 +406,24 @@ let dispatch_request (config : Configuration.t) let f (_ : unit option) = rpc_ok @@ Qty Z.zero in build ~f module_ parameters | Method (Trace_transaction.Method, module_) -> - let f (_ : Tracer_types.input option) = - return - (Error - JSONRPC. - { - code = -32000; - message = "Method not implemented"; - data = Some (`String method_); - }) + let f ((hash, config) : Tracer_types.input) = + let*! trace = Backend_rpc.trace_transaction hash config in + match trace with + | Ok _ -> rpc_ok () + | Error (Tracer_types.Not_supported :: _) -> + rpc_error + (Rpc_errors.method_not_supported Trace_transaction.method_) + | Error (Tracer_types.Transaction_not_found hash :: _) -> + rpc_error (Rpc_errors.trace_transaction_not_found hash) + | Error (Tracer_types.Block_not_found number :: _) -> + rpc_error (Rpc_errors.trace_block_not_found number) + | Error (Tracer_types.Trace_not_found :: _) -> + rpc_error Rpc_errors.trace_not_found + | Error e -> + let msg = Format.asprintf "%a" pp_print_trace e in + rpc_error (Rpc_errors.internal_error msg) in - build ~f module_ parameters + build_with_input ~f module_ parameters | Method (_, _) -> Stdlib.failwith "The pattern matching of methods is not exhaustive" in diff --git a/etherlink/bin_node/lib_dev/services_backend_sig.ml b/etherlink/bin_node/lib_dev/services_backend_sig.ml index e8613de09bdd..845ae5ccfb12 100644 --- a/etherlink/bin_node/lib_dev/services_backend_sig.ml +++ b/etherlink/bin_node/lib_dev/services_backend_sig.ml @@ -134,6 +134,12 @@ module type S = sig Ethereum_types.hex tzresult Lwt.t val smart_rollup_address : string + + (** [trace_transaction hash tracer] replays the block containing the + transaction [hash], and traces this transaction with the specified + [tracer]. *) + val trace_transaction : + Ethereum_types.hash -> Tracer_types.config -> unit tzresult Lwt.t end module type Backend = sig @@ -151,6 +157,8 @@ module type Backend = sig Ethereum_types.Block_parameter.extended -> Ethereum_types.quantity tzresult Lwt.t + module Tracer : Tracer_sig.Backend + val smart_rollup_address : string end @@ -162,5 +170,12 @@ module Make (Backend : Backend) : S = struct let block_param_to_block_number = Backend.block_param_to_block_number + include + Tracer_sig.Make + (struct + let transaction_receipt = transaction_receipt + end) + (Backend.Tracer) + let smart_rollup_address = Backend.smart_rollup_address end diff --git a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml index d1e160f21038..74d81ae7e89f 100644 --- a/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml +++ b/etherlink/bin_node/lib_dev/threshold_encryption_sequencer.ml @@ -40,6 +40,8 @@ end) : Services_backend_sig.Backend = struct let block_param_to_block_number = Evm_context.block_param_to_block_number + module Tracer = Tracer + let smart_rollup_address = Tezos_crypto.Hashed.Smart_rollup_address.to_string Ctxt.smart_rollup_address end diff --git a/etherlink/bin_node/lib_dev/tracer_sig.ml b/etherlink/bin_node/lib_dev/tracer_sig.ml new file mode 100644 index 000000000000..e49edd87bd09 --- /dev/null +++ b/etherlink/bin_node/lib_dev/tracer_sig.ml @@ -0,0 +1,33 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +module type Backend = sig + val trace_transaction : + block_number:Ethereum_types.quantity -> + transaction_hash:Ethereum_types.hash -> + config:Tracer_types.config -> + unit tzresult Lwt.t +end + +module Make (Storage : sig + val transaction_receipt : + Ethereum_types.hash -> + Ethereum_types.transaction_receipt option tzresult Lwt.t +end) +(Tracer : Backend) = +struct + let trace_transaction transaction_hash config = + let open Lwt_result_syntax in + let* receipt = Storage.transaction_receipt transaction_hash in + match receipt with + | None -> tzfail (Tracer_types.Transaction_not_found transaction_hash) + | Some Ethereum_types.{blockNumber; _} -> + Tracer.trace_transaction + ~block_number:blockNumber + ~transaction_hash + ~config +end -- GitLab From 7b06794886beba51003652da91b264d9d0156843 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Thu, 16 May 2024 09:29:22 +0200 Subject: [PATCH 4/4] EVM/Tests: debug_traceTransaction is now implemented without a result --- etherlink/tezt/tests/evm_sequencer.ml | 59 ++++++++++++++++----------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 59de4deaa278..f802367a0376 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -3456,23 +3456,15 @@ let test_trace_transaction = (* Check tracing without options works *) let* trace_result = Rpc.trace_transaction ~transaction_hash sequencer in (match trace_result with - | Ok _ -> Test.fail "debug_traceTransaction should have failed" - | Error {message; _} -> - Check.( - (message = "Method not implemented") - string - ~error_msg:"debug_traceTransaction is not implemented")) ; + | Ok _ -> () + | Error _ -> Test.fail "Trace transaction shouldn't have failed") ; (* Check tracing with a tracer without config *) let* trace_result = Rpc.trace_transaction ~transaction_hash ~tracer:"structLogger" sequencer in (match trace_result with - | Ok _ -> Test.fail "debug_traceTransaction should have failed" - | Error {message; _} -> - Check.( - (message = "Method not implemented") - string - ~error_msg:"debug_traceTransaction is not implemented")) ; + | Ok _ -> () + | Error _ -> Test.fail "Trace transaction shouldn't have failed") ; (* Check tracing with a tracer and a config *) let* trace_result = Rpc.trace_transaction @@ -3482,26 +3474,44 @@ let test_trace_transaction = sequencer in (match trace_result with - | Ok _ -> Test.fail "debug_traceTransaction should have failed" - | Error {message; _} -> - Check.( - (message = "Method not implemented") - string - ~error_msg:"debug_traceTransaction is not implemented")) ; + | Ok _ -> () + | Error _ -> Test.fail "Trace transaction shouldn't have failed") ; (* Check tracing without a tracer and a config *) let* trace_result = Rpc.trace_transaction ~transaction_hash - ~tracer_config:[("enableMemory", `Bool true)] + ~tracer_config: + [ + ("enableMemory", `Bool true); + ("enableReturnData", `Bool true); + ("disableStorage", `Bool true); + ("disableStack", `Bool true); + ] + sequencer + in + (match trace_result with + | Ok _ -> () + | Error _ -> Test.fail "Trace transaction shouldn't have failed") ; + unit + +let test_trace_transaction_on_invalid_transaction = + register_both + ~tags:["evm"; "rpc"; "trace"; "fail"] + ~title:"debug_traceTransaction fails on invalid transactions" + @@ fun {sc_rollup_node; sequencer; client; proxy; _} _protocol -> + let* () = bake_until_sync ~sequencer ~sc_rollup_node ~proxy ~client () in + (* Check tracing without options works *) + let* trace_result = + Rpc.trace_transaction + ~transaction_hash:("0x" ^ String.make 64 'f') sequencer in (match trace_result with - | Ok _ -> Test.fail "debug_traceTransaction should have failed" + | Ok _ -> Test.fail "Trace transaction should have failed" | Error {message; _} -> Check.( - (message = "Method not implemented") - string - ~error_msg:"debug_traceTransaction is not implemented")) ; + (message =~ rex "not found") + ~error_msg:"traceTransaction failed with the wrong error")) ; unit let protocols = Protocol.all @@ -3553,4 +3563,5 @@ let () = test_store_smart_rollup_address protocols ; test_replay_rpc protocols ; test_txpool_content_empty_with_legacy_encoding protocols ; - test_trace_transaction protocols + test_trace_transaction protocols ; + test_trace_transaction_on_invalid_transaction protocols -- GitLab