From 26883d7a1011bbd74c85da63ea8cfb47bb19ef3a Mon Sep 17 00:00:00 2001 From: Valentin Chaboche Date: Thu, 18 Jul 2024 12:09:41 +0200 Subject: [PATCH] EVM/Node: generalise simulation input type --- etherlink/bin_node/lib_dev/simulation.ml | 82 ++++++++++++++---------- etherlink/bin_node/lib_dev/simulator.ml | 54 +++++++--------- etherlink/bin_node/lib_dev/tracer.ml | 2 +- 3 files changed, 71 insertions(+), 67 deletions(-) diff --git a/etherlink/bin_node/lib_dev/simulation.ml b/etherlink/bin_node/lib_dev/simulation.ml index 6f966f9cfd5e..a11aeac8572d 100644 --- a/etherlink/bin_node/lib_dev/simulation.ml +++ b/etherlink/bin_node/lib_dev/simulation.ml @@ -26,51 +26,63 @@ open Ethereum_types -(** New versions of estimate gas may specify whether we want to - include the DA fees or not. As currently some kernels running on - Ghostnet and/or Mainnet don't support yet this feature, we put - this as optional. *) -type estimate_gas_input = { +type estimate_gas_input_v1 = { call : Ethereum_types.call; - with_da_fees : bool option; + with_da_fees : bool; (** If true, the gas returned by the simulation include the DA gas units. *) } +type estimate_gas_input = V0 of call | V1 of estimate_gas_input_v1 + (** [hex_string_to_bytes s] transforms a hex string [s] into a byte string. *) let hex_string_to_bytes (Hex s) = `Hex s |> Hex.to_bytes_exn +let of_opt of_val = function + | None -> Rlp.Value Bytes.empty + | Some v -> of_val v + +let of_addr (Address s) = Rlp.Value (hex_string_to_bytes s) + +let of_qty (Qty z) = Rlp.Value (Z.to_bits z |> Bytes.of_string) + +let of_hash (Hash h) = Rlp.Value (hex_string_to_bytes h) + +let rlp_v0 call = + Rlp.List + [ + of_opt of_addr call.from; + of_opt of_addr call.to_; + of_opt of_qty call.gas; + of_opt of_qty call.gasPrice; + of_opt of_qty call.value; + of_opt of_hash call.data; + ] + +let rlp_v1 {call; with_da_fees} = + Rlp.List + [ + of_opt of_addr call.from; + of_opt of_addr call.to_; + of_opt of_qty call.gas; + of_opt of_qty call.gasPrice; + of_opt of_qty call.value; + of_opt of_hash call.data; + Ethereum_types.bool_to_rlp_bytes with_da_fees; + ] + (** Encoding used to forward the call to the kernel, to be used in simulation - mode only. *) -let rlp_encode ({call; with_da_fees} : estimate_gas_input) = - let of_opt of_val = function - | None -> Rlp.Value Bytes.empty - | Some v -> of_val v - in - let of_addr (Address s) = Rlp.Value (hex_string_to_bytes s) in - let of_qty (Qty z) = Rlp.Value (Z.to_bits z |> Bytes.of_string) in - let of_hash (Hash h) = Rlp.Value (hex_string_to_bytes h) in - let rlp_form = - Rlp.List - ([ - of_opt of_addr call.from; - of_opt of_addr call.to_; - of_opt of_qty call.gas; - of_opt of_qty call.gasPrice; - of_opt of_qty call.value; - of_opt of_hash call.data; - ] - @ - match with_da_fees with - | Some with_da_fees -> [Ethereum_types.bool_to_rlp_bytes with_da_fees] - | None -> []) + mode only. *) +let rlp_encode input = + let prefix, rlp = + match input with + | V0 call -> (None, rlp_v0 call) + | V1 input -> (Some '\001', rlp_v1 input) in - - (* we aim to use [String.chunk_bytes] *) - if Option.is_some with_da_fees then - (* If with_da_fees is present, that's the new version of simulation. *) - Bytes.cat (Bytes.make 1 '\001') (Rlp.encode rlp_form) - else Rlp.encode rlp_form + let payload = Rlp.encode rlp in + match prefix with + | None -> payload + | Some prefix -> Bytes.cat (Bytes.make 1 prefix) payload type simulation_message = | Start diff --git a/etherlink/bin_node/lib_dev/simulator.ml b/etherlink/bin_node/lib_dev/simulator.ml index 16e46ba59916..4a55e3777fb3 100644 --- a/etherlink/bin_node/lib_dev/simulator.ml +++ b/etherlink/bin_node/lib_dev/simulator.ml @@ -67,18 +67,23 @@ module Make (SimulationBackend : SimulationBackend) = struct log_kernel_debug_file = Some log_file; } - (* The feature DA fees has been added later in the kernel. The node must - decide whether it wants to activate it or not. + let simulation_input ~simulation_version ~with_da_fees call = + match simulation_version with + | `V0 -> Simulation.V0 call + | `V1 -> V1 {call; with_da_fees} + + (* Simulation have different versions in the kernel, the inputs change + between the different versions. As the simulation can be performed on past states, including past kernels, - we cannot consider it enabled even if it's supported by all latest + we cannot consider only latest version if it's supported by all latest kernels on ghostnet and mainnet. *) - let enabled_with_da_fees simulation_state = + let simulation_version simulation_state = let open Lwt_result_syntax in let* storage_version = get_storage_version simulation_state in - if storage_version < 12 then return_false - else if storage_version > 12 then return_true + if storage_version < 12 then return `V0 + else if storage_version > 12 then return `V1 else (* We are in the unknown, some kernels with STORAGE_VERSION = 12 have the features, some do not. *) @@ -86,25 +91,21 @@ module Make (SimulationBackend : SimulationBackend) = struct (* This is supposed to be the only version where STORAGE_VERSION is 12, but with_da_fees isn't enabled. *) if kernel_version = "ec7c3b349624896b269e179384d0a45cf39e1145" then - return_false - else return_true + return `V0 + else return `V1 let simulate_call call block_param = let open Lwt_result_syntax in let* simulation_state = SimulationBackend.simulation_state ~block:block_param () in - let* enabled_with_da_fees = enabled_with_da_fees simulation_state in + let* simulation_version = simulation_version simulation_state in let* bytes = call_simulation simulation_state ~log_file:"simulate_call" ~input_encoder:Simulation.encode - ~input: - { - call; - with_da_fees = (if enabled_with_da_fees then Some true else None); - } + ~input:(simulation_input ~simulation_version ~with_da_fees:true call) in Lwt.return (Simulation.simulation_result bytes) @@ -119,7 +120,7 @@ module Make (SimulationBackend : SimulationBackend) = struct in Lwt.return (Simulation.gas_estimation bytes) - let rec confirm_gas ~enabled_with_da_fees (call : Ethereum_types.call) gas + let rec confirm_gas ~simulation_version (call : Ethereum_types.call) gas simulation_state = let open Ethereum_types in let open Lwt_result_syntax in @@ -128,10 +129,7 @@ module Make (SimulationBackend : SimulationBackend) = struct let new_call = {call with gas = Some gas} in let* result = call_estimate_gas - { - call = new_call; - with_da_fees = (if enabled_with_da_fees then Some false else None); - } + (simulation_input ~simulation_version ~with_da_fees:false new_call) simulation_state in match result with @@ -141,16 +139,13 @@ module Make (SimulationBackend : SimulationBackend) = struct let new_gas = double gas in if reached_max new_gas then failwith "Gas estimate reached max gas limit." - else confirm_gas ~enabled_with_da_fees call new_gas simulation_state + else confirm_gas ~simulation_version call new_gas simulation_state | Ok (Ok _) -> ( (* Since the gas computation related to execution is fine. We can call the estimation with the da fees taken into account. *) let* res = call_estimate_gas - { - call; - with_da_fees = (if enabled_with_da_fees then Some true else None); - } + (simulation_input ~simulation_version ~with_da_fees:true call) simulation_state in match res with @@ -163,21 +158,18 @@ module Make (SimulationBackend : SimulationBackend) = struct Gas estimation currently ignores the block parameter. When this is fixed, we need to give the block parameter to the call to - {!enabled_with_da_fees}. *) + {!simulation_version}. *) let* simulation_state = SimulationBackend.simulation_state () in - let* enabled_with_da_fees = enabled_with_da_fees simulation_state in + let* simulation_version = simulation_version simulation_state in let* res = call_estimate_gas - { - call; - with_da_fees = (if enabled_with_da_fees then Some false else None); - } + (simulation_input ~simulation_version ~with_da_fees:false call) simulation_state in match res with | Ok (Ok {gas_used = Some gas; value}) -> let+ gas_used = - confirm_gas ~enabled_with_da_fees call gas simulation_state + confirm_gas ~simulation_version call gas simulation_state in Ok (Ok {Simulation.gas_used = Some gas_used; value}) | _ -> return res diff --git a/etherlink/bin_node/lib_dev/tracer.ml b/etherlink/bin_node/lib_dev/tracer.ml index 18e183ae65e1..f1dc6be46f69 100644 --- a/etherlink/bin_node/lib_dev/tracer.ml +++ b/etherlink/bin_node/lib_dev/tracer.ml @@ -87,7 +87,7 @@ let trace_call ~call ~block ~config = return state in - let*? messages = Simulation.encode {call; with_da_fees = Some false} in + let*? messages = Simulation.(encode (V1 {call; with_da_fees = false})) in let simulation_input = Simulation.Encodings. -- GitLab