diff --git a/etherlink/CHANGES_NODE.md b/etherlink/CHANGES_NODE.md index 3b55a583924af5ccdf3667cebadfe8b6e27c3b77..2c900f74eb11c07321d2c5725e83029949dc8fd4 100644 --- a/etherlink/CHANGES_NODE.md +++ b/etherlink/CHANGES_NODE.md @@ -16,6 +16,11 @@ - Fix `octez_evm_node_head` for the RPC mode. (!14849) +#### RPCs + +- Add state override option to `eth_call` RPC, similar to go-ethereum. + Limited to fields `balance`, `nonce` and `code`. (!14708) + ## Version 0.2 (2024-09-05) This release introduces a number of quality of life improvements for operators diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml index 05293931206dafae0ca2339fc6e8073a6ee11b45..e38b1ff9a5ac7ec0725fe480d5781224f949680f 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.ml @@ -269,6 +269,10 @@ let encode_u256_le (Qty n) = let bits = Z.to_bits n |> Bytes.of_string in pad_to_n_bytes_le bits 32 +let encode_u64_le (Qty n) = + let bits = Z.to_bits n |> Bytes.of_string in + pad_to_n_bytes_le bits 4 + type transaction_log = { address : address; topics : hash list; @@ -1054,3 +1058,26 @@ module From_rlp = struct Rlp.decode_value (fun b -> Result_syntax.return @@ Z.to_int @@ Z.of_bits @@ Bytes.to_string b) end + +type state_account_override = { + balance : quantity option; + nonce : quantity option; + code : hex option; +} + +type state_override = state_account_override AddressMap.t + +let state_account_override_encoding = + let open Data_encoding in + conv + (fun {balance; nonce; code} -> (balance, nonce, code)) + (fun (balance, nonce, code) -> {balance; nonce; code}) + (obj3 + (opt "balance" quantity_encoding) + (opt "nonce" quantity_encoding) + (opt "code" hex_encoding)) + +let state_override_empty = AddressMap.empty + +let state_override_encoding = + AddressMap.associative_array_encoding state_account_override_encoding diff --git a/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli b/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli index 415a414769c96a0e32ed7cc1e0a918370c741a7b..e2eb6532cd97933f55c57b7b596a61748b916050 100644 --- a/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli +++ b/etherlink/bin_node/lib_dev/encodings/ethereum_types.mli @@ -95,6 +95,8 @@ val decode_number_be : bytes -> quantity val encode_u256_le : quantity -> bytes +val encode_u64_le : quantity -> bytes + (** [u16_to_bytes n] Translate an int in a binary string of two bytes (little endian). Ints greater than 2 bytes are truncated. *) val u16_to_bytes : int -> string @@ -230,6 +232,18 @@ type txpool = { val txpool_encoding : txpool Data_encoding.t +type state_account_override = { + balance : quantity option; + nonce : quantity option; + code : hex option; +} + +type state_override = state_account_override AddressMap.t + +val state_override_encoding : state_override Data_encoding.t + +val state_override_empty : state_override + val block_from_rlp : bytes -> block module Block_parameter : sig diff --git a/etherlink/bin_node/lib_dev/evm_context_based_reader.ml b/etherlink/bin_node/lib_dev/evm_context_based_reader.ml deleted file mode 100644 index 40a15ff4747fb17b843fd02700b76de6f25544d0..0000000000000000000000000000000000000000 --- a/etherlink/bin_node/lib_dev/evm_context_based_reader.ml +++ /dev/null @@ -1,29 +0,0 @@ -(*****************************************************************************) -(* *) -(* SPDX-License-Identifier: MIT *) -(* Copyright (c) 2023 Nomadic Labs *) -(* *) -(*****************************************************************************) - -type state = Evm_state.t - -let get_state ?(block = Ethereum_types.Block_parameter.(Block_parameter Latest)) - () = - Evm_context.get_evm_state block - -let read state path = - let open Lwt_result_syntax in - let*! res = Evm_state.inspect state path in - return res - -let subkeys state path = - let open Lwt_result_syntax in - let*! res = Evm_state.subkeys state path in - return res - -let simulate_and_read state ~input = - let open Lwt_result_syntax in - let* raw_insights = Evm_context.execute_and_inspect state input in - match raw_insights with - | [Some bytes] -> return bytes - | _ -> Error_monad.failwith "Invalid insights format" diff --git a/etherlink/bin_node/lib_dev/evm_ro_context.ml b/etherlink/bin_node/lib_dev/evm_ro_context.ml index d69bb7e7fa83ebf8eba684c9306c01005567d4a6..166e5eaaf7418bcca689bfa8211e71e73047cf1e 100644 --- a/etherlink/bin_node/lib_dev/evm_ro_context.ml +++ b/etherlink/bin_node/lib_dev/evm_ro_context.ml @@ -195,7 +195,7 @@ struct module SimulatorBackend = struct include Reader - let simulate_and_read simulate_state ~input = + let simulate_and_read ?state_override simulate_state ~input = let open Lwt_result_syntax in let config = Config.config @@ -205,6 +205,9 @@ struct ~destination:Ctxt.ctxt.smart_rollup_address () in + let*! simulate_state = + State_override.update_accounts state_override simulate_state + in let* raw_insights = Evm_state.execute_and_inspect ~config diff --git a/etherlink/bin_node/lib_dev/rollup_node.ml b/etherlink/bin_node/lib_dev/rollup_node.ml index b32148b2f870dc410e95f69a8e15f485ad377b6f..c7230502ab64e1f83972c829c66245f1ccf0371a 100644 --- a/etherlink/bin_node/lib_dev/rollup_node.ml +++ b/etherlink/bin_node/lib_dev/rollup_node.ml @@ -131,7 +131,7 @@ end) : Services_backend_sig.Backend = struct module SimulatorBackend = struct include Reader - let simulate_and_read _state ~input = + let simulate_and_read ?state_override:_ _state ~input = let open Lwt_result_syntax in let* json = call_service diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.ml b/etherlink/bin_node/lib_dev/rpc_encodings.ml index 88dd4e8eb1e84ab8ccf59b405b74d7c82a31440b..24af4c83bf6dd4ea1e97344b7f73c58f3f8679e1 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.ml +++ b/etherlink/bin_node/lib_dev/rpc_encodings.ml @@ -555,11 +555,46 @@ end module Eth_call = struct open Ethereum_types - type input = call * Block_parameter.extended + type input = call * Block_parameter.extended * state_override type output = hash - let input_encoding = encoding_with_optional_extended_block_param call_encoding + let default_block = Ethereum_types.Block_parameter.(Block_parameter Latest) + + let default_state_override = AddressMap.empty + + let input_encoding = + let open Data_encoding in + union + [ + case + ~title:"Only call" + (Tag 0) + (tup1 call_encoding) + (fun (c, _, _) -> Some c) + (fun c -> (c, default_block, default_state_override)); + case + ~title:"Call and block param" + (Tag 1) + (tup2 call_encoding Block_parameter.extended_encoding) + (fun (c, b, _) -> Some (c, b)) + (fun (c, b) -> (c, b, default_state_override)); + case + ~title:"Call and state override" + (Tag 2) + (tup2 call_encoding state_override_encoding) + (fun (c, _, s) -> Some (c, s)) + (fun (c, s) -> (c, default_block, s)); + case + ~title:"Call, block param and state override" + (Tag 3) + (tup3 + call_encoding + Block_parameter.extended_encoding + state_override_encoding) + (fun (c, b, s) -> Some (c, b, s)) + (fun (c, b, s) -> (c, b, s)); + ] let output_encoding = hash_encoding diff --git a/etherlink/bin_node/lib_dev/rpc_encodings.mli b/etherlink/bin_node/lib_dev/rpc_encodings.mli index 46048e97ae18eab0835570c3206abcc00fcd3e2f..ac1669296c427c9d4eca12828907afe17fb6b050 100644 --- a/etherlink/bin_node/lib_dev/rpc_encodings.mli +++ b/etherlink/bin_node/lib_dev/rpc_encodings.mli @@ -235,7 +235,9 @@ module Send_raw_transaction : module Eth_call : METHOD with type input = - Ethereum_types.call * Ethereum_types.Block_parameter.extended + Ethereum_types.call + * Ethereum_types.Block_parameter.extended + * Ethereum_types.state_override and type output = Ethereum_types.hash module Get_estimate_gas : diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index 619e168be6a78ee7435576e4ea2ded8d70dbe80a..35712ce601198aa3a865428513c60f11920eb533 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -607,8 +607,10 @@ let dispatch_request (rpc : Configuration.rpc) (config : Configuration.t) in build_with_input ~f module_ parameters | Eth_call.Method -> - let f (call, block_param) = - let* call_result = Backend_rpc.simulate_call call block_param in + let f (call, block_param, state_override) = + let* call_result = + Backend_rpc.simulate_call call block_param state_override + in match call_result with | Ok (Ok {value = Some value; gas_used = _}) -> rpc_ok value | Ok (Ok {value = None; gas_used = _}) -> diff --git a/etherlink/bin_node/lib_dev/services_backend_sig.ml b/etherlink/bin_node/lib_dev/services_backend_sig.ml index 3f67560aaede669c36306093b19146051455ffac..65d45885caa42856c90ec436ca6380a7725c7c79 100644 --- a/etherlink/bin_node/lib_dev/services_backend_sig.ml +++ b/etherlink/bin_node/lib_dev/services_backend_sig.ml @@ -108,11 +108,13 @@ module type S = sig latest root hash that was applied during an upgrade). *) val kernel_root_hash : unit -> string option tzresult Lwt.t - (** [simulate_call call_info block_param] simulates a call on context - [block_param] and returns the result. *) + (** [simulate_call call_info block_param state_override] simulates a call on + context [block_param] (optionally updated with [state_override]) and + returns the result. *) val simulate_call : Ethereum_types.call -> Ethereum_types.Block_parameter.extended -> + Ethereum_types.state_override -> Simulation.call_result Simulation.simulation_result tzresult Lwt.t (** [estimate_gas call_info] asks the rollup to simulate a call, and diff --git a/etherlink/bin_node/lib_dev/simulator.ml b/etherlink/bin_node/lib_dev/simulator.ml index 6e4151765c9103bcd56eb3353684070a52fbb006..3d81105a0013ee184f5739f9a73fd67c7c70283c 100644 --- a/etherlink/bin_node/lib_dev/simulator.ml +++ b/etherlink/bin_node/lib_dev/simulator.ml @@ -10,20 +10,25 @@ module type SimulationBackend = sig include Durable_storage.READER val simulate_and_read : - state -> input:Simulation.Encodings.simulate_input -> bytes tzresult Lwt.t + ?state_override:Ethereum_types.state_override -> + state -> + input:Simulation.Encodings.simulate_input -> + bytes tzresult Lwt.t end (* This value is a hard maximum used by estimateGas. Set at Int64.max_int / 2 *) let max_gas_limit = Z.of_int64 0x3FFFFFFFFFFFFFFFL module Make (SimulationBackend : SimulationBackend) = struct - let call_simulation ~log_file ~input_encoder ~input simulation_state = + let call_simulation ?(state_override = Ethereum_types.state_override_empty) + ~log_file ~input_encoder ~input simulation_state = let open Lwt_result_syntax in let*? messages = input_encoder input in let insight_requests = [Simulation.Encodings.Durable_storage_key ["evm"; "simulation_result"]] in SimulationBackend.simulate_and_read + ~state_override simulation_state ~input: { @@ -65,12 +70,13 @@ module Make (SimulationBackend : SimulationBackend) = struct return `V0 else return `V1 - let simulate_call call block_param = + let simulate_call call block_param state_override = let open Lwt_result_syntax in let* simulation_state = SimulationBackend.get_state ~block:block_param () in let* simulation_version = simulation_version simulation_state in let* bytes = call_simulation + ~state_override simulation_state ~log_file:"simulate_call" ~input_encoder:Simulation.encode diff --git a/etherlink/bin_node/lib_dev/state_override.ml b/etherlink/bin_node/lib_dev/state_override.ml new file mode 100644 index 0000000000000000000000000000000000000000..9d4a9bd7c73fb273a00b7341cfa5e23a7627aa91 --- /dev/null +++ b/etherlink/bin_node/lib_dev/state_override.ml @@ -0,0 +1,32 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +let durable_balance v = v |> Ethereum_types.encode_u256_le |> Bytes.to_string + +let durable_nonce v = v |> Ethereum_types.encode_u64_le |> Bytes.to_string + +let durable_code = Ethereum_types.hex_to_bytes + +let update_account address {Ethereum_types.balance; nonce; code} state = + let open Durable_storage_path in + let open Lwt_syntax in + let update v_opt key encode state = + match v_opt with + | None -> Lwt_syntax.return state + | Some v -> Evm_state.modify ~key ~value:(encode v) state + in + let* state = + update balance (Accounts.balance address) durable_balance state + in + let* state = update nonce (Accounts.nonce address) durable_nonce state in + let* state = update code (Accounts.code address) durable_code state in + return state + +let update_accounts state_override state = + match state_override with + | None -> Lwt_syntax.return state + | Some so -> Ethereum_types.AddressMap.fold_s update_account so state diff --git a/etherlink/bin_node/lib_dev/state_override.mli b/etherlink/bin_node/lib_dev/state_override.mli new file mode 100644 index 0000000000000000000000000000000000000000..470131e9857196364a9b430febc77a8a4d374069 --- /dev/null +++ b/etherlink/bin_node/lib_dev/state_override.mli @@ -0,0 +1,9 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +val update_accounts : + Ethereum_types.state_override option -> Evm_state.t -> Evm_state.t Lwt.t diff --git a/etherlink/kernel_evm/solidity_examples/state_override_tester.sol b/etherlink/kernel_evm/solidity_examples/state_override_tester.sol new file mode 100644 index 0000000000000000000000000000000000000000..c31bc544624a4a3a8c86e41f379a159919e9583b --- /dev/null +++ b/etherlink/kernel_evm/solidity_examples/state_override_tester.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +contract Child { + + constructor() payable { + } +} + +contract StateOverrideTester { + + int private count = 42; + + function getBalance() public view returns (uint256) { + return msg.sender.balance; + } + + function create() public + returns (address addr) + { + Child child = new Child(); + return address(child); + } +} diff --git a/etherlink/kernel_evm/solidity_examples/state_override_tester_readable.sol b/etherlink/kernel_evm/solidity_examples/state_override_tester_readable.sol new file mode 100644 index 0000000000000000000000000000000000000000..1eebcb578686e8d3a5b62e6c00e8dbc1227f7d01 --- /dev/null +++ b/etherlink/kernel_evm/solidity_examples/state_override_tester_readable.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +contract StateOverrideTester { + int private count = 42; + + function getBalance() public view returns (uint256) { + return msg.sender.balance; + } + + function getCount() public view returns (int) { + return count; + } +} diff --git a/etherlink/tezt/lib/cast.ml b/etherlink/tezt/lib/cast.ml index f2cde7dcb6302c60cd5f8c4371200947045b3059..060294f386df264f2e6626283981e89a3ae2d4b4 100644 --- a/etherlink/tezt/lib/cast.ml +++ b/etherlink/tezt/lib/cast.ml @@ -83,3 +83,6 @@ let gen_wallets ~number () = | None -> [] in return wallets + +let calldata ?(args = []) signature = + spawn_command_and_read_string ("calldata" :: signature :: args) diff --git a/etherlink/tezt/lib/cast.mli b/etherlink/tezt/lib/cast.mli index e359db4439bf014de5ef2ae14a15b12105faa54d..b4c43b6f35f16a9bbdd44f5898fbca0e8360c41a 100644 --- a/etherlink/tezt/lib/cast.mli +++ b/etherlink/tezt/lib/cast.mli @@ -55,3 +55,6 @@ type wallet = {address : string; private_key : string} generates [number] random wallets and returns a list of [wallet] structs. This call does not store anything in the file system. *) val gen_wallets : number:int -> unit -> wallet list Lwt.t + +(** [calldata ?args signature] returns the encoded calldata. *) +val calldata : ?args:string list -> string -> string Lwt.t diff --git a/etherlink/tezt/lib/solidity_contracts.ml b/etherlink/tezt/lib/solidity_contracts.ml index f6a75539b433491c5e54e3e2f50dcc25b14ed4f4..2cbcccf4aba8836b8fe0035c03e295b1720ee298 100644 --- a/etherlink/tezt/lib/solidity_contracts.ml +++ b/etherlink/tezt/lib/solidity_contracts.ml @@ -10,7 +10,12 @@ open Helpers -type contract = {label : string; abi : string; bin : string} +type contract = { + label : string; + abi : string; + bin : string; + deployed_bin : string option; +} let solidity_contracts_path = "etherlink/kernel_evm/solidity_examples" @@ -37,7 +42,8 @@ let generate_json_string ~label ~contract ~path ~evm_version = "*": { "%s": [ "abi", - "evm.bytecode.object" + "evm.bytecode.object", + "evm.deployedBytecode.object" ] } } @@ -86,12 +92,21 @@ let compile_contract ~source ~label ~contract ~evm_version = json |-> "contracts" |-> label |-> contract |-> "evm" |-> "bytecode" |-> "object" |> as_string) in + let deployed_bin = + JSON.( + json |-> "contracts" |-> label |-> contract |-> "evm" + |-> "deployedBytecode" |-> "object" |> as_string) + in let abi_file = Tezt.Temp.file (label ^ ".abi") in let bin_file = Tezt.Temp.file (label ^ ".bin") in + let deployed_file = Tezt.Temp.file (label ^ ".deployed.bin") in + JSON.encode_to_file abi_file abi ; Tezt.Base.write_file bin_file ~contents:bin ; - return {label; abi = abi_file; bin = bin_file} + Tezt.Base.write_file deployed_file ~contents:deployed_bin ; + return + {label; abi = abi_file; bin = bin_file; deployed_bin = Some deployed_file} (** The info for the "storage.sol" contract. *) let simple_storage () = @@ -284,6 +299,7 @@ let block_constants = label = "blocks_constants"; abi = kernel_inputs_path ^ "/block_constants.abi"; bin = kernel_inputs_path ^ "/block_constants.bin"; + deployed_bin = None; } (** The info for the "call_withdrawal.sol" contract. @@ -293,6 +309,7 @@ let call_withdrawal = label = "call_withdrawal"; abi = kernel_inputs_path ^ "/call_withdrawal.abi"; bin = kernel_inputs_path ^ "/call_withdrawal.bin"; + deployed_bin = None; } (** The info for the "callcode_withdrawal.sol" contract. @@ -302,6 +319,7 @@ let callcode_withdrawal = label = "callcode_withdrawal"; abi = kernel_inputs_path ^ "/callcode_withdrawal.abi"; bin = kernel_inputs_path ^ "/callcode_withdrawal.bin"; + deployed_bin = None; } let gas_left () = @@ -345,3 +363,17 @@ let precompiles () = ~label:"precompiles" ~contract:"PrecompileCaller" ~evm_version:"shanghai" + +let state_override_tester () = + compile_contract + ~source:(solidity_contracts_path ^ "/state_override_tester.sol") + ~label:"state_override_tester" + ~contract:"StateOverrideTester" + ~evm_version:"shanghai" + +let state_override_tester_readable () = + compile_contract + ~source:(solidity_contracts_path ^ "/state_override_tester_readable.sol") + ~label:"state_override_tester_readable" + ~contract:"StateOverrideTester" + ~evm_version:"shanghai" diff --git a/etherlink/tezt/tests/dune b/etherlink/tezt/tests/dune index 576213a56d735dc00cc264f9e7057cd29261e181..d61803a326b4cd03a1149e5b57a1ffb15daa510d 100644 --- a/etherlink/tezt/tests/dune +++ b/etherlink/tezt/tests/dune @@ -24,7 +24,7 @@ -open Tezt_tezos -open Tezt_tezos.Runnable.Syntax -open Tezt_etherlink) - (modules evm_rollup evm_sequencer validate dal_sequencer)) + (modules evm_rollup evm_sequencer validate dal_sequencer eth_call)) (executable (name main) diff --git a/etherlink/tezt/tests/eth_call.ml b/etherlink/tezt/tests/eth_call.ml new file mode 100644 index 0000000000000000000000000000000000000000..0075b7a1b3cf7de5a1070b21c4b93ebf3e2878d3 --- /dev/null +++ b/etherlink/tezt/tests/eth_call.ml @@ -0,0 +1,246 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Etherlink: EVM simulator + Requirement: make -f etherlink.mk build + npm install eth-cli + # Install cast or foundry (see: https://book.getfoundry.sh/getting-started/installation) + curl -L https://foundry.paradigm.xyz | bash + foundryup + make octez-evm-node + Invocation: dune exec etherlink/tezt/tests/main.exe -- --file eth_call.ml +*) + +open Setup +open Helpers + +let register ?genesis_timestamp ?bootstrap_accounts ?(kernels = Kernel.all) + ?preimages_dir ?maximum_allowed_ticks ?enable_fa_bridge ?history_mode + ?additional_uses ~title ~tags body protocols = + register_test_for_kernels + ~__FILE__ + ~time_between_blocks:Nothing + ?genesis_timestamp + ?bootstrap_accounts + ~kernels + ?preimages_dir + ?maximum_allowed_ticks + ?enable_fa_bridge + ?additional_uses + ?history_mode + ~enable_dal:false + ~threshold_encryption:false + ~title + ~tags + body + protocols + +let test_call_state_override_balance = + register + ~kernels:[Latest] (* Not a kernel specific test. *) + ~tags:["evm"; "state_override"; "balance_override"; "eth_call"] + ~title:"Can override balance in eth_call" + @@ fun {sequencer; _} _protocol -> + (* + This test checks that the simulation allows balance override. + To do so we deploy a contract which returns the balance of the message + sender, and call it with a non-sensical address. + *) + let* constant = Solidity_contracts.state_override_tester () in + let* () = Eth_cli.add_abi ~label:constant.label ~abi:constant.abi () in + (* Deploy the contract. *) + let* contract, _tx_hash = + send_transaction_to_sequencer + (fun () -> + Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) + sequencer + in + let caller_address = "0x0123456789012345678901234567890123456789" in + let* calldata = Cast.calldata "getBalance()" in + let call = + `O + [ + ("from", `String caller_address); + ("to", `String contract); + ("data", `String calldata); + ] + in + let* call_result = + Evm_node.( + call_evm_rpc sequencer {method_ = "eth_call"; parameters = `A [call]}) + in + Check.( + (Evm_node.extract_result call_result + |> JSON.as_string + = "0x0000000000000000000000000000000000000000000000000000000000000000") + string) + ~error_msg:"Expected result %R but got %L " ; + let override_balance = + `O [(caller_address, `O [("balance", `String "0xffff")])] + in + let* call_result = + Evm_node.( + call_evm_rpc + sequencer + {method_ = "eth_call"; parameters = `A [call; override_balance]}) + (* we omit the block paramater to test the encoding *) + in + Check.( + (Evm_node.extract_result call_result + |> JSON.as_string + = "0x000000000000000000000000000000000000000000000000000000000000ffff") + string) + ~error_msg:"Expected result %R but got %L " ; + + unit + +let test_call_state_override_code = + register + ~kernels:[Latest] (* Not a kernel specific test. *) + ~tags:["evm"; "state_override"; "code_override"; "eth_call"] + ~title:"Can override code in eth_call" + @@ fun {sequencer; _} _protocol -> + (* + This test checks that the simulation allows code override. + To do so we deploy a contract without any function, and call it with an + alternative code that does have a function. + *) + let* constant = Solidity_contracts.state_override_tester () in + let* constant_readable = + Solidity_contracts.state_override_tester_readable () + in + let* () = Eth_cli.add_abi ~label:constant.label ~abi:constant.abi () in + (* Deploy the contract. *) + let* contract, _tx_hash = + send_transaction_to_sequencer + (fun () -> + Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:constant.abi + ~bin:constant.bin) + sequencer + in + let bytecode_accessor = + read_file (Option.value ~default:"" constant_readable.deployed_bin) + in + let* calldata = Cast.calldata "getCount()" in + let call = `O [("to", `String contract); ("data", `String calldata)] in + + (* Check that the contract normaly doesn't allow "getCount()" *) + let* call_result = + Evm_node.( + call_evm_rpc + sequencer + {method_ = "eth_call"; parameters = `A [call; `String "latest"]}) + in + Check.( + (Evm_node.extract_error_message call_result + |> JSON.as_string = "execution reverted") + string) + ~error_msg:"Expected error %R but got %L " ; + + (* try again with an override *) + let override_code = + `O [(contract, `O [("code", `String ("0x" ^ bytecode_accessor))])] + in + let* call_result = + Evm_node.( + call_evm_rpc + sequencer + { + method_ = "eth_call"; + parameters = `A [call; `String "latest"; override_code]; + }) + in + Check.( + (Evm_node.extract_result call_result + |> JSON.as_string + = "0x000000000000000000000000000000000000000000000000000000000000002a") + string) + ~error_msg:"Expected result %R but got %L " ; + + unit + +let test_call_state_override_nonce = + register + ~kernels:[Latest] (* Not a kernel specific test. *) + ~tags:["evm"; "state_override"; "nonce_override"; "eth_call"] + ~title:"Can override nonce in eth_call" + @@ fun {sequencer; _} _protocol -> + (* + This test checks that the simulation allows nonce override. + To do so we deploy a contract that creates a contract and returns the + address of the new contract, and call it twice with different nonce. + The addresses should be different. + *) + let* factory = Solidity_contracts.state_override_tester () in + let* () = Eth_cli.add_abi ~label:factory.label ~abi:factory.abi () in + (* Deploy the contract. *) + let* contract, _tx_hash = + send_transaction_to_sequencer + (fun () -> + Eth_cli.deploy + ~source_private_key:Eth_account.bootstrap_accounts.(0).private_key + ~endpoint:(Evm_node.endpoint sequencer) + ~abi:factory.abi + ~bin:factory.bin) + sequencer + in + + let* calldata = Cast.calldata "create()" in + let caller_address = Eth_account.bootstrap_accounts.(0).address in + let call = + `O + [ + ("from", `String caller_address); + ("to", `String contract); + ("data", `String calldata); + ] + in + + (* Call a first time to have an address *) + let* call_result = + Evm_node.( + call_evm_rpc + sequencer + {method_ = "eth_call"; parameters = `A [call; `String "latest"]}) + in + let addr1 = Evm_node.extract_result call_result |> JSON.as_string in + + (* try again with an override *) + let override_code = `O [(contract, `O [("nonce", `String "0x2a")])] in + let* call_result = + Evm_node.( + call_evm_rpc + sequencer + { + method_ = "eth_call"; + parameters = `A [call; `String "latest"; override_code]; + }) + in + let addr2 = Evm_node.extract_result call_result |> JSON.as_string in + + (* the two address were calculated with different nonce so should be different + (hopefully) *) + Check.((addr1 <> addr2) string) + ~error_msg:"Address should have been different but got %R and %L" ; + + unit + +let protocols = Protocol.all + +let () = + test_call_state_override_code protocols ; + test_call_state_override_nonce protocols ; + test_call_state_override_balance protocols diff --git a/manifest/product_etherlink.ml b/manifest/product_etherlink.ml index 8d389b2b44753209973f84ef40d576370be15d39..727fe77363b9ecd3e9d544cc651a3e1127537869 100644 --- a/manifest/product_etherlink.ml +++ b/manifest/product_etherlink.ml @@ -185,7 +185,7 @@ let _octez_evm_node_tests = let _tezt_etherlink = tezt - ["evm_rollup"; "evm_sequencer"; "validate"; "dal_sequencer"] + ["evm_rollup"; "evm_sequencer"; "validate"; "dal_sequencer"; "eth_call"] ~path:"etherlink/tezt/tests" ~opam:"tezt-etherlink" ~synopsis:"Tezt integration tests for Etherlink"