From 3fc85e43ca4f871106bb666e98912923be5be4a3 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Tue, 16 May 2023 11:48:52 +0200 Subject: [PATCH 1/4] EVM/Proxy: batched RPC requests --- src/bin_evm_proxy/services.ml | 146 ++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 59 deletions(-) diff --git a/src/bin_evm_proxy/services.ml b/src/bin_evm_proxy/services.ml index 5460b1ee9220..5950a050df15 100644 --- a/src/bin_evm_proxy/services.ml +++ b/src/bin_evm_proxy/services.ml @@ -37,11 +37,33 @@ let version dir = Directory.register0 dir version_service (fun () () -> Lwt.return_ok Tezos_version.Bin_version.version_string) +(* The proxy server can either take a single request or multiple requests at + once. *) +type 'a request = Singleton of 'a | Batch of 'a list + +let request_encoding kind = + Data_encoding.( + union + [ + case + ~title:"singleton" + (Tag 0) + kind + (function Singleton i -> Some i | _ -> None) + (fun i -> Singleton i); + case + ~title:"batch" + (Tag 1) + (list kind) + (function Batch i -> Some i | _ -> None) + (fun i -> Batch i); + ]) + let dispatch_service = Service.post_service ~query:Query.empty - ~input:Input.encoding - ~output:Output.encoding + ~input:(request_encoding Input.encoding) + ~output:(request_encoding Output.encoding) Path.(root) let get_block ~full_transaction_object block_param @@ -52,64 +74,70 @@ let get_block ~full_transaction_object block_param | Latest | Earliest | Pending -> Rollup_node_rpc.current_block ~full_transaction_object -let dispatch ((module Rollup_node_rpc : Rollup_node.S), smart_rollup_address) - dir = - Directory.register0 dir dispatch_service (fun () (input, id) -> +let dispatch_input + ((module Rollup_node_rpc : Rollup_node.S), smart_rollup_address) (input, id) + = + let open Lwt_result_syntax in + let* output = + match input with + | Accounts.Input _ -> return (Accounts.Output (Ok [])) + | Network_id.Input _ -> return (Network_id.Output (Ok Mockup.net_version)) + | Chain_id.Input _ -> return (Chain_id.Output (Ok Mockup.chain_id)) + | Get_balance.Input (Some (address, _block_param)) -> + let* balance = Rollup_node_rpc.balance address in + return (Get_balance.Output (Ok balance)) + | Block_number.Input _ -> + let* block_number = Rollup_node_rpc.current_block_number () in + return (Block_number.Output (Ok block_number)) + | Get_block_by_number.Input (Some (block_param, full_transaction_object)) -> + let* block = + get_block + ~full_transaction_object + block_param + (module Rollup_node_rpc) + in + return (Get_block_by_number.Output (Ok block)) + | Get_block_by_number.Input None -> + return (Get_block_by_number.Output (Ok Mockup.(block ()))) + | Get_block_by_hash.Input _ -> + return (Get_block_by_hash.Output (Ok Mockup.(block ()))) + | Get_code.Input _ -> return (Get_code.Output (Ok Mockup.code)) + | Gas_price.Input _ -> return (Gas_price.Output (Ok Mockup.gas_price)) + | Get_transaction_count.Input (Some (address, _)) -> + let* nonce = Rollup_node_rpc.nonce address in + return (Get_transaction_count.Output (Ok nonce)) + | Get_transaction_receipt.Input (Some tx_hash) -> + let* receipt = + let*! res = Rollup_node_rpc.transaction_receipt tx_hash in + match res with Ok x -> return_some x | Error _ -> return_none + in + return (Get_transaction_receipt.Output (Ok receipt)) + | Get_transaction_by_hash.Input _ -> + return (Get_transaction_by_hash.Output (Ok Mockup.transaction_object)) + | Send_raw_transaction.Input (Some tx_raw) -> + let* tx_hash = + Rollup_node_rpc.inject_raw_transaction ~smart_rollup_address tx_raw + in + return (Send_raw_transaction.Output (Ok tx_hash)) + | Send_transaction.Input _ -> + return (Send_transaction.Output (Ok Mockup.transaction_hash)) + | Eth_call.Input _ -> return (Eth_call.Output (Ok Mockup.call)) + | Get_estimate_gas.Input _ -> + return (Get_estimate_gas.Output (Ok Mockup.gas_price)) + | _ -> Error_monad.failwith "Unsupported method\n%!" + in + return (output, id) + +let dispatch ctx dir = + Directory.register0 dir dispatch_service (fun () input -> let open Lwt_result_syntax in - let* output = - match input with - | Accounts.Input _ -> return (Accounts.Output (Ok [])) - | Network_id.Input _ -> - return (Network_id.Output (Ok Mockup.net_version)) - | Chain_id.Input _ -> return (Chain_id.Output (Ok Mockup.chain_id)) - | Get_balance.Input (Some (address, _block_param)) -> - let* balance = Rollup_node_rpc.balance address in - return (Get_balance.Output (Ok balance)) - | Block_number.Input _ -> - let* block_number = Rollup_node_rpc.current_block_number () in - return (Block_number.Output (Ok block_number)) - | Get_block_by_number.Input - (Some (block_param, full_transaction_object)) -> - let* block = - get_block - ~full_transaction_object - block_param - (module Rollup_node_rpc) - in - return (Get_block_by_number.Output (Ok block)) - | Get_block_by_number.Input None -> - return (Get_block_by_number.Output (Ok (Mockup.block ()))) - | Get_block_by_hash.Input _ -> - return (Get_block_by_hash.Output (Ok (Mockup.block ()))) - | Get_code.Input _ -> return (Get_code.Output (Ok Mockup.code)) - | Gas_price.Input _ -> return (Gas_price.Output (Ok Mockup.gas_price)) - | Get_transaction_count.Input (Some (address, _)) -> - let* nonce = Rollup_node_rpc.nonce address in - return (Get_transaction_count.Output (Ok nonce)) - | Get_transaction_receipt.Input (Some tx_hash) -> - let* receipt = - let*! res = Rollup_node_rpc.transaction_receipt tx_hash in - match res with Ok x -> return_some x | Error _ -> return_none - in - return (Get_transaction_receipt.Output (Ok receipt)) - | Get_transaction_by_hash.Input _ -> - return - (Get_transaction_by_hash.Output (Ok Mockup.transaction_object)) - | Send_raw_transaction.Input (Some tx_raw) -> - let* tx_hash = - Rollup_node_rpc.inject_raw_transaction - ~smart_rollup_address - tx_raw - in - return (Send_raw_transaction.Output (Ok tx_hash)) - | Send_transaction.Input _ -> - return (Send_transaction.Output (Ok Mockup.transaction_hash)) - | Eth_call.Input _ -> return (Eth_call.Output (Ok Mockup.call)) - | Get_estimate_gas.Input _ -> - return (Get_estimate_gas.Output (Ok Mockup.gas_price)) - | _ -> Error_monad.failwith "Unsupported method\n%!" - in - return (output, id)) + match input with + | Singleton input -> + let+ output = dispatch_input ctx input in + Singleton output + | Batch inputs -> + let+ outputs = List.map_es (dispatch_input ctx) inputs in + Batch outputs) let directory (rollup_node_config : (module Rollup_node.S) * string) = Directory.empty |> version |> dispatch rollup_node_config -- GitLab From aae9fbd32fd32726103c32d3108954ea779841c3 Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Sun, 21 May 2023 18:24:37 +0200 Subject: [PATCH 2/4] EVM/Proxy: update Tezt interface to handle batch request --- tezt/lib_tezos/evm_proxy_server.ml | 20 +++++++++++++++++--- tezt/lib_tezos/evm_proxy_server.mli | 13 ++++++++++--- tezt/tests/evm_rollup.ml | 3 +-- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/tezt/lib_tezos/evm_proxy_server.ml b/tezt/lib_tezos/evm_proxy_server.ml index 0461c94efc43..7b7649e2f310 100644 --- a/tezt/lib_tezos/evm_proxy_server.ml +++ b/tezt/lib_tezos/evm_proxy_server.ml @@ -134,7 +134,9 @@ let init ?runner ?rpc_addr ?rpc_port rollup_node = let* () = run proxy_server in return proxy_server -let request method_ parameters : JSON.t = +type request = {method_ : string; parameters : JSON.u} + +let request_to_JSON {method_; parameters} : JSON.u = `O [ ("jsonrpc", `String "2.0"); @@ -142,8 +144,20 @@ let request method_ parameters : JSON.t = ("params", parameters); ("id", `String "0"); ] + +let build_request request = + request_to_JSON request |> JSON.annotate ~origin:"evm_proxy_server" + +let batch_requests requests = + `A (List.map request_to_JSON requests) |> JSON.annotate ~origin:"evm_proxy_server" -let call_evm_rpc proxy_server ~method_ ~parameters = +(* We keep both encoding (with a single object or an array of objects) and both + function on purpose, to ensure both encoding are supported by the server. *) +let call_evm_rpc proxy_server request = + let endpoint = endpoint proxy_server in + RPC.Curl.post endpoint (build_request request) |> Runnable.run + +let batch_evm_rpc proxy_server requests = let endpoint = endpoint proxy_server in - RPC.Curl.post endpoint (request method_ parameters) |> Runnable.run + RPC.Curl.post endpoint (batch_requests requests) |> Runnable.run diff --git a/tezt/lib_tezos/evm_proxy_server.mli b/tezt/lib_tezos/evm_proxy_server.mli index ca99775647fd..9924ce624077 100644 --- a/tezt/lib_tezos/evm_proxy_server.mli +++ b/tezt/lib_tezos/evm_proxy_server.mli @@ -58,6 +58,13 @@ val spawn_run : t -> Process.t [proxy_server]. *) val endpoint : t -> string -(** [call_evm_rpc proxy_server ~method_ ~parameters] sends a JSON-RPC request to - the [proxy_server], for the given [method_] and [parameters]. *) -val call_evm_rpc : t -> method_:string -> parameters:JSON.u -> JSON.t Lwt.t +(** JSON-RPC request. *) +type request = {method_ : string; parameters : JSON.u} + +(** [call_evm_rpc proxy_server ~request] sends a JSON-RPC request to the + [proxy_server], for the given [request]. *) +val call_evm_rpc : t -> request -> JSON.t Lwt.t + +(** [batch_evm_rpc proxy_server ~requests] sends multiple JSON-RPC requests to the + [proxy_server], for the given [requests]. *) +val batch_evm_rpc : t -> request list -> JSON.t Lwt.t diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index c3580cf138fb..5a34f4ab769f 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -62,8 +62,7 @@ let get_transaction_count proxy_server address = let* transaction_count = Evm_proxy_server.call_evm_rpc proxy_server - ~method_:"eth_getTransactionCount" - ~parameters + {method_ = "eth_getTransactionCount"; parameters} in return JSON.(transaction_count |-> "result" |> as_int64) -- GitLab From 32cd6f6850f881e9ed2f6914119d2a6e5b29bbfb Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Mon, 22 May 2023 09:16:56 +0200 Subject: [PATCH 3/4] EVM/Proxy: test batch requests --- tezt/tests/evm_rollup.ml | 96 +++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index 5a34f4ab769f..50ab9496a9f4 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -57,15 +57,6 @@ let evm_proxy_server_version proxy_server = let get_version_url = endpoint ^ "/version" in RPC.Curl.get get_version_url -let get_transaction_count proxy_server address = - let parameters : JSON.u = `A [`String address; `String "latest"] in - let* transaction_count = - Evm_proxy_server.call_evm_rpc - proxy_server - {method_ = "eth_getTransactionCount"; parameters} - in - return JSON.(transaction_count |-> "result" |> as_int64) - (** [next_evm_level ~sc_rollup_node ~node ~client] moves [sc_rollup_node] to the [node]'s next level. *) let next_evm_level ~sc_rollup_node ~node ~client = @@ -291,23 +282,106 @@ let test_rpc_getBlockByNumber = ~error_msg:"Unexpected list of transactions, should be %%R, but got %%L" ; unit +let transaction_count_request address = + Evm_proxy_server. + { + method_ = "eth_getTransactionCount"; + parameters = `A [`String address; `String "latest"]; + } + +let get_transaction_count proxy_server address = + let* transaction_count = + Evm_proxy_server.call_evm_rpc + proxy_server + (transaction_count_request address) + in + return JSON.(transaction_count |-> "result" |> as_int64) + let test_rpc_getTransactionCount = Protocol.register_test ~__FILE__ ~tags:["evm"; "get_transaction_count"] ~title:"RPC method eth_getTransactionCount" @@ fun protocol -> + let* {node; client; sc_rollup_node; evm_proxy_server; _} = + setup_evm_kernel protocol + in + (* Force a level to got past the genesis block *) + let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* transaction_count = + get_transaction_count + evm_proxy_server + Eth_account.bootstrap_accounts.(0).address + in + Check.((transaction_count = 0L) int64) + ~error_msg:"Expected a nonce of %R, but got %L" ; + unit + +let test_rpc_getTransactionCountBatch = + Protocol.register_test + ~__FILE__ + ~tags:["evm"; "get_transaction_count_as_batch"] + ~title:"RPC method eth_getTransactionCount in batch" + @@ fun protocol -> let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in - (* Force a level to got past the genesis block *) + (* Force a level to get past the genesis block *) let* _level = next_evm_level ~sc_rollup_node ~node ~client in let* transaction_count = get_transaction_count evm_proxy_server Eth_account.bootstrap_accounts.(0).address in + let* transaction_count_batch = + let* transaction_count = + Evm_proxy_server.batch_evm_rpc + evm_proxy_server + [transaction_count_request Eth_account.bootstrap_accounts.(0).address] + in + match JSON.as_list transaction_count with + | [transaction_count] -> + return JSON.(transaction_count |-> "result" |> as_int64) + | _ -> Test.fail "Unexpected result from batching one request" + in + Check.((transaction_count = transaction_count_batch) int64) + ~error_msg:"Nonce from a single request is %L, but got %R from batching it" ; + unit + +let test_rpc_batch = + Protocol.register_test + ~__FILE__ + ~tags:["evm"; "rpc"; "batch"] + ~title:"RPC batch requests" + @@ fun protocol -> + let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in + let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in + (* Force a level to get past the genesis block *) + let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* transaction_count, chain_id = + let transaction_count = + transaction_count_request Eth_account.bootstrap_accounts.(0).address + in + let chain_id = + Evm_proxy_server.{method_ = "eth_chainId"; parameters = `Null} + in + let* results = + Evm_proxy_server.batch_evm_rpc + evm_proxy_server + [transaction_count; chain_id] + in + match JSON.as_list results with + | [transaction_count; chain_id] -> + return + ( JSON.(transaction_count |-> "result" |> as_int64), + JSON.(chain_id |-> "result" |> as_int64) ) + | _ -> Test.fail "Unexpected result from batching two requests" + in Check.((transaction_count = 0L) int64) ~error_msg:"Expected a nonce of %R, but got %L" ; + (* Default chain id for Ethereum custom networks, not chosen randomly. *) + let default_chain_id = 1337L in + Check.((chain_id = default_chain_id) int64) + ~error_msg:"Expected a chain_id of %R, but got %L" ; unit let test_l2_blocks_progression = @@ -423,6 +497,8 @@ let register_evm_proxy_server ~protocols = test_rpc_getBalance protocols ; test_rpc_getBlockByNumber protocols ; test_rpc_getTransactionCount protocols ; + test_rpc_getTransactionCountBatch protocols ; + test_rpc_batch protocols ; test_l2_blocks_progression protocols ; test_l2_transfer protocols ; test_chunked_transaction protocols ; -- GitLab From ba8b9ce901d9b9a29bbaef01e2d73113fd5d88cc Mon Sep 17 00:00:00 2001 From: Pierrick Couderc Date: Tue, 23 May 2023 13:35:17 +0200 Subject: [PATCH 4/4] EVM/Tezt: helper for setup evm and go past genesis --- tezt/tests/evm_rollup.ml | 47 ++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index 50ab9496a9f4..542d17f7c46b 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -151,6 +151,14 @@ let setup_evm_kernel ?(originator_key = Constant.bootstrap1.public_key_hash) evm_proxy_server; } +let setup_past_genesis ?originator_key ?rollup_operator_key protocol = + let* ({node; client; sc_rollup_node; _} as full_setup) = + setup_evm_kernel ?originator_key ?rollup_operator_key protocol + in + (* Force a level to got past the genesis block *) + let* _level = next_evm_level ~sc_rollup_node ~node ~client in + return full_setup + let test_evm_proxy_server_connection = Protocol.register_test ~__FILE__ @@ -230,10 +238,7 @@ let test_rpc_getBalance = ~tags:["evm"; "get_balance"] ~title:"RPC method eth_getBalance" @@ fun protocol -> - let* {node; client; sc_rollup_node; evm_proxy_server; _} = - setup_evm_kernel protocol - in - let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* {evm_proxy_server; _} = setup_past_genesis protocol in let evm_proxy_server_endpoint = Evm_proxy_server.endpoint evm_proxy_server in let* balance = Eth_cli.balance @@ -253,17 +258,8 @@ let test_rpc_getBlockByNumber = ~tags:["evm"; "get_block_by_number"] ~title:"RPC method eth_getBlockByNumber" @@ fun protocol -> - let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in - let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in + let* {evm_proxy_server; _} = setup_past_genesis protocol in let evm_proxy_server_endpoint = Evm_proxy_server.endpoint evm_proxy_server in - let* () = Client.bake_for_and_wait client in - let first_evm_run_level = Node.get_level node in - let* _level = - Sc_rollup_node.wait_for_level - ~timeout:30. - sc_rollup_node - first_evm_run_level - in let* block = Eth_cli.get_block ~block_id:"0" ~endpoint:evm_proxy_server_endpoint in @@ -303,11 +299,7 @@ let test_rpc_getTransactionCount = ~tags:["evm"; "get_transaction_count"] ~title:"RPC method eth_getTransactionCount" @@ fun protocol -> - let* {node; client; sc_rollup_node; evm_proxy_server; _} = - setup_evm_kernel protocol - in - (* Force a level to got past the genesis block *) - let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* {evm_proxy_server; _} = setup_past_genesis protocol in let* transaction_count = get_transaction_count evm_proxy_server @@ -323,10 +315,7 @@ let test_rpc_getTransactionCountBatch = ~tags:["evm"; "get_transaction_count_as_batch"] ~title:"RPC method eth_getTransactionCount in batch" @@ fun protocol -> - let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in - let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in - (* Force a level to get past the genesis block *) - let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* {evm_proxy_server; _} = setup_past_genesis protocol in let* transaction_count = get_transaction_count evm_proxy_server @@ -353,10 +342,7 @@ let test_rpc_batch = ~tags:["evm"; "rpc"; "batch"] ~title:"RPC batch requests" @@ fun protocol -> - let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in - let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in - (* Force a level to get past the genesis block *) - let* _level = next_evm_level ~sc_rollup_node ~node ~client in + let* {evm_proxy_server; _} = setup_past_genesis protocol in let* transaction_count, chain_id = let transaction_count = transaction_count_request Eth_account.bootstrap_accounts.(0).address @@ -415,7 +401,6 @@ let test_l2_deploy = let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in let evm_proxy_server_endpoint = Evm_proxy_server.endpoint evm_proxy_server in - let* _level = next_evm_level ~sc_rollup_node ~node ~client in let sender = Eth_account.bootstrap_accounts.(0) in let* () = Eth_cli.add_abi @@ -434,10 +419,10 @@ let test_l2_deploy = unit let transfer ?data protocol = - let* {node; client; sc_rollup_node; _} = setup_evm_kernel protocol in - let* evm_proxy_server = Evm_proxy_server.init sc_rollup_node in + let* {node; client; sc_rollup_node; evm_proxy_server; _} = + setup_past_genesis protocol + in let evm_proxy_server_endpoint = Evm_proxy_server.endpoint evm_proxy_server in - let* _level = next_evm_level ~sc_rollup_node ~node ~client in let balance account = Eth_cli.balance ~account ~endpoint:evm_proxy_server_endpoint in -- GitLab