From b38c2fff870710ab1238a4f9138d8bab28a2425b Mon Sep 17 00:00:00 2001 From: pecornilleau Date: Mon, 12 Jun 2023 20:38:18 +0200 Subject: [PATCH 1/2] EVM: eth_estimateGas --- src/bin_evm_proxy/lib/mockup.ml | 7 +++--- src/bin_evm_proxy/lib/rollup_node.ml | 28 ++++++++++++++++++++- src/bin_evm_proxy/lib/rollup_node.mli | 5 ++++ src/bin_evm_proxy/lib/services.ml | 5 ++-- src/bin_evm_proxy/lib/simulation.ml | 26 ++++++++++++++++--- src/kernel_evm/kernel/src/simulation.rs | 1 + src/kernel_evm/kernel/src/storage.rs | 8 ++++++ tezt/tests/evm_rollup.ml | 33 ++++++++++++++++++++++++- 8 files changed, 102 insertions(+), 11 deletions(-) diff --git a/src/bin_evm_proxy/lib/mockup.ml b/src/bin_evm_proxy/lib/mockup.ml index e7fc40c2b75e..8cda75d95012 100644 --- a/src/bin_evm_proxy/lib/mockup.ml +++ b/src/bin_evm_proxy/lib/mockup.ml @@ -57,9 +57,8 @@ let code _ = "0x608060405234801561001057600080fd5b5061017f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80634e70b1dc1461004657806360fe47b1146100645780636d4ce63c14610080575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61007e6004803603810190610079919061011c565b6100a4565b005b6100886100ae565b60405161009591906100d0565b60405180910390f35b60005481565b8060008190555050565b60008054905090565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220ec57e49a647342208a1f5c9b1f2049bf1a27f02e19940819f38929bf67670a5964736f6c63430008120033" |> Lwt_result_syntax.return -(* Gas limit must be at least 21000, - must be more to allow for contract create *) -let gas_price = qty_f @@ Z.of_int 21000000 +(* Gas limit must be at least 21000 *) +let gas_price = qty_f @@ Z.of_int 21000 let transaction_counter = ref 0 @@ -141,6 +140,8 @@ let transaction_object = let simulate_call _ = Lwt_result_syntax.return (hash_of_string "0x000102030405") +let estimate_gas _ = Lwt_result_syntax.return gas_price + let smart_rollup_address = return "foo" let balance _addr = return balance diff --git a/src/bin_evm_proxy/lib/rollup_node.ml b/src/bin_evm_proxy/lib/rollup_node.ml index c3e1902339ba..a35ac58d27c1 100644 --- a/src/bin_evm_proxy/lib/rollup_node.ml +++ b/src/bin_evm_proxy/lib/rollup_node.ml @@ -482,7 +482,28 @@ module RPC = struct () {messages; reveal_pages = None; insight_requests} in - Simulation.parse_insights r + Simulation.call_result r + + let estimate_gas base call = + let open Lwt_result_syntax in + let*? messages = Simulation.encode call in + let insight_requests = + [ + Simulation.Encodings.Durable_storage_key ["evm"; "simulation_gas"]; + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5900 + for now the status is not used but it should be for error handling *) + Simulation.Encodings.Durable_storage_key ["evm"; "simulation_status"]; + ] + in + let* r = + call_service + ~base + simulation + () + () + {messages; reveal_pages = None; insight_requests} + in + Simulation.gas_estimation r end module type S = sig @@ -518,6 +539,9 @@ module type S = sig val chain_id : unit -> Ethereum_types.quantity tzresult Lwt.t val simulate_call : Ethereum_types.call -> Ethereum_types.hash tzresult Lwt.t + + val estimate_gas : + Ethereum_types.call -> Ethereum_types.quantity tzresult Lwt.t end module Make (Base : sig @@ -548,4 +572,6 @@ end) : S = struct let chain_id = RPC.chain_id Base.base let simulate_call = RPC.simulate_call Base.base + + let estimate_gas = RPC.estimate_gas Base.base end diff --git a/src/bin_evm_proxy/lib/rollup_node.mli b/src/bin_evm_proxy/lib/rollup_node.mli index 473edf2ea400..20a6f38c37de 100644 --- a/src/bin_evm_proxy/lib/rollup_node.mli +++ b/src/bin_evm_proxy/lib/rollup_node.mli @@ -101,6 +101,11 @@ module type S = sig (** [simulate_call call_info] asks the rollup to simulate a call, and returns the result. *) val simulate_call : Ethereum_types.call -> Ethereum_types.hash tzresult Lwt.t + + (** [estimate_gas call_info] asks the rollup to simulate a call, and returns the + gas used to execute the call. *) + val estimate_gas : + Ethereum_types.call -> Ethereum_types.quantity tzresult Lwt.t end (** Instantiate a module of type {!S} that communicates with a rollup diff --git a/src/bin_evm_proxy/lib/services.ml b/src/bin_evm_proxy/lib/services.ml index 7b455a97d0e2..cd1990cdb179 100644 --- a/src/bin_evm_proxy/lib/services.ml +++ b/src/bin_evm_proxy/lib/services.ml @@ -143,8 +143,9 @@ let dispatch_input | Eth_call.Input (Some (call, _)) -> let* call_result = Rollup_node_rpc.simulate_call call in return (Eth_call.Output (Ok call_result)) - | Get_estimate_gas.Input _ -> - return (Get_estimate_gas.Output (Ok Mockup.gas_price)) + | Get_estimate_gas.Input (Some call) -> + let* gas = Rollup_node_rpc.estimate_gas call in + return (Get_estimate_gas.Output (Ok gas)) | Txpool_content.Input _ -> let* txpool = Rollup_node_rpc.txpool () in return (Txpool_content.Output (Ok txpool)) diff --git a/src/bin_evm_proxy/lib/simulation.ml b/src/bin_evm_proxy/lib/simulation.ml index cf30843756c5..482e423c795c 100644 --- a/src/bin_evm_proxy/lib/simulation.ml +++ b/src/bin_evm_proxy/lib/simulation.ml @@ -206,13 +206,31 @@ module Encodings = struct ~description:"PVM state values requested after the simulation") end -let parse_insights (r : Data_encoding.json) = +let parse_insights decode (r : Data_encoding.json) = let s = Data_encoding.Json.destruct Encodings.eval_result r in match s.insights with - | Some b :: _ -> - let v = b |> Hex.of_bytes |> Hex.show in - Lwt.return_ok (Hash v) + | Some b :: _ -> Lwt.return_ok (decode b) | _ -> Error_monad.failwith "Couldn't parse insights: %s" (Data_encoding.Json.to_string r) + +let call_result = + parse_insights (fun b -> + let v = b |> Hex.of_bytes |> Hex.show in + Hash v) + +let gas_estimation json = + let open Lwt_result_syntax in + let decode b = b |> Bytes.to_string |> Z.of_bits in + let* simulated_amount = parse_insights decode json in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5977 + remove this once the gas is accounted correctly *) + (* minimum gas for any Ethereum transaction *) + let min_amount = Z.of_int 21000 in + let amount = + Z.( + if simulated_amount < min_amount then simulated_amount + min_amount + else simulated_amount) + in + return @@ quantity_of_z @@ Z.max simulated_amount amount diff --git a/src/kernel_evm/kernel/src/simulation.rs b/src/kernel_evm/kernel/src/simulation.rs index 95023496ea5e..8a2dad19af0a 100644 --- a/src/kernel_evm/kernel/src/simulation.rs +++ b/src/kernel_evm/kernel/src/simulation.rs @@ -241,6 +241,7 @@ pub fn start_simulation_mode(host: &mut Host) -> Result<(), Error let outcome = simulation.run(host)?; debug_msg!(host, "outcome={:?} ", outcome); storage::store_simulation_status(host, outcome.is_success)?; + storage::store_simulation_gas(host, outcome.gas_used)?; storage::store_simulation_result(host, outcome.result) } diff --git a/src/kernel_evm/kernel/src/storage.rs b/src/kernel_evm/kernel/src/storage.rs index ed9ae69178b0..ad9f51cdc34b 100644 --- a/src/kernel_evm/kernel/src/storage.rs +++ b/src/kernel_evm/kernel/src/storage.rs @@ -58,6 +58,7 @@ const EVM_INFO_PER_LEVEL_STATS_TOTAL: RefPath = pub const SIMULATION_RESULT: RefPath = RefPath::assert_from(b"/simulation_result"); pub const SIMULATION_STATUS: RefPath = RefPath::assert_from(b"/simulation_status"); +pub const SIMULATION_GAS: RefPath = RefPath::assert_from(b"/simulation_gas"); pub const KERNEL_UPGRADE_NONCE: RefPath = RefPath::assert_from(b"/upgrade_nonce"); pub const DEPOSIT_NONCE: RefPath = RefPath::assert_from(b"/deposit_nonce"); @@ -314,6 +315,13 @@ pub fn store_simulation_result( Ok(()) } +pub fn store_simulation_gas( + host: &mut Host, + result: u64, +) -> Result<(), Error> { + write_u256(host, &SIMULATION_GAS.into(), U256::from(result)) +} + pub fn store_simulation_status( host: &mut Host, result: bool, diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index 783167029892..9971a2342c1a 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -1228,6 +1228,36 @@ let test_eth_call_large = unit) +let test_estimate_gas = + Protocol.register_test + ~__FILE__ + ~tags:["evm"; "eth_estimategas"; "simulate"] + ~title:"Try to estimate gas for contract creation" + (fun protocol -> + (* setup *) + let* {evm_proxy_server; _} = + setup_past_genesis protocol ~deposit_admin:None + in + + (* large request *) + let data = read_file simple_storage.bin in + let eth_call = [("data", Ezjsonm.encode_string @@ "0x" ^ data)] in + + (* make call to proxy *) + let* call_result = + Evm_proxy_server.( + call_evm_rpc + evm_proxy_server + {method_ = "eth_estimateGas"; parameters = `A [`O eth_call]}) + in + + (* Check the RPC returns a `result`. *) + let r = call_result |> Evm_proxy_server.extract_result in + Check.((JSON.as_int r = 21123) int) + ~error_msg:"Expected result greater than %R, but got %L" ; + + unit) + let test_eth_call_storage_contract_rollup_node = Protocol.register_test ~__FILE__ @@ -1538,6 +1568,7 @@ let register_evm_proxy_server ~protocols = test_eth_call_storage_contract_eth_cli protocols ; test_eth_call_large protocols ; test_preinitialized_evm_kernel protocols ; - test_deposit_fa12 protocols + test_deposit_fa12 protocols ; + test_estimate_gas protocols let register ~protocols = register_evm_proxy_server ~protocols -- GitLab From ee7d3813cab2bcf2cf3cdf13488bebcd02b4ed16 Mon Sep 17 00:00:00 2001 From: pecornilleau Date: Fri, 23 Jun 2023 18:16:25 +0200 Subject: [PATCH 2/2] EVM: accept unbounded nb of fields --- src/bin_evm_proxy/lib/ethereum_types.ml | 40 +++++++++++++--------- tezt/tests/evm_rollup.ml | 45 +++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/bin_evm_proxy/lib/ethereum_types.ml b/src/bin_evm_proxy/lib/ethereum_types.ml index 5ca11d389af0..60b81234654d 100644 --- a/src/bin_evm_proxy/lib/ethereum_types.ml +++ b/src/bin_evm_proxy/lib/ethereum_types.ml @@ -708,22 +708,32 @@ type call = { data : hash option; } -let call_encoding = +let call_extendable_encoding = let open Data_encoding in - conv - (fun {from; to_; gas; gasPrice; value; data} -> - (from, Some to_, gas, gasPrice, value, data)) - (fun (from, to_, gas, gasPrice, value, data) -> - {from; to_ = Option.join to_; gas; gasPrice; value; data}) - (obj6 - (opt "from" address_encoding) - (opt "to" (option address_encoding)) - (* `call` is also used for estimateGas, which allows all fields to be - empty, hence `to` can be `null` or absent. *) - (opt "gas" quantity_encoding) - (opt "gasPrice" quantity_encoding) - (opt "value" quantity_encoding) - (opt "data" hash_encoding)) + (* `merge_objs unit` allows the encoding to accept any number of + unspecified fields from JSON. *) + merge_objs + (conv + (fun {from; to_; gas; gasPrice; value; data} -> + (from, Some to_, gas, gasPrice, value, data)) + (fun (from, to_, gas, gasPrice, value, data) -> + {from; to_ = Option.join to_; gas; gasPrice; value; data}) + (obj6 + (opt "from" address_encoding) + (opt "to" (option address_encoding)) + (* `call` is also used for estimateGas, which allows all fields to be + empty, hence `to` can be `null` or absent. *) + (opt "gas" quantity_encoding) + (opt "gasPrice" quantity_encoding) + (opt "value" quantity_encoding) + (opt "data" hash_encoding))) + unit + +let call_encoding = + Data_encoding.conv + (fun call -> (call, ())) + (fun (call, ()) -> call) + call_extendable_encoding (** The txpool encoding can be found in https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-txpool#txpool-content. diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index 9971a2342c1a..5d0c27ad8e3b 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -1193,7 +1193,7 @@ let test_inject_100_transactions = let test_eth_call_large = Protocol.register_test ~__FILE__ - ~tags:["evm"; "eth_call"; "simulate"; "large"; "try"] + ~tags:["evm"; "eth_call"; "simulate"; "large"] ~title:"Try to call with a large amount of data" (fun protocol -> (* setup *) @@ -1258,6 +1258,46 @@ let test_estimate_gas = unit) +let test_estimate_gas_additionnal_field = + Protocol.register_test + ~__FILE__ + ~tags:["evm"; "eth_estimategas"; "simulate"; "remix"] + ~title:"eth_estimateGas allows additional fields" + (fun protocol -> + (* setup *) + let* {evm_proxy_server; _} = + setup_past_genesis protocol ~deposit_admin:None + in + + (* large request *) + let data = read_file simple_storage.bin in + let eth_call = + [ + ( "from", + Ezjsonm.encode_string + @@ "0x6ce4d79d4e77402e1ef3417fdda433aa744c6e1c" ); + ("data", Ezjsonm.encode_string @@ "0x" ^ data); + ("value", Ezjsonm.encode_string @@ "0x0"); + (* for some reason remix adds the "type" field *) + ("type", Ezjsonm.encode_string @@ "0x1"); + ] + in + + (* make call to proxy *) + let* call_result = + Evm_proxy_server.( + call_evm_rpc + evm_proxy_server + {method_ = "eth_estimateGas"; parameters = `A [`O eth_call]}) + in + + (* Check the RPC returns a `result`. *) + let r = call_result |> Evm_proxy_server.extract_result in + Check.((JSON.as_int r = 21123) int) + ~error_msg:"Expected result greater than %R, but got %L" ; + + unit) + let test_eth_call_storage_contract_rollup_node = Protocol.register_test ~__FILE__ @@ -1569,6 +1609,7 @@ let register_evm_proxy_server ~protocols = test_eth_call_large protocols ; test_preinitialized_evm_kernel protocols ; test_deposit_fa12 protocols ; - test_estimate_gas protocols + test_estimate_gas protocols ; + test_estimate_gas_additionnal_field protocols let register ~protocols = register_evm_proxy_server ~protocols -- GitLab