diff --git a/etherlink/bin_node/lib_dev/rpc_server.ml b/etherlink/bin_node/lib_dev/rpc_server.ml index 1776d4b0aaaf3835dd5e038a74b707d9aba90384..2360109cefbce1ba457e0cb7e135b2daad48420d 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 d4a1aaef7db2c6e4faedeabefc9a4a3a78901ecf..660d64e81bda4ab23a0d8e99a3b3355b1893b89e 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 62dc9a390aab61a7a7488fc335e7e9ebe75e13f1..fdcdd7d3207228a43adaf6742b78319239d66a3a 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 78cda08abec208a81ee5b64e7489adab5137613d..44c014a93e895114272d6834e0e38308fa7e33ca 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 f3d2ceccda118a5543ba28c23f10a0e08183c0d0..1c0cd07a46dddea7b907043283b293e7a76204f1 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 410ea577f507f62fb7502a6884cc73a0a3dec898..b0a45a78975fd7bfb6f8e29cb33facdd821c52d5 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 557c2d0260a261c39fe5a6ba6bb139fd588fdb61..e33b4f1fa9a5e9b1f9cb48a4c44c9a1873d6ae77 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 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 e4eb07c1e8e28b594de6c33d3ddab69875f2d0df..da201179cf555238f05a0f2933a50ffd45b5d264 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