From bade04f9ff68878e33e8523aa45bd690d3d1e353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Sat, 17 May 2025 21:35:44 +0200 Subject: [PATCH 1/2] Tezlink/Node: add injection/operations RPC --- etherlink/bin_node/lib_dev/rpc_server.ml | 23 +++++++++++- etherlink/bin_node/lib_dev/services.ml | 5 +-- .../lib_dev/tezlink/tezlink_directory.ml | 22 ++++++++++-- .../lib_dev/tezlink/tezlink_directory.mli | 2 ++ .../lib_dev/tezlink/tezos_services.ml | 10 ++++++ .../bin_node/lib_dev/tezlink/tezos_types.ml | 35 +++++++++++++++++++ .../bin_node/lib_dev/tezlink/tezos_types.mli | 2 ++ 7 files changed, 93 insertions(+), 6 deletions(-) diff --git a/etherlink/bin_node/lib_dev/rpc_server.ml b/etherlink/bin_node/lib_dev/rpc_server.ml index 1776d4b0aaaf..2360109cefbc 100644 --- a/etherlink/bin_node/lib_dev/rpc_server.ml +++ b/etherlink/bin_node/lib_dev/rpc_server.ml @@ -147,7 +147,8 @@ let monitor_performances ~data_dir = let start_public_server (type f) ~(rpc_server_family : f Rpc_types.rpc_server_family) ~l2_chain_id ?delegate_health_check_to ?evm_services ?data_dir validation - (config : Configuration.t) tx_container ctxt = + (config : Configuration.t) + (tx_container : f Services_backend_sig.tx_container) ctxt = let open Lwt_result_syntax in let*! can_start_performance_metrics = Octez_performance_metrics.supports_performance_metrics () @@ -176,10 +177,30 @@ let start_public_server (type f) | Some l2_chain_id -> return l2_chain_id | None -> Backend.chain_id () in + let (Services_backend_sig.Michelson_tx_container (module Tx_container)) + = + tx_container + in return @@ Evm_directory.init_from_resto_directory @@ Tezlink_directory.register_tezlink_services ~l2_chain_id (module Backend.Tezlink) + ~add_operation:(fun op raw -> + (* TODO: https://gitlab.com/tezos/tezos/-/issues/8007 + Validate the operation and use the resulting "next_nonce" *) + let next_nonce = Ethereum_types.Qty op.counter in + let* hash_res = + Tx_container.add + ~next_nonce + op + ~raw_tx:(Ethereum_types.hex_of_bytes raw) + in + let* hash = + match hash_res with + | Ok hash -> return hash + | Error s -> failwith "%s" s + in + return hash) | Single_chain_node_rpc_server EVM | Multichain_sequencer_rpc_server -> return @@ Evm_directory.empty config.experimental_features.rpc_server in diff --git a/etherlink/bin_node/lib_dev/services.ml b/etherlink/bin_node/lib_dev/services.ml index d4a1aaef7db2..660d64e81bda 100644 --- a/etherlink/bin_node/lib_dev/services.ml +++ b/etherlink/bin_node/lib_dev/services.ml @@ -1460,8 +1460,9 @@ let dispatch_websocket_private (type f) "/private/ws" (dispatch_private_websocket rpc_server_family ~block_production rpc) -let directory ~rpc_server_family ?delegate_health_check_to rpc validation config - (tx_container : _ Services_backend_sig.tx_container) backend dir = +let directory (type f) ~rpc_server_family ?delegate_health_check_to rpc + validation config (tx_container : f Services_backend_sig.tx_container) + backend dir = dir |> version |> configuration config |> health_check ?delegate_to:delegate_health_check_to |> dispatch_public diff --git a/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.ml b/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.ml index 62dc9a390aab..fdcdd7d32072 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.ml +++ b/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.ml @@ -410,7 +410,7 @@ let register_monitor_heads (module Backend : Tezlink_backend_sig.S) dir = Tezos_rpc.Answer.return_stream {next; shutdown}) (** Builds the root directory. *) -let build_dir ~l2_chain_id backend = +let build_dir ~l2_chain_id ~add_operation backend = let (module Backend : Tezlink_backend_sig.S) = backend in Tezos_rpc.Directory.empty |> register_dynamic_block_services ~l2_chain_id backend @@ -424,12 +424,28 @@ let build_dir ~l2_chain_id backend = return (hash, input_time)) |> register_monitor_heads backend |> register ~service:Tezos_services.version ~impl:(fun () () () -> version ()) + |> register_with_conversion + ~service:Tezos_services.injection_operation + ~impl:(fun + () + (* TODO: https://gitlab.com/tezos/tezos/-/issues/8007 + When async is true, the RPC should return before + validation. *) + (_ : < async : bool ; chain : chain option >) + (raw_operation : bytes) + -> + let open Lwt_result_syntax in + let*? op = raw_operation |> Tezos_types.Operation.decode in + add_operation op raw_operation) + ~convert_output:(fun hash -> + hash |> Ethereum_types.hash_to_bytes |> Operation_hash.of_string_exn + |> Result_syntax.return) let tezlink_root = Tezos_rpc.Path.(open_root / "tezlink") (* module entrypoint *) -let register_tezlink_services ~l2_chain_id backend = - let directory = build_dir ~l2_chain_id backend in +let register_tezlink_services ~l2_chain_id ~add_operation backend = + let directory = build_dir ~l2_chain_id ~add_operation backend in let directory = Tezos_rpc.Directory.register_describe_directory_service directory diff --git a/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.mli b/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.mli index 78cda08abec2..44c014a93e89 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.mli +++ b/etherlink/bin_node/lib_dev/tezlink/tezlink_directory.mli @@ -10,5 +10,7 @@ creates a directory where all the Tezlink services are registered. *) val register_tezlink_services : l2_chain_id:L2_types.chain_id -> + add_operation: + (Tezos_types.Operation.t -> bytes -> Ethereum_types.hash tzresult Lwt.t) -> (module Tezlink_backend_sig.S) -> unit Tezos_rpc.Directory.t diff --git a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml index f3d2ceccda11..1c0cd07a46dd 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml +++ b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml @@ -457,3 +457,13 @@ let get_storage_normalized : Tezos_rpc.Service.t = Tezos_rpc.Service.subst1 Imported_protocol_plugin.RPC.Contract.S.get_storage_normalized + +let injection_operation : + ( [`POST], + unit, + unit, + < async : bool ; chain : chain option >, + bytes, + Operation_hash.t ) + Tezos_rpc.Service.t = + Tezos_shell_services.Injection_services.S.operation diff --git a/etherlink/bin_node/lib_dev/tezlink/tezos_types.ml b/etherlink/bin_node/lib_dev/tezlink/tezos_types.ml index 410ea577f507..b0a45a78975f 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezos_types.ml +++ b/etherlink/bin_node/lib_dev/tezlink/tezos_types.ml @@ -69,6 +69,41 @@ module Operation = struct raw : bytes; } + let decode raw = + let open Result_syntax in + let* op = + match + Data_encoding.Binary.of_bytes_opt + Tezlink_imports.Alpha_context.Operation.encoding + raw + with + | None -> error_with "Can't parse the operation" + | Some op -> return op + in + let from_contents (type kind) + (contents : kind Tezlink_imports.Alpha_context.contents) : t tzresult = + match contents with + | Tezlink_imports.Alpha_context.Manager_operation {source; counter; _} -> + let* counter = + convert_using_serialization + ~name:"counter" + ~dst:Data_encoding.n + ~src: + Tezlink_imports.Alpha_context.Manager_counter.encoding_for_RPCs + counter + in + return ({source; counter; op; raw} : t) + | _ -> error_with "Not a manager operation" + in + let (Operation_data op) = op.protocol_data in + match op.contents with + | Single contents -> from_contents contents + | Cons _ -> + (* TODO: https://gitlab.com/tezos/tezos/-/issues/8008 + support operation batches + *) + error_with "Unsupported feature: operation batch" + let encoding : t Data_encoding.t = let open Data_encoding in conv diff --git a/etherlink/bin_node/lib_dev/tezlink/tezos_types.mli b/etherlink/bin_node/lib_dev/tezlink/tezos_types.mli index 557c2d0260a2..e33b4f1fa9a5 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezos_types.mli +++ b/etherlink/bin_node/lib_dev/tezlink/tezos_types.mli @@ -55,6 +55,8 @@ module Operation : sig val hash_operation : t -> Ethereum_types.hash val encoding : t Data_encoding.t + + val decode : bytes -> t tzresult end module Tez : sig -- GitLab From a840a450c313e4114fbf32b9246ca8c976a641ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Cauderlier?= Date: Wed, 18 Jun 2025 12:23:53 +0200 Subject: [PATCH 2/2] Tezlink/Tezt: reset regression trace --- .../Alpha- Test the -describe endpoint.out | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Test the -describe endpoint.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Test the -describe endpoint.out index e4eb07c1e8e2..da201179cf55 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Test the -describe endpoint.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/Alpha- Test the -describe endpoint.out @@ -36,6 +36,17 @@ Available services: The chain unique identifier. - GET /tezlink/describe RPCs documentation and input/output schema + - POST /tezlink/injection/operation + Inject an operation in node and broadcast it. Returns the ID of the + operation. The `signedOperationContents` should be constructed using + contextual RPCs from the latest block and signed by the client. The + injection of the operation will apply it on the current mempool + context. This context may change at each operation injection or + operation reception from peers. By default, the RPC will wait for the + operation to be (pre-)validated before returning. However, if ?async + is true, the function returns immediately. The optional ?chain + parameter can be used to specify whether to inject on the test chain + or the main chain. - GET /tezlink/monitor/bootstrapped Wait for the node to have synchronized its chain with a few peers (configured by the node's administrator), streaming head updates that @@ -84,6 +95,17 @@ Available services: - /chains//blocks/ - GET /chains//chain_id The chain unique identifier. + - POST /injection/operation + Inject an operation in node and broadcast it. Returns the ID of the + operation. The `signedOperationContents` should be constructed using + contextual RPCs from the latest block and signed by the client. The + injection of the operation will apply it on the current mempool + context. This context may change at each operation injection or + operation reception from peers. By default, the RPC will wait for the + operation to be (pre-)validated before returning. However, if ?async is + true, the function returns immediately. The optional ?chain parameter + can be used to specify whether to inject on the test chain or the main + chain. - GET /monitor/bootstrapped Wait for the node to have synchronized its chain with a few peers (configured by the node's administrator), streaming head updates that -- GitLab