From 804bcd88ddd4e2eb53e6d2186b509daf5a837ae3 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Cornilleau Date: Mon, 31 Mar 2025 12:36:49 +0200 Subject: [PATCH 1/2] Tezlink/Node: balance service The module Contract_services unfortunately doesn't expose the services definition, only their implementation. So we have to redefine it. --- .../bin_node/lib_dev/tezlink_services_impl.ml | 6 ++- etherlink/bin_node/lib_dev/tezos_services.ml | 49 ++++++++++++++++++- etherlink/bin_node/lib_dev/tezos_services.mli | 5 +- .../Alpha- Test the -describe endpoint.out | 20 +++++--- .../Quebec- Test the -describe endpoint.out | 20 +++++--- .../R022-- Test the -describe endpoint.out | 20 +++++--- 6 files changed, 97 insertions(+), 23 deletions(-) diff --git a/etherlink/bin_node/lib_dev/tezlink_services_impl.ml b/etherlink/bin_node/lib_dev/tezlink_services_impl.ml index 1c27b9190f75..551491741cb7 100644 --- a/etherlink/bin_node/lib_dev/tezlink_services_impl.ml +++ b/etherlink/bin_node/lib_dev/tezlink_services_impl.ml @@ -8,8 +8,7 @@ open Tezos_services let current_level (module Backend : Services_backend_sig.S) - (constants : - Imported_protocol.Protocol.Alpha_context.Constants.Parametric.t) chain + (constants : Imported_protocol.Alpha_context.Constants.Parametric.t) chain block level_query = let open Lwt_result_syntax in let* offset = @@ -58,4 +57,7 @@ let michelson_services_methods backend constants = (* TODO: #7857 need proper implementation *) Lwt_result_syntax.return Tezlink_version.mock); protocols = (fun () -> Lwt_result_syntax.return Tezlink_protocols.current); + balance = + (fun _ _ _ -> + Lwt_result_syntax.return @@ Ethereum_types.quantity_of_z Z.one); } diff --git a/etherlink/bin_node/lib_dev/tezos_services.ml b/etherlink/bin_node/lib_dev/tezos_services.ml index 0338097f3e91..31215241f9fd 100644 --- a/etherlink/bin_node/lib_dev/tezos_services.ml +++ b/etherlink/bin_node/lib_dev/tezos_services.ml @@ -4,9 +4,11 @@ (* Copyright (c) 2025 Nomadic Labs *) (* *) (*****************************************************************************) -module Imported_protocol = Tezos_protocol_021_PsQuebec + +module Imported_protocol = Tezos_protocol_021_PsQuebec.Protocol module Imported_protocol_plugin = Tezos_protocol_plugin_021_PsQuebec module Imported_protocol_parameters = Tezos_protocol_021_PsQuebec_parameters +module Imported_env = Tezos_protocol_environment_021_PsQuebec (* The output type of the current_level service but with less duplicated information. Can be changed, as long as the [conversion_encoding] is also @@ -60,7 +62,7 @@ let convert_using_serialization ~name ~dst ~src value = might be difficult to actually build, so we define conversion function from local types to protocol types. *) module Protocol_types = struct - module Alpha_context = Imported_protocol.Protocol.Alpha_context + module Alpha_context = Imported_protocol.Alpha_context module Raw_level = Alpha_context.Raw_level module Cycle = struct @@ -112,6 +114,15 @@ module Protocol_types = struct ~dst:encoding ~src:conversion_encoding end + + module Tez = struct + include Alpha_context.Tez + + let convert q = + q |> Ethereum_types.Qty.to_z |> Z.to_int64 |> Alpha_context.Tez.of_mutez + |> Option.value ~default:Alpha_context.Tez.zero + |> Result_syntax.return + end end (** [wrap conversion service_implementation] changes the output type @@ -137,6 +148,8 @@ type tezlink_rpc_context = { chain : Tezos_shell_services.Chain_services.chain; } +type contract = Imported_protocol.Alpha_context.Contract.t + (** Builds a [tezlink_rpc_context] from paths parameters. *) let make_env (chain : Tezos_shell_services.Chain_services.chain) (block : Tezos_shell_services.Block_services.block) : @@ -209,6 +222,32 @@ module Imported_services = struct Tezlink_protocols.protocols ) Tezos_rpc.Service.t = import_service Tezos_shell_services.Shell_services.Blocks.S.protocols + + let contract_arg_path s = + let contract_path = Imported_protocol_plugin.RPC.Contract.S.path in + let contract_args = Imported_protocol.Alpha_context.Contract.rpc_arg in + Imported_env.RPC_path.(contract_path /: contract_args / s) + + (* TODO: #7876 Expose and import the service definition from the plugin. *) + let balance : + ( [`GET], + tezlink_rpc_context, + tezlink_rpc_context * contract, + unit, + unit, + Protocol_types.Tez.t ) + Tezos_rpc.Service.t = + Tezos_rpc.Service.subst1 + (* TODO: #7876 should be imported *) + @@ Tezos_rpc.Service.get_service + ~description: + "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." + ~query:Tezos_rpc.Query.empty + ~output:Protocol_types.Tez.encoding + (contract_arg_path "balance") end type block = Tezos_shell_services.Block_services.block @@ -228,6 +267,7 @@ type tezos_services_implementation = { chain -> block -> Imported_services.level_query -> level tzresult Lwt.t; version : unit -> Tezlink_version.version tzresult Lwt.t; protocols : unit -> Tezlink_protocols.protocols tzresult Lwt.t; + balance : chain -> block -> contract -> Ethereum_types.quantity tzresult Lwt.t; } (** Builds the directory registering services under `/chains/
/blocks//...`. *) @@ -243,6 +283,11 @@ let build_block_dir impl = ~convert_output:Protocol_types.Level.convert |> register ~service:Imported_services.protocols ~impl:(fun _ _ () -> impl.protocols ()) + |> register_with_conversion + ~service:Imported_services.balance + ~impl:(fun ({block; chain}, contract) _ _ -> + impl.balance chain block contract) + ~convert_output:Protocol_types.Tez.convert (** Builds the root director. *) let build_dir impl = diff --git a/etherlink/bin_node/lib_dev/tezos_services.mli b/etherlink/bin_node/lib_dev/tezos_services.mli index 86fcbb12a078..54ec9a18dc38 100644 --- a/etherlink/bin_node/lib_dev/tezos_services.mli +++ b/etherlink/bin_node/lib_dev/tezos_services.mli @@ -5,7 +5,7 @@ (* *) (*****************************************************************************) -module Imported_protocol = Tezos_protocol_021_PsQuebec +module Imported_protocol = Tezos_protocol_021_PsQuebec.Protocol module Imported_protocol_parameters = Tezos_protocol_021_PsQuebec_parameters module Tezlink_protocols : sig @@ -42,11 +42,14 @@ type block = Tezos_shell_services.Block_services.block type chain = Tezos_shell_services.Chain_services.chain +type contract = Imported_protocol.Alpha_context.Contract.t + (** Container for the implementations necessary to answer tezos RPC requests. *) type tezos_services_implementation = { current_level : chain -> block -> level_query -> level tzresult Lwt.t; version : unit -> Tezlink_version.version tzresult Lwt.t; protocols : unit -> Tezlink_protocols.protocols tzresult Lwt.t; + balance : chain -> block -> contract -> Ethereum_types.quantity tzresult Lwt.t; } (* THIS IS THE ENTRYPOINT *) 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 883bf7def0ce..4d8820e51ce1 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 @@ -21,12 +21,18 @@ Available services: - GET /health_check Assess the health of the RPC server + tezlink/ - - 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. - - GET /tezlink/chains//blocks//protocols - Current and next protocol. + + chains//blocks// + - 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//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. + - GET /tezlink/chains//blocks//protocols + Current and next protocol. - GET /tezlink/describe RPCs documentation and input/output schema - GET /tezlink/version @@ -37,6 +43,8 @@ Available services: Dynamic parameter description: + + A contract identifier encoded in b58check. A block identifier. This can take one of the following values: diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/Quebec- Test the -describe endpoint.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/Quebec- Test the -describe endpoint.out index 883bf7def0ce..4d8820e51ce1 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/Quebec- Test the -describe endpoint.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/Quebec- Test the -describe endpoint.out @@ -21,12 +21,18 @@ Available services: - GET /health_check Assess the health of the RPC server + tezlink/ - - 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. - - GET /tezlink/chains//blocks//protocols - Current and next protocol. + + chains//blocks// + - 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//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. + - GET /tezlink/chains//blocks//protocols + Current and next protocol. - GET /tezlink/describe RPCs documentation and input/output schema - GET /tezlink/version @@ -37,6 +43,8 @@ Available services: Dynamic parameter description: + + A contract identifier encoded in b58check. A block identifier. This can take one of the following values: diff --git a/etherlink/tezt/tests/expected/evm_sequencer.ml/R022-- Test the -describe endpoint.out b/etherlink/tezt/tests/expected/evm_sequencer.ml/R022-- Test the -describe endpoint.out index 883bf7def0ce..4d8820e51ce1 100644 --- a/etherlink/tezt/tests/expected/evm_sequencer.ml/R022-- Test the -describe endpoint.out +++ b/etherlink/tezt/tests/expected/evm_sequencer.ml/R022-- Test the -describe endpoint.out @@ -21,12 +21,18 @@ Available services: - GET /health_check Assess the health of the RPC server + tezlink/ - - 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. - - GET /tezlink/chains//blocks//protocols - Current and next protocol. + + chains//blocks// + - 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//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. + - GET /tezlink/chains//blocks//protocols + Current and next protocol. - GET /tezlink/describe RPCs documentation and input/output schema - GET /tezlink/version @@ -37,6 +43,8 @@ Available services: Dynamic parameter description: + + A contract identifier encoded in b58check. A block identifier. This can take one of the following values: -- GitLab From 9b6ecdf7d07792e0067c2a7ff57b407272b320a9 Mon Sep 17 00:00:00 2001 From: Pierre-Emmanuel Cornilleau Date: Mon, 31 Mar 2025 12:37:55 +0200 Subject: [PATCH 2/2] Tezlink/tezt: test balance --- etherlink/tezt/tests/evm_sequencer.ml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/etherlink/tezt/tests/evm_sequencer.ml b/etherlink/tezt/tests/evm_sequencer.ml index b96341340ded..522081ff6b37 100644 --- a/etherlink/tezt/tests/evm_sequencer.ml +++ b/etherlink/tezt/tests/evm_sequencer.ml @@ -555,6 +555,29 @@ let test_tezlink_protocols = ~error_msg:"Expected %R but got %L") ; unit +let test_tezlink_balance = + register_tezlink_test + ~title:"Test of the balance rpc" + ~tags:["evm"; "rpc"; "balance"] + @@ fun {sequencer; _} _protocol -> + (* call the balance rpc and parse the result *) + let rpc_balance () = + let path = + sf + "/tezlink/chains/main/blocks/head/context/contracts/%s/balance" + Constant.bootstrap1.public_key_hash + in + let* res = + Curl.get_raw ~args:["-v"] (Evm_node.endpoint sequencer ^ path) + |> Runnable.run + in + return @@ JSON.parse ~origin:"curl_balance" res + in + + let* res = rpc_balance () in + Check.(JSON.(res |> as_int = 1) int ~error_msg:"Expected %R but got %L") ; + unit + let test_tezlink_version = register_tezlink_test ~title:"Test of the version rpc" @@ -12777,6 +12800,7 @@ let () = test_fa_deposit_and_withdrawals_events [Alpha] ; test_block_producer_validation [Alpha] ; test_tezlink_current_level [Alpha] ; + test_tezlink_balance [Alpha] ; test_tezlink_protocols [Alpha] ; test_tezlink_version [Alpha] ; test_tezlink_produceBlock [Alpha] -- GitLab