From 2b44083c43e77f8b00a518eb334af283e715b948 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Cornilleau Date: Wed, 11 Jun 2025 18:35:33 +0200 Subject: [PATCH 1/4] Tezlink/Node: use dynamic registration for block rpc directory --- .../lib_dev/tezlink/tezos_services.ml | 222 ++++++++++-------- .../Alpha- Test the -describe endpoint.out | 93 +------- 2 files changed, 124 insertions(+), 191 deletions(-) diff --git a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml index a8111f07e60b..a5a3f35b9949 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml +++ b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml @@ -704,106 +704,115 @@ let check_block = (Unsupported_block_parameter (Tezos_shell_services.Block_services.to_string block)) +let register_dynamic ~root_dir ~path dir_of_path = + Tezos_rpc.Directory.register_dynamic_directory root_dir path dir_of_path + (** Builds the directory registering services under `/chains/
/blocks//...`. *) -let register_block_services ~l2_chain_id +let build_block_static_directory ~l2_chain_id + (module Backend : Tezlink_backend_sig.S) = + let open Lwt_result_syntax in + Tezos_rpc.Directory.empty + |> register_with_conversion + ~service:Imported_services.current_level + ~impl:(fun {block; chain} query () -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.current_level chain block ~offset:query.offset) + ~convert_output:Protocol_types.Level.convert + |> register + ~service:Imported_services.protocols + ~impl:(fun {block; chain} _query () -> + let*? `Main = check_chain chain in + let*? _block = check_block block in + protocols ()) + |> register_with_conversion + ~service:Imported_services.contract_info + ~impl:(fun ({block; chain}, contract) _query () -> + let*? chain = check_chain chain in + let*? block = check_block block in + let* balance = Backend.balance chain block contract in + let* counter = Backend.counter chain block contract in + return (balance, counter)) + ~convert_output:Protocol_types.Contract.make_info + |> register + ~service:Imported_services.balance + ~impl:(fun ({chain; block}, contract) _ _ -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.balance chain block contract) + |> register + ~service:Imported_services.get_storage_normalized + (* TODO: #7995 + Take unparsing_mode argument into account *) + ~impl:(fun ({chain; block}, contract) () _unparsing_mode -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.get_storage chain block contract) + |> register + ~service:Imported_services.manager_key + ~impl:(fun ({chain; block}, contract) _ _ -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.manager_key chain block contract) + |> register_with_conversion + ~service:Imported_services.counter + ~impl:(fun ({block; chain}, contract) () () -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.counter chain block contract) + ~convert_output:Protocol_types.Counter.of_z + |> register + ~service:Imported_services.constants + ~impl:(fun {block; chain} () () -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.constants chain block) + |> register_with_conversion + ~service:Imported_services.header + ~impl:(fun {chain; block} () () -> + let*? chain = check_chain chain in + let*? block = check_block block in + let* tezlink_block = Backend.block chain block in + Lwt_result_syntax.return (tezlink_block, chain)) + ~convert_output: + (Protocol_types.Block_header.tezlink_block_to_block_header + ~l2_chain_id) + |> opt_register_with_conversion + ~service:Imported_services.hash + ~impl:(fun {block; chain} () () -> + let*? chain = check_chain chain in + let*? block = check_block block in + Backend.block_hash chain block) + ~convert_output:Protocol_types.ethereum_to_tezos_block_hash + |> register + ~service:Adaptive_issuance_services.expected_issuance + ~impl:(fun _ () () -> + (* The mock assumes we stay in cycle 0 for now *) + Lwt.return @@ Adaptive_issuance_services.dummy_rewards Int32.zero) + |> register + (* TODO: https://gitlab.com/tezos/tezos/-/issues/7965 *) + (* We need a proper implementation *) + ~service:Imported_services.simulate_operation + ~impl:(fun + {block; chain} + _param + ( _blocks_before_activation, + operation, + _chain_id, + _operation_inclusion_latency ) + -> + let*? _chain = check_chain chain in + let*? _block = check_block block in + let op = operation.protocol_data in + let*? mock_result = Mock.Operation_metadata.operation_metadata op in + return (op, mock_result)) + +let register_dynamic_block_services ~l2_chain_id (module Backend : Tezlink_backend_sig.S) base_dir = let open Lwt_result_syntax in - let dir = - Tezos_rpc.Directory.empty - |> register_with_conversion - ~service:Imported_services.current_level - ~impl:(fun {block; chain} query () -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.current_level chain block ~offset:query.offset) - ~convert_output:Protocol_types.Level.convert - |> register - ~service:Imported_services.protocols - ~impl:(fun {block; chain} _query () -> - let*? `Main = check_chain chain in - let*? _block = check_block block in - protocols ()) - |> register_with_conversion - ~service:Imported_services.contract_info - ~impl:(fun ({block; chain}, contract) _query () -> - let*? chain = check_chain chain in - let*? block = check_block block in - let* balance = Backend.balance chain block contract in - let* counter = Backend.counter chain block contract in - return (balance, counter)) - ~convert_output:Protocol_types.Contract.make_info - |> register - ~service:Imported_services.balance - ~impl:(fun ({chain; block}, contract) _ _ -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.balance chain block contract) - |> register - ~service:Imported_services.get_storage_normalized - (* TODO: #7995 - Take unparsing_mode argument into account *) - ~impl:(fun ({chain; block}, contract) () _unparsing_mode -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.get_storage chain block contract) - |> register - ~service:Imported_services.manager_key - ~impl:(fun ({chain; block}, contract) _ _ -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.manager_key chain block contract) - |> register_with_conversion - ~service:Imported_services.counter - ~impl:(fun ({block; chain}, contract) () () -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.counter chain block contract) - ~convert_output:Protocol_types.Counter.of_z - |> register - ~service:Imported_services.constants - ~impl:(fun {block; chain} () () -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.constants chain block) - |> register_with_conversion - ~service:Imported_services.header - ~impl:(fun {chain; block} () () -> - let*? chain = check_chain chain in - let*? block = check_block block in - let* tezlink_block = Backend.block chain block in - Lwt_result_syntax.return (tezlink_block, chain)) - ~convert_output: - (Protocol_types.Block_header.tezlink_block_to_block_header - ~l2_chain_id) - |> opt_register_with_conversion - ~service:Imported_services.hash - ~impl:(fun {block; chain} () () -> - let*? chain = check_chain chain in - let*? block = check_block block in - Backend.block_hash chain block) - ~convert_output:Protocol_types.ethereum_to_tezos_block_hash - |> register - ~service:Adaptive_issuance_services.expected_issuance - ~impl:(fun _ () () -> - (* The mock assumes we stay in cycle 0 for now *) - Lwt.return @@ Adaptive_issuance_services.dummy_rewards Int32.zero) - |> register - (* TODO: https://gitlab.com/tezos/tezos/-/issues/7965 *) - (* We need a proper implementation *) - ~service:Imported_services.simulate_operation - ~impl:(fun - {block; chain} - _param - ( _blocks_before_activation, - operation, - _chain_id, - _operation_inclusion_latency ) - -> - let*? _chain = check_chain chain in - let*? _block = check_block block in - let op = operation.protocol_data in - let*? mock_result = Mock.Operation_metadata.operation_metadata op in - return (op, mock_result)) + let static_dir = build_block_static_directory ~l2_chain_id (module Backend) in + let dynamic_dir_current_proto = + static_dir (* TODO: https://gitlab.com/tezos/tezos/-/issues/7993 *) (* RPCs at directory level doesn't appear properly in the describe RPC *) |> register_with_conversion @@ -816,12 +825,17 @@ let register_block_services ~l2_chain_id ~convert_output: (Protocol_types.Block.tezlink_block_to_block_info ~l2_chain_id) in - Tezos_rpc.Directory.prefix - block_directory_path - (Tezos_rpc.Directory.map - (fun (((), chain), block) -> make_env chain block) - dir) - |> Tezos_rpc.Directory.merge base_dir + let dynamic_dir = + register_dynamic + ~root_dir:Tezos_rpc.Directory.empty + ~path:block_directory_path + (fun (((), _chain), _block) -> + Lwt.return + @@ Tezos_rpc.Directory.map + (fun (((), chain), block) -> make_env chain block) + dynamic_dir_current_proto) + in + Tezos_rpc.Directory.merge base_dir dynamic_dir let register_chain_services ~l2_chain_id (module Backend : Tezlink_backend_sig.S) base_dir = @@ -878,7 +892,7 @@ let register_monitor_heads (module Backend : Tezlink_backend_sig.S) dir = let build_dir ~l2_chain_id backend = let (module Backend : Tezlink_backend_sig.S) = backend in Tezos_rpc.Directory.empty - |> register_block_services ~l2_chain_id backend + |> register_dynamic_block_services ~l2_chain_id backend |> register_chain_services ~l2_chain_id backend |> register_with_conversion ~service:Imported_services.bootstrapped 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 ca2d1f89d60b..a345c55920a3 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 @@ -24,48 +24,9 @@ Available services: - GET /health_check Assess the health of the RPC server + tezlink/ - + chains// - + blocks// - + context/ - - GET /tezlink/chains//blocks//context/constants - All constants - + contracts// - - GET /tezlink/chains//blocks//context/contracts//balance - The spendable balance of a contract (in mutez), also known as - liquid balance. Corresponds to tez owned by the contract that - are neither staked, nor in unstaked requests, nor in frozen - bonds. Identical to the 'spendable' RPC. - - GET /tezlink/chains//blocks//context/contracts//counter - Access the counter of a contract, if any. - - GET /tezlink/chains//blocks//context/contracts//manager_key - Access the manager of an implicit contract. - - POST /tezlink/chains//blocks//context/contracts//storage/normalized - Access the data of the contract and normalize it using the - requested unparsing mode. - - GET /tezlink/chains//blocks//context/issuance/expected_issuance - Returns the expected issued tez for the provided block and the - next 'consensus_rights_delay' cycles (in mutez) - - GET /tezlink/chains//blocks//hash - The block's hash, its unique identifier. - - GET /tezlink/chains//blocks//header - The whole block header. - - GET /tezlink/chains//blocks//helpers/current_level - Returns the level of the interrogated block, or the one of a - block located `offset` blocks after it in the chain. For - instance, the next block if `offset` is 1. The offset cannot be - negative. - - POST /tezlink/chains//blocks//helpers/scripts/simulate_operation - Simulate running an operation at some future moment (based on the - number of blocks given in the `latency` argument), and return the - operation application result. The result is the same as - run_operation except for the consumed gas, which depends on the - contents of the cache at that future moment. This RPC estimates - future gas consumption by trying to predict the state of the - cache using some heuristics. - - GET /tezlink/chains//blocks//protocols - Current and next protocol. - - GET /tezlink/chains//chain_id - The chain unique identifier. + - /tezlink/chains//blocks/ + - GET /tezlink/chains//chain_id + The chain unique identifier. - GET /tezlink/describe RPCs documentation and input/output schema - GET /tezlink/monitor/bootstrapped @@ -85,8 +46,6 @@ Available services: Dynamic parameter description: - - A contract identifier encoded in b58check. A block identifier. This can take one of the following values: @@ -114,47 +73,9 @@ Warning: Available services: - + chains// - + blocks// - + context/ - - GET /chains//blocks//context/constants - All constants - + contracts// - - GET /chains//blocks//context/contracts//balance - The spendable balance of a contract (in mutez), also known as - liquid balance. Corresponds to tez owned by the contract that - are neither staked, nor in unstaked requests, nor in frozen - bonds. Identical to the 'spendable' RPC. - - GET /chains//blocks//context/contracts//counter - Access the counter of a contract, if any. - - GET /chains//blocks//context/contracts//manager_key - Access the manager of an implicit contract. - - POST /chains//blocks//context/contracts//storage/normalized - Access the data of the contract and normalize it using the - requested unparsing mode. - - GET /chains//blocks//context/issuance/expected_issuance - Returns the expected issued tez for the provided block and the - next 'consensus_rights_delay' cycles (in mutez) - - GET /chains//blocks//hash - The block's hash, its unique identifier. - - GET /chains//blocks//header - The whole block header. - - GET /chains//blocks//helpers/current_level - Returns the level of the interrogated block, or the one of a block - located `offset` blocks after it in the chain. For instance, the - next block if `offset` is 1. The offset cannot be negative. - - POST /chains//blocks//helpers/scripts/simulate_operation - Simulate running an operation at some future moment (based on the - number of blocks given in the `latency` argument), and return the - operation application result. The result is the same as - run_operation except for the consumed gas, which depends on the - contents of the cache at that future moment. This RPC estimates - future gas consumption by trying to predict the state of the cache - using some heuristics. - - GET /chains//blocks//protocols - Current and next protocol. - - GET /chains//chain_id - The chain unique identifier. + - /chains//blocks/ + - GET /chains//chain_id + The chain unique identifier. - 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 @@ -170,8 +91,6 @@ Available services: Dynamic parameter description: - - A contract identifier encoded in b58check. A block identifier. This can take one of the following values: -- GitLab From fac129ea03a2948532e084aebc211b480416b9da Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Cornilleau Date: Thu, 12 Jun 2025 11:14:33 +0200 Subject: [PATCH 2/4] Tezt/lib: allows a url argument for "octez-client rpc list " We need to have a description of a subtree, hidden behind a dynamic directory --- tezt/lib_tezos/client.ml | 8 ++++---- tezt/lib_tezos/client.mli | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 4520439c2756..50fe8d2d786e 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -401,11 +401,11 @@ let rpc ?log_command ?log_status_on_exit ?log_output ?better_errors ?endpoint in return res -let spawn_rpc_list ?endpoint ?hooks client = - spawn_command ?endpoint ?hooks client ["rpc"; "list"] +let spawn_rpc_list ?endpoint ?hooks ?url client = + spawn_command ?endpoint ?hooks client (["rpc"; "list"] @ Option.to_list url) -let rpc_list ?endpoint ?hooks client = - spawn_rpc_list ?endpoint ?hooks client |> Process.check_and_read_stdout +let rpc_list ?endpoint ?hooks ?url client = + spawn_rpc_list ?endpoint ?hooks ?url client |> Process.check_and_read_stdout let spawn_rpc_schema ?log_command ?log_status_on_exit ?log_output ?(better_errors = false) ?endpoint ?hooks ?env ?protocol_hash meth path diff --git a/tezt/lib_tezos/client.mli b/tezt/lib_tezos/client.mli index e0e22e1d5740..ee4463ae0424 100644 --- a/tezt/lib_tezos/client.mli +++ b/tezt/lib_tezos/client.mli @@ -271,12 +271,13 @@ module Spawn : sig JSON.t Runnable.process end -(** Run [octez-client rpc list]. *) -val rpc_list : ?endpoint:endpoint -> ?hooks:Process.hooks -> t -> string Lwt.t +(** Run [octez-client rpc list ]. *) +val rpc_list : + ?endpoint:endpoint -> ?hooks:Process.hooks -> ?url:string -> t -> string Lwt.t (** Same as [rpc_list], but do not wait for the process to exit. *) val spawn_rpc_list : - ?endpoint:endpoint -> ?hooks:Process.hooks -> t -> Process.t + ?endpoint:endpoint -> ?hooks:Process.hooks -> ?url:string -> t -> Process.t (** Run [octez-client rpc schema]. *) val rpc_schema : -- GitLab From 65ad6b534af15d7e84857bc17e98144af871042e Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Cornilleau Date: Thu, 12 Jun 2025 11:30:09 +0200 Subject: [PATCH 3/4] Tezlink/tezt: describe block directory --- etherlink/tezt/tests/evm_sequencer.ml | 7 +++ .../Alpha- Test the -describe endpoint.out | 50 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index 1ec0d10209ad..9670f315c7d6 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -10610,6 +10610,13 @@ let test_describe_endpoint = let* (_ : string) = Client.rpc_list ~hooks ~endpoint:tezlink_endpoint client in + let* (_ : string) = + Client.rpc_list + ~hooks + ~endpoint:tezlink_endpoint + ~url:"chains/main/blocks/head" + client + in unit let test_relay_restricted_rpcs = 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 a345c55920a3..3be7eec543a5 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 @@ -108,3 +108,53 @@ Dynamic parameter description: A chain identifier. This is either a chain hash in Base58Check notation or a one the predefined aliases: 'main', 'test'. + +./octez-client rpc list chains/main/blocks/head + +Available services: + + + chains/main/blocks/head/ + + context/ + - GET /chains/main/blocks/head/context/constants + All constants + + contracts// + - GET /chains/main/blocks/head/context/contracts//balance + The spendable balance of a contract (in mutez), also known as + liquid balance. Corresponds to tez owned by the contract that are + neither staked, nor in unstaked requests, nor in frozen bonds. + Identical to the 'spendable' RPC. + - GET /chains/main/blocks/head/context/contracts//counter + Access the counter of a contract, if any. + - GET /chains/main/blocks/head/context/contracts//manager_key + Access the manager of an implicit contract. + - POST /chains/main/blocks/head/context/contracts//storage/normalized + Access the data of the contract and normalize it using the + requested unparsing mode. + - GET /chains/main/blocks/head/context/issuance/expected_issuance + Returns the expected issued tez for the provided block and the next + 'consensus_rights_delay' cycles (in mutez) + - GET /chains/main/blocks/head/hash + The block's hash, its unique identifier. + - GET /chains/main/blocks/head/header + The whole block header. + - GET /chains/main/blocks/head/helpers/current_level + Returns the level of the interrogated block, or the one of a block + located `offset` blocks after it in the chain. For instance, the next + block if `offset` is 1. The offset cannot be negative. + - POST /chains/main/blocks/head/helpers/scripts/simulate_operation + Simulate running an operation at some future moment (based on the + number of blocks given in the `latency` argument), and return the + operation application result. The result is the same as run_operation + except for the consumed gas, which depends on the contents of the + cache at that future moment. This RPC estimates future gas + consumption by trying to predict the state of the cache using some + heuristics. + - GET /chains/main/blocks/head/protocols + Current and next protocol. + + +Dynamic parameter description: + + + A contract identifier encoded in b58check. + -- GitLab From 86859eae5e00eb8f75c544aecb689a3f98b59c84 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel CORNILLEAU Date: Tue, 17 Jun 2025 11:39:08 +0000 Subject: [PATCH 4/4] Tezlink/Node: docstring on dynamic registering --- etherlink/bin_node/lib_dev/tezlink/tezos_services.ml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml index a5a3f35b9949..bc3c59d6667d 100644 --- a/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml +++ b/etherlink/bin_node/lib_dev/tezlink/tezos_services.ml @@ -707,7 +707,7 @@ let check_block = let register_dynamic ~root_dir ~path dir_of_path = Tezos_rpc.Directory.register_dynamic_directory root_dir path dir_of_path -(** Builds the directory registering services under `/chains/
/blocks//...`. *) +(** Builds the static part of the directory registering services under `/chains/
/blocks//...`. *) let build_block_static_directory ~l2_chain_id (module Backend : Tezlink_backend_sig.S) = let open Lwt_result_syntax in @@ -807,6 +807,12 @@ let build_block_static_directory ~l2_chain_id let*? mock_result = Mock.Operation_metadata.operation_metadata op in return (op, mock_result)) +(** We currently support a single target protocol version but we need to handle early blocks (blocks at + levels 0 and 1) specifically because TzKT expects the `protocol` and `next_protocol` fields of the + block headers and block metadata at these levels to indicate the hashes of the genesis protocols. + Patching these fields is unfortunately not doable from within the implementation of the services + because these fields are added in the output encodings of the services. For this reason, the services + for which a special treatment of early blocks is needed are registered dynamically. *) let register_dynamic_block_services ~l2_chain_id (module Backend : Tezlink_backend_sig.S) base_dir = let open Lwt_result_syntax in -- GitLab