Revision cbda8dd964df0dfd3415209db7ff9d1d9e91c1cf authored by Alain Mebsout on 08 July 2022, 08:12:22 UTC, committed by Alain Mebsout on 06 September 2022, 14:48:23 UTC
1 parent dcc833b
RPC_test.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2020 Metastate AG <hello@metastate.dev> *)
(* Copyright (c) 2021 Nomadic Labs <contact@nomadic-labs.com> *)
(* Copyright (c) 2022 Trili Tech <contact@trili.tech> *)
(* *)
(* Permission is hereby granted, free of charge, to any person obtaining a *)
(* copy of this software and associated documentation files (the "Software"),*)
(* to deal in the Software without restriction, including without limitation *)
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
(* and/or sell copies of the Software, and to permit persons to whom the *)
(* Software is furnished to do so, subject to the following conditions: *)
(* *)
(* The above copyright notice and this permission notice shall be included *)
(* in all copies or substantial portions of the Software. *)
(* *)
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
(* DEALINGS IN THE SOFTWARE. *)
(* *)
(*****************************************************************************)
(* Testing
-------
Component: RPC regression tests
Invocation: dune exec tezt/tests/main.exe -- -f RPC_test.ml
Note that to reset the regression outputs, one can use
the [--reset-regressions] option. When doing so, it is
recommended to clear the output directory [tezt/_regressions/rpc]
first to remove unused files in case some paths change.
Subject: RPC regression tests capture the output of RPC calls and compare it
with the output from the previous run. The test passes only if the
outputs match exactly.
*)
(* These hooks must be attached to every process that should be captured for
regression testing *)
let hooks = Tezos_regression.hooks
let title_tag_of_test_mode = function
| `Client -> (`Client, "client")
| `Client_data_dir_proxy_server -> (`Client, "proxy_server_data_dir")
| `Client_rpc_proxy_server -> (`Client, "proxy_server_rpc")
| `Light -> (`Light, "light")
| `Proxy -> (`Proxy, "proxy")
let patch_protocol_parameters protocol = function
| None -> Lwt.return_none
| Some overrides ->
let* file =
Protocol.write_parameter_file
~base:(Either.right (protocol, None))
(overrides protocol)
in
Lwt.return_some file
let initialize_chain_for_test client = function
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server ->
(* Because the proxy server doesn't support genesis. *)
Client.bake_for_and_wait client
| `Client | `Light | `Proxy -> unit
let endpoint_of_test_mode_tag node = function
| `Client | `Light | `Proxy -> Lwt.return_some Client.(Node node)
| (`Client_rpc_proxy_server | `Client_data_dir_proxy_server) as
proxy_server_mode ->
let args =
Some
(match proxy_server_mode with
| `Client_rpc_proxy_server -> [Proxy_server.Data_dir]
| `Client_data_dir_proxy_server -> [])
in
let* proxy_server = Proxy_server.init ?args node in
Lwt.return_some Client.(Proxy_server proxy_server)
(* A helper to register a RPC test environment with a node and a client for the
given protocol version.
- [test_mode_tag] specifies the client mode ([`Client], [`Light] or [`Proxy]).
- [test_function] is the function to run to perform RPCs after the test
environment is set up.
- [parameter_overrides] specifies protocol parameters to change from the default
sandbox parameters.
- [nodes_args] specifies additional parameters to pass to the node.
- [sub_group] is a short identifier for your test, used in the test title and as a tag.
Additionally, since this uses [Protocol.register_regression_test], this has an
implicit argument to specify the list of protocols to test. *)
let check_rpc_regression ~test_mode_tag ~test_function ?parameter_overrides
?nodes_args sub_group =
let client_mode_tag, title_tag = title_tag_of_test_mode test_mode_tag in
Protocol.register_regression_test
~__FILE__
~title:(sf "(mode %s) RPC regression tests: %s" title_tag sub_group)
~tags:["rpc"; title_tag; sub_group]
@@ fun protocol ->
let* parameter_file =
patch_protocol_parameters protocol parameter_overrides
in
(* Initialize a node with alpha protocol and data to be used for RPC calls.
The log of the node is not captured in the regression output. *)
let* node, client =
Client.init_with_protocol
?parameter_file
?nodes_args
~protocol
client_mode_tag
()
in
let* () = initialize_chain_for_test client test_mode_tag in
let* endpoint = endpoint_of_test_mode_tag node test_mode_tag in
let* _ = test_function test_mode_tag protocol ?endpoint client in
unit
(* Like [check_rpc_regression], but does not register a regression tests *)
let check_rpc ~test_mode_tag ~test_function ?parameter_overrides ?nodes_args
sub_group =
let client_mode_tag, title_tag = title_tag_of_test_mode test_mode_tag in
Protocol.register_test
~__FILE__
~title:(sf "(mode %s) RPC tests: %s" title_tag sub_group)
~tags:["rpc"; title_tag; sub_group]
@@ fun protocol ->
let* parameter_file =
patch_protocol_parameters protocol parameter_overrides
in
(* Initialize a node with alpha protocol and data to be used for RPC calls. *)
let* node, client =
Client.init_with_protocol
?parameter_file
?nodes_args
~protocol
client_mode_tag
()
in
let* () = initialize_chain_for_test client test_mode_tag in
let* endpoint = endpoint_of_test_mode_tag node test_mode_tag in
let* _ = test_function test_mode_tag protocol ?endpoint client in
unit
(* Test the contracts RPC. *)
let test_contracts _test_mode_tag _protocol ?endpoint client =
let test_implicit_contract contract_id =
let*! _ = RPC.Contracts.get ?endpoint ~hooks ~contract_id client in
let*! _ = RPC.Contracts.get_balance ?endpoint ~hooks ~contract_id client in
let*! _ = RPC.Contracts.get_counter ?endpoint ~hooks ~contract_id client in
let*! _ =
RPC.Contracts.get_manager_key ?endpoint ~hooks ~contract_id client
in
unit
in
let*! _contracts = RPC.Contracts.get_all ?endpoint ~hooks client in
let* contracts = RPC.Delegates.get_all ?endpoint ~hooks client in
Log.info "Test implicit baker contract" ;
let bootstrap = List.hd contracts in
let* () = test_implicit_contract bootstrap in
let*! _ =
RPC.Contracts.get_delegate ?endpoint ~hooks ~contract_id:bootstrap client
in
Log.info "Test un-allocated implicit contract" ;
let unallocated_implicit = "tz1c5BVkpwCiaPHJBzyjg7UHpJEMPTYA1bHG" in
assert (not @@ List.mem unallocated_implicit contracts) ;
let*! _ =
RPC.Contracts.get ?endpoint ~hooks ~contract_id:unallocated_implicit client
in
Log.info "Test non-delegated implicit contract" ;
let simple_implicit = "simple" in
let* simple_implicit_key =
Client.gen_and_show_keys ~alias:simple_implicit client
in
let bootstrap1 = "bootstrap1" in
let* () =
Client.transfer
~amount:Tez.(of_int 100)
~giver:bootstrap1
~receiver:simple_implicit_key.public_key_hash
~burn_cap:Constant.implicit_account_burn
client
in
let* () = Client.bake_for_and_wait client in
let* () = test_implicit_contract simple_implicit_key.public_key_hash in
let* () =
Lwt_list.iter_s
(fun rpc ->
let*? process =
rpc
?endpoint
?hooks:(Some hooks)
?chain:None
?block:None
~contract_id:simple_implicit_key.public_key_hash
client
in
Process.check ~expect_failure:true process)
[
RPC.Contracts.get_delegate;
RPC.Contracts.get_entrypoints;
RPC.Contracts.get_script;
RPC.Contracts.get_storage;
]
in
Log.info "Test delegated implicit contract" ;
let delegated_implicit = "delegated" in
let* delegated_implicit_key =
Client.gen_and_show_keys ~alias:delegated_implicit client
in
let* () =
Client.transfer
~amount:Tez.(of_int 100)
~giver:bootstrap1
~receiver:delegated_implicit
~burn_cap:Constant.implicit_account_burn
client
in
let* () = Client.bake_for_and_wait client in
let*! () =
Client.set_delegate ~src:delegated_implicit ~delegate:bootstrap1 client
in
let* () = Client.bake_for_and_wait client in
let* () = test_implicit_contract delegated_implicit_key.public_key_hash in
let*! _ =
RPC.Contracts.get_delegate
?endpoint
~hooks
~contract_id:delegated_implicit_key.public_key_hash
client
in
let* () =
Lwt_list.iter_s
(fun rpc ->
let*? process =
rpc
?endpoint
?hooks:(Some hooks)
?chain:None
?block:None
~contract_id:delegated_implicit_key.public_key_hash
client
in
Process.check ~expect_failure:true process)
[
RPC.Contracts.get_entrypoints;
RPC.Contracts.get_script;
RPC.Contracts.get_storage;
]
in
let test_originated_contract contract_id =
let*! _ = RPC.Contracts.get ?endpoint ~hooks ~contract_id client in
let*! _ = RPC.Contracts.get_balance ?endpoint ~hooks ~contract_id client in
let*? process =
RPC.Contracts.get_counter ?endpoint ~hooks ~contract_id client
in
let* () = Process.check ~expect_failure:true process in
let*? process =
RPC.Contracts.get_manager_key ?endpoint ~hooks ~contract_id client
in
let* () = Process.check ~expect_failure:true process in
let big_map_key =
Ezjsonm.value_from_string
"{ \"key\": { \"int\": \"0\" }, \"type\": { \"prim\": \"int\" } }"
in
let*! _ =
RPC.Contracts.big_map_get
?endpoint
~hooks
~contract_id
~data:big_map_key
client
in
let*! _ =
RPC.Contracts.get_entrypoints ?endpoint ~hooks ~contract_id client
in
let*! _ = RPC.Contracts.get_script ?endpoint ~hooks ~contract_id client in
let*! _ = RPC.Contracts.get_storage ?endpoint ~hooks ~contract_id client in
unit
in
(* A smart contract without any big map or entrypoints *)
Log.info "Test simple originated contract" ;
let* originated_contract_simple =
Client.originate_contract
~alias:"originated_contract_simple"
~amount:Tez.zero
~src:"bootstrap1"
~prg:"file:./tezt/tests/contracts/proto_alpha/str_id.tz"
~init:"Some \"initial storage\""
~burn_cap:Tez.(of_int 3)
client
in
let* () = Client.bake_for_and_wait client in
let* () = test_originated_contract originated_contract_simple in
(* A smart contract with a big map and entrypoints *)
Log.info "Test advanced originated contract" ;
let* originated_contract_advanced =
Client.originate_contract
~alias:"originated_contract_advanced"
~amount:Tez.zero
~src:"bootstrap1"
~prg:"file:./tezt/tests/contracts/proto_alpha/big_map_entrypoints.tz"
~init:"Pair { Elt \"dup\" 0 } { Elt \"dup\" 1 ; Elt \"test\" 5 }"
~burn_cap:Tez.(of_int 3)
client
in
let* () = Client.bake_for_and_wait client in
let* () = test_originated_contract originated_contract_advanced in
let unique_big_map_key =
Ezjsonm.value_from_string
"{ \"key\": { \"string\": \"test\" }, \"type\": { \"prim\": \"string\" } \
}"
in
let*! _ =
RPC.Contracts.big_map_get
?endpoint
~hooks
~contract_id:originated_contract_advanced
~data:unique_big_map_key
client
in
let duplicate_big_map_key =
Ezjsonm.value_from_string
"{ \"key\": { \"string\": \"dup\" }, \"type\": { \"prim\": \"string\" } }"
in
let*! _ =
RPC.Contracts.big_map_get
?endpoint
~hooks
~contract_id:originated_contract_advanced
~data:duplicate_big_map_key
client
in
unit
let test_delegates_on_registered_alpha ~contracts ?endpoint client =
Log.info "Test implicit baker contract" ;
let bootstrap = List.hd contracts in
let* _ = RPC.Delegates.get ?endpoint ~hooks ~pkh:bootstrap client in
let* _ =
RPC.Delegates.get_full_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_frozen_deposits ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_deactivated ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_delegated_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_delegated_contracts ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_grace_period ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_staking_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_voting_power ?endpoint ~hooks ~pkh:bootstrap client
in
unit
let test_delegates_on_registered_hangzhou ~contracts ?endpoint client =
Log.info "Test implicit baker contract" ;
let bootstrap = List.hd contracts in
let* _ = RPC.Delegates.get ?endpoint ~hooks ~pkh:bootstrap client in
let* _ = RPC.Delegates.get_balance ?endpoint ~hooks ~pkh:bootstrap client in
let* _ =
RPC.Delegates.get_frozen_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_frozen_balance_by_cycle
?endpoint
~hooks
~pkh:bootstrap
client
in
let* _ =
RPC.Delegates.get_staking_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_delegated_contracts ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_delegated_balance ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_deactivated ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_grace_period ?endpoint ~hooks ~pkh:bootstrap client
in
let* _ =
RPC.Delegates.get_voting_power ?endpoint ~hooks ~pkh:bootstrap client
in
unit
(* Test the delegates RPC with unregistered baker for Tenderbake protocols. *)
let test_delegates_on_unregistered_alpha ~contracts ?endpoint client =
Log.info "Test with a PKH that is not a registered baker contract" ;
let unregistered_baker = "tz1c5BVkpwCiaPHJBzyjg7UHpJEMPTYA1bHG" in
assert (not @@ List.mem unregistered_baker contracts) ;
let* _ =
RPC.Delegates.spawn_get ?endpoint ~hooks ~pkh:unregistered_baker client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_full_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_frozen_deposits
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_deactivated
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_delegated_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_delegated_contracts
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_grace_period
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_staking_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_voting_power
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
unit
(* Test the delegates RPC with unregistered baker for Emmy protocols. *)
let test_delegates_on_unregistered_hangzhou ~contracts ?endpoint client =
Log.info "Test with a PKH that is not a registered baker contract" ;
let unregistered_baker = "tz1c5BVkpwCiaPHJBzyjg7UHpJEMPTYA1bHG" in
assert (not @@ List.mem unregistered_baker contracts) ;
let* _ =
RPC.Delegates.spawn_get ?endpoint ~hooks ~pkh:unregistered_baker client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_deactivated
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_delegated_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_delegated_contracts
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_frozen_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_frozen_balance_by_cycle
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_grace_period
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_staking_balance
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
let* _ =
RPC.Delegates.spawn_get_voting_power
?endpoint
~hooks
~pkh:unregistered_baker
client
|> Process.check ~expect_failure:true
in
unit
let get_contracts ?endpoint client =
let*! _ = RPC.Contracts.get_all ?endpoint ~hooks client in
let* contracts = RPC.Delegates.get_all ?endpoint ~hooks client in
Lwt.return contracts
(* Test the delegates RPC for the specified protocol. *)
let test_delegates _test_mode_tag _protocol ?endpoint client =
let* contracts = get_contracts ?endpoint client in
let* () = test_delegates_on_registered_alpha ~contracts ?endpoint client in
test_delegates_on_unregistered_alpha ~contracts ?endpoint client
(* Test the votes RPC. *)
let test_votes _test_mode_tag _protocol ?endpoint client =
(* initialize data *)
let proto_hash = "ProtoDemoNoopsDemoNoopsDemoNoopsDemoNoopsDemo6XBoYp" in
let* () = Client.submit_proposals ~proto_hash client in
let* () = Client.bake_for_and_wait client in
(* RPC calls *)
let* _ = RPC.Votes.get_ballot_list ?endpoint ~hooks client in
let* _ = RPC.Votes.get_ballots ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_period ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_proposal ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_quorum ?endpoint ~hooks client in
let* _ = RPC.Votes.get_listings ?endpoint ~hooks client in
let* _ = RPC.Votes.get_proposals ?endpoint ~hooks client in
let* _ = RPC.Votes.get_successor_period ?endpoint ~hooks client in
let* _ = RPC.Votes.get_total_voting_power ?endpoint ~hooks client in
(* bake to testing vote period and submit some ballots *)
let* () = repeat 2 (fun () -> Client.bake_for_and_wait client) in
let* () = Client.submit_ballot ~key:"bootstrap1" ~proto_hash Yay client in
let* () = Client.submit_ballot ~key:"bootstrap2" ~proto_hash Nay client in
let* () = Client.submit_ballot ~key:"bootstrap3" ~proto_hash Pass client in
let* () = Client.bake_for_and_wait client in
(* RPC calls again *)
let* _ = RPC.Votes.get_ballot_list ?endpoint ~hooks client in
let* _ = RPC.Votes.get_ballots ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_period ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_proposal ?endpoint ~hooks client in
let* _ = RPC.Votes.get_current_quorum ?endpoint ~hooks client in
let* _ = RPC.Votes.get_listings ?endpoint ~hooks client in
let* _ = RPC.Votes.get_proposals ?endpoint ~hooks client in
let* _ = RPC.Votes.get_successor_period ?endpoint ~hooks client in
let* _ = RPC.Votes.get_total_voting_power ?endpoint ~hooks client in
unit
(* Test the various other RPCs. *)
let test_misc_protocol _test_mode_tag _protocol ?endpoint client =
let* _ =
RPC.Client.call ?endpoint ~hooks client
@@ RPC.get_chain_block_context_constants ()
in
let* _ = RPC.get_baking_rights ?endpoint ~hooks client in
let* _ = RPC.get_current_level ?endpoint ~hooks client in
let* _ = RPC.get_endorsing_rights ?endpoint ~hooks client in
let* _ = RPC.get_levels_in_current_cycle ?endpoint ~hooks client in
unit
let mempool_hooks =
let replace_variable string =
let replacements =
[
("edsig\\w{94}", "[SIGNATURE]");
("sig\\w{93}", "[SIGNATURE]");
("o\\w{50}", "[OPERATION_HASH]");
("B\\w{50}", "[BRANCH_HASH]");
("vh\\w{50}", "[BLOCK_PAYLOAD_HASH]");
]
in
List.fold_left
(fun string (replace, by) ->
Base.replace_string ~all:true (rex replace) ~by string)
string
replacements
in
{
Tezos_regression.hooks with
on_log = (fun output -> replace_variable output |> hooks.on_log);
}
let get_client_port client =
match
Client.get_mode client |> Client.mode_to_endpoint
|> Option.map Client.rpc_port
with
| Some port -> port
| None ->
Test.fail
"Client for mempool rpc tests should be initialized with a node or a \
proxy server only. Both have an endpoint and hence a RPC port so this \
should not happen."
let mempool_node_flags =
Node.
[
Synchronisation_threshold 0;
(* Node does not need to be synchronized with peers before being
bootstrapped *)
Connections 1
(* Number of connection allowed for each of our 2 nodes used in the
mempool tests *);
]
let bake_empty_block ?endpoint client =
let mempool = Client.empty_mempool_file () in
(* We defer to using a low-level command here since we enforce using protocol
in order to distinguish which form of the option we actually need, since
the name has changed. *)
Client.spawn_command
?endpoint
client
(["bake"; "for"; Constant.bootstrap1.alias; "--minimal-timestamp"]
@
let option_name = "--operations-pool" in
[option_name; mempool])
|> Process.check
(* Test the mempool RPCs: /chains/<chain>/mempool/...
Tested RPCs:
- GET pending_operations
- GET monitor_operations
- GET filter
- POST filter
Testing RPCs monitor_operations and pending_operations:
We create an applied operation and three operations that fail
to be applied. Each one of these failed operations has a different
classification to test every possibility of the monitor_operations and
pending_operations RPCs. The error classifications are the following :
- Branch_refused (Branch error classification in protocol)
- Branch_delayed (Temporary)
- Refused (Permanent)
- Outdated (Outdated)
The main goal is to have a record of the encoding of the different
operations returned by the RPC calls. This allows
us to detect undesired changes. *)
(* TODO: https://gitlab.com/tezos/tezos/-/issues/1900
Test the following RPCs:
- POST request_operations
- POST ban_operation
- POST unban_operation
- POST unban_all_operations
*)
(* [mode] is only useful insofar as it helps adapting the test to specific
* `Proxy mode constraint*)
let test_mempool _test_mode_tag protocol ?endpoint client =
let* node = Node.init mempool_node_flags in
let* () = Client.Admin.trust_address ?endpoint client ~peer:node in
let* () = Client.Admin.connect_address ?endpoint client ~peer:node in
let level = 1 in
let* _ = Node.wait_for_level node level in
let* node1_identity = Node.wait_for_identity node in
let* () = Client.Admin.kick_peer ~peer:node1_identity client in
(* Inject a transfer to increment the counter of bootstrap1. Bake with this
transfer to trigger the counter_in_the_past error for the following
transfer. *)
let* _ =
Operation.Manager.(inject ~force:true [make @@ transfer ()] client)
in
let* _ = Client.bake_for_and_wait ?endpoint client in
let* _ = bake_empty_block ?endpoint client in
(* Outdated operation after the second empty baking. *)
let* () = Client.endorse_for ~protocol ~force:true client in
let* _ = bake_empty_block ?endpoint client in
let monitor_path =
(* To test the monitor_operations rpc we use curl since the client does
not support streaming RPCs yet. *)
sf
"http://localhost:%d/chains/main/mempool/monitor_operations?applied=true&outdated=true&branch_delayed=true&refused=true&branch_refused=true"
(get_client_port client)
in
let proc_monitor =
(* monitor_operation rpc must be lanched before adding operations in order
to record them. *)
Process.spawn ~hooks:mempool_hooks "curl" ["-s"; monitor_path]
in
let*! counter =
RPC.Contracts.get_counter
~contract_id:Constant.bootstrap1.Account.public_key_hash
client
in
let counter = JSON.as_int counter in
(* Branch_refused op: counter_in_the_past *)
let* _ =
Operation.Manager.(inject ~force:true [make ~counter @@ transfer ()] client)
in
let*! counter =
RPC.Contracts.get_counter
~contract_id:Constant.bootstrap2.Account.public_key_hash
client
in
let counter = JSON.as_int counter in
(* Branch_delayed op: counter_in_the_future *)
let* _ =
Operation.Manager.(
inject
~force:true
[make ~source:Constant.bootstrap2 ~counter:(counter + 5) @@ transfer ()]
client)
in
(* Refused op: fees_too_low *)
let* _ =
Operation.Manager.(
inject
[
make ~source:Constant.bootstrap3 ~fee:0
@@ transfer ~dest:Constant.bootstrap2 ();
]
client)
in
(* Applied op *)
let* _ =
Operation.Manager.(
inject
[
make ~source:Constant.bootstrap4
@@ transfer ~dest:Constant.bootstrap2 ();
]
client)
in
let* () = Client.Admin.connect_address ?endpoint ~peer:node client in
let flush_waiter = Node_event_level.wait_for_flush node in
let* _ =
Prevalidator.bake_empty_block ~protocol ~endpoint:(Client.Node node) client
in
let* _ = flush_waiter in
let* _output_monitor = Process.check_and_read_stdout proc_monitor in
let* complete_mempool =
Mempool.get_mempool ?endpoint ~hooks:mempool_hooks client
in
let* _ =
Lwt_list.iter_s
(fun i ->
let* mempool =
Mempool.get_mempool
?endpoint
~hooks:mempool_hooks
~applied:(i = `Applied)
~refused:(i = `Refused)
~branch_delayed:(i = `Branch_delayed)
~branch_refused:(i = `Branch_refused)
~outdated:(i = `Outdated)
client
in
let may_get_field field ophs = if i = field then ophs else [] in
let expected_mempool =
Mempool.
{
applied = may_get_field `Applied complete_mempool.applied;
refused = may_get_field `Refused complete_mempool.refused;
outdated = may_get_field `Outdated complete_mempool.outdated;
branch_refused =
may_get_field `Branch_refused complete_mempool.branch_refused;
branch_delayed =
may_get_field `Branch_delayed complete_mempool.branch_delayed;
unprocessed = [];
}
in
Check.(
(expected_mempool = mempool)
Mempool.classified_typ
~error_msg:"Expected mempool %L, got %R") ;
unit)
[`Applied; `Refused; `Branch_delayed; `Branch_refused; `Outdated]
in
(* We spawn a second monitor_operation RPC that monitor operations
reclassified with errors. *)
let proc_monitor =
Process.spawn ~hooks:mempool_hooks "curl" ["-s"; monitor_path]
in
let* _ =
Prevalidator.bake_empty_block ~protocol ~endpoint:(Client.Node node) client
in
let* _output_monitor = Process.check_and_read_stdout proc_monitor in
(* Test RPCs [GET|POST /chains/main/mempool/filter] *)
let get_filter_variations () =
let get_filter ?include_default client =
RPC.Client.call ?endpoint ~hooks:mempool_hooks client
@@ RPC.get_chain_mempool_filter ?include_default ()
in
let* _ = get_filter client in
let* _ = get_filter ~include_default:true client in
let* _ = get_filter ~include_default:false client in
unit
in
let post_and_get_filter config_str =
let* _ =
RPC.Client.call ?endpoint ~hooks:mempool_hooks client
@@ RPC.post_chain_mempool_filter ~data:(Ezjsonm.from_string config_str) ()
in
get_filter_variations ()
in
let* _ = get_filter_variations () in
let* _ =
post_and_get_filter
{|{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false }|}
in
let* _ =
post_and_get_filter
{|{ "minimal_fees": "200", "allow_script_failure": true }|}
in
let* _ = post_and_get_filter "{}" in
unit
let start_with_acl address acl =
let* node =
Node.init
~rpc_host:address
~patch_config:(JSON.update "rpc" (JSON.put ("acl", acl)))
[]
in
let endpoint = Client.Node node in
Client.init ~endpoint ()
let test_network test_mode_tag _protocol ?endpoint client =
let test peer_id =
let call rpc = RPC.Client.call ?endpoint client rpc in
let* _ = call @@ RPC.get_network_connection peer_id in
let* _ = call RPC.get_network_greylist_clear in
(* Peers *)
let* _ = call RPC.get_network_peers in
let* _ = call @@ RPC.get_network_peer peer_id in
let* _ = call @@ RPC.get_network_peer_ban peer_id in
let* _ = call @@ RPC.get_network_peer_banned peer_id in
let* _ = call @@ RPC.get_network_peer_unban peer_id in
let* _ = call @@ RPC.get_network_peer_untrust peer_id in
let* _ = call @@ RPC.get_network_peer_trust peer_id in
(* Connections *)
let* points = call @@ RPC.get_network_points in
let point_id =
match points with
| (p, _) :: _ -> p
| _ -> Test.fail "Expected at least one point."
in
let* _ = call @@ RPC.get_network_point point_id in
let* _ = call @@ RPC.get_network_point_ban point_id in
let* _ = call @@ RPC.get_network_point_banned point_id in
let* _ = call @@ RPC.get_network_point_unban point_id in
let* _ = call @@ RPC.get_network_point_untrust point_id in
let* _ = call @@ RPC.get_network_point_trust point_id in
let* _ = call RPC.get_network_stat in
let* _ = call RPC.get_network_version in
let* _ = call RPC.get_network_versions in
unit
in
match test_mode_tag with
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server ->
Log.info "Skipping network RPCs" ;
unit
| `Light ->
(* In light mode, the node is already connected to another node: use that as peer *)
let* peers = RPC.Client.call ?endpoint client @@ RPC.get_network_peers in
let peer_id =
match peers with
| (p, _) :: _ -> p
| _ -> Test.fail "Expected at least one peer in light mode."
in
test peer_id
| `Proxy | `Client ->
(* Create a second node to get a peer for testing RPCs that are
parameterized by a point or peer id *)
let* node = Node.init ~name:"node" [Connections 1] in
let* () = Client.Admin.trust_address ?endpoint client ~peer:node in
let* () = Client.Admin.connect_address ?endpoint client ~peer:node in
let* _ = Node.wait_for_level node 1 in
let* client = Client.init ~endpoint:(Node node) () in
let* peer_id =
RPC.Client.call ~endpoint:(Node node) client RPC.get_network_self
in
test peer_id
let test_workers _test_mode_tag _protocol ?endpoint client =
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_worker_block_validator in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_workers_chain_validators
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_worker_chain_validator ()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_worker_chain_validator_ddb ()
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_worker_chain_validator_peers_validators ()
in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_workers_prevalidators in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_worker_prevalidator () in
unit
let test_misc_shell _test_mode_tag protocol ?endpoint client =
let protocol_hash = Protocol.hash protocol in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_errors in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_protocols in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_protocol protocol_hash in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_fetch_protocol protocol_hash
in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_stats_gc in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_stats_memory in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_config in
unit
let test_chain _test_mode_tag _protocol ?endpoint client =
let block_level = 3 in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_chain_blocks () in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_chain_chain_id () in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_invalid_blocks ()
in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_chain_block () in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_context_constants_errors ()
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_context_nonce block_level
in
let* _ =
(* Calls [/chains/main/blocks/head/context/sc_rollup] *)
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_context_sc_rollup ()
in
let* _ =
(* Calls [/chains/main/blocks/head/context/raw/bytes] *)
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_context_raw ~ctxt_type:Bytes ~value_path:[] ()
in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_chain_block_hash () in
let* _ = RPC.Client.call ?endpoint client @@ RPC.get_chain_block_header () in
let* _ =
(* Calls [/chains/main/blocks/head/header/protocol_data] *)
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_header_protocol_data ()
in
let* _ =
(* Calls [/chains/main/blocks/head/header/protocol_data/raw] *)
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_header_protocol_data_raw ()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_header_raw ()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_header_raw ()
in
let* _ =
let pkh = Constant.bootstrap1.public_key in
let prefix = String.sub pkh 0 10 in
(* TODO: https://gitlab.com/tezos/tezos/-/issues/3234
The result of this RPC endpoint is empty, but I guess it
should be a list containing [pkh]. The original python
test has the same result. *)
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_helper_complete prefix
in
let* _ =
let* () = Client.bake_for_and_wait client in
let* head_hash =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_hash ()
in
let prefix = String.sub head_hash 0 10 in
(* TODO: https://gitlab.com/tezos/tezos/-/issues/3234
The result of this RPC endpoint is empty, but I guess it
should be a list containing [head_hash]. The original python
test has the same result. *)
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_helper_complete prefix
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_live_blocks ()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_metadata ()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_operation_hashes ()
in
let* () =
let validation_pass = 3 in
let operation_offset = 0 in
let* () =
Client.transfer
~amount:Tez.one
~giver:"bootstrap1"
~receiver:"bootstrap2"
client
in
let* () = Client.bake_for_and_wait client in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_operation_hashes ()
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_operation_hashes_of_validation_pass validation_pass
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_operation_hash
~validation_pass
~operation_offset
()
in
let* _ =
RPC.Client.call ?endpoint client @@ RPC.get_chain_block_operations ()
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_operations_validation_pass ~validation_pass ()
in
let* _ =
RPC.Client.call ?endpoint client
@@ RPC.get_chain_block_operations_validation_pass
~validation_pass
~operation_offset
()
in
unit
in
unit
(* Test access to RPC regulated with an ACL. *)
let test_whitelist address () =
let whitelist =
JSON.annotate ~origin:"whitelist"
@@ `A
[
`O
[
("address", `String address);
( "whitelist",
`A [`String "GET /describe/**"; `String "GET /chains/**"] );
];
]
in
let* client = start_with_acl address whitelist in
let* success_resp = RPC.Client.call client @@ RPC.get_chain_blocks () in
let block_hash = JSON.geti 0 success_resp |> JSON.geti 0 |> JSON.as_string in
let* () =
if block_hash = "BLockGenesisGenesisGenesisGenesisGenesisf79b5d1CoW2" then
unit
else
Test.fail "Received an unexpected block hash from node: '%s'." block_hash
in
let* () =
Client.spawn_rpc GET ["network"; "connections"] client
|> Process.check_error ~exit_code:1
in
unit
let test_blacklist address () =
let blacklist =
JSON.annotate ~origin:"blacklist"
@@ `A
[
`O
[
("address", `String address);
("blacklist", `A [`String "GET /chains/**"]);
];
]
in
let* client = start_with_acl address blacklist in
let* () =
Client.spawn_rpc GET ["chains"; "main"; "blocks"] client
|> Process.check_error ~exit_code:1
in
let* _success_resp = RPC.Client.call client @@ RPC.get_network_connections in
unit
let binary_regression_test () =
let node = Node.create ~rpc_host:"127.0.0.1" [] in
let endpoint = Client.(Node node) in
let* () = Node.config_init node [] in
let* () = Node.identity_generate node in
let* () = Node.run node [] in
let* () = Node.wait_for_ready node in
let* json_client = Client.init ~endpoint ~media_type:Json () in
let* binary_client = Client.init ~endpoint ~media_type:Binary () in
let call_rpc client =
Client.spawn_rpc
~hooks
GET
["chains"; "main"; "blocks"; "head"; "header"; "shell"]
client
|> Process.check_and_read_stdout
in
let* json_result = call_rpc json_client in
let* binary_result = call_rpc binary_client in
let* decoded_binary_result =
Codec.decode ~name:"block_header.shell" binary_result
in
if JSON.unannotate decoded_binary_result = Ezjsonm.from_string json_result
then Lwt.return_unit
else Test.fail "Unexpected binary answer"
let test_node_binary_mode address () =
let node = Node.create ~rpc_host:address [] in
let endpoint = Client.(Node node) in
let* () = Node.config_init node [] in
let* () = Node.identity_generate node in
let* () = Node.run node [Media_type Binary] in
let* () = Node.wait_for_ready node in
let* client = Client.init ~endpoint ~media_type:Json () in
Client.spawn_rpc GET ["chains"; "main"; "blocks"] client
|> Process.check_error ~exit_code:1
let test_no_service_at_valid_prefix address () =
let node = Node.create ~rpc_host:address [] in
let endpoint = Client.(Node node) in
let* () = Node.config_init node [] in
let* () = Node.identity_generate node in
let* () = Node.run node [] in
let* () = Node.wait_for_ready node in
let* client = Client.init ~endpoint () in
let* () =
Client.spawn_rpc ~better_errors:true GET ["chains"; "main"] client
|> Process.check_error
~exit_code:1
~msg:
(rex
"Fatal error:\n\
\ No service found at this URL (but this is a valid prefix)*\n\
*")
in
unit
let register protocols =
Regression.register
~__FILE__
~title:"Binary RPC regression tests"
~tags:["rpc"; "regression"; "binary"]
binary_regression_test ;
let register protocols test_mode_tag =
let check_rpc_regression ?parameter_overrides ?nodes_args ~test_function
sub_group =
check_rpc_regression
~test_mode_tag
?parameter_overrides
?nodes_args
~test_function
sub_group
protocols
in
let check_rpc ?parameter_overrides ?nodes_args ~test_function sub_group =
check_rpc
~test_mode_tag
?parameter_overrides
?nodes_args
~test_function
sub_group
protocols
in
let consensus_threshold _protocol = [(["consensus_threshold"], Some "0")] in
check_rpc_regression
"contracts"
~test_function:test_contracts
~parameter_overrides:consensus_threshold ;
check_rpc_regression
"delegates"
~test_function:test_delegates
~parameter_overrides:consensus_threshold ;
check_rpc_regression
"votes"
~test_function:test_votes
~parameter_overrides:(fun protocol ->
(* reduced periods duration to get to testing vote period faster *)
if Protocol.number protocol >= 14 then
(* We need nonce_revelation_threshold < blocks_per_cycle for sanity
checks *)
[
(["blocks_per_cycle"], Some "4");
(["cycles_per_voting_period"], Some "1");
(["nonce_revelation_threshold"], Some "3");
]
else if Protocol.number protocol >= 13 then
[
(["blocks_per_cycle"], Some "4");
(["cycles_per_voting_period"], Some "1");
]
else
[
(["blocks_per_cycle"], Some "4");
(["blocks_per_voting_period"], Some "4");
]
@ consensus_threshold protocol) ;
check_rpc_regression
"misc_protocol"
~test_function:test_misc_protocol
~parameter_overrides:consensus_threshold ;
(match test_mode_tag with
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server | `Light -> ()
| _ ->
check_rpc_regression
"mempool"
~test_function:test_mempool
~nodes_args:mempool_node_flags) ;
check_rpc
"network"
~test_function:test_network
~parameter_overrides:consensus_threshold
~nodes_args:[Connections 1] ;
(match test_mode_tag with
(* No worker RPCs in these modes *)
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server -> ()
| _ ->
check_rpc
"workers"
~test_function:test_workers
~parameter_overrides:consensus_threshold) ;
(match test_mode_tag with
(* No misc shell RPCs in these modes *)
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server -> ()
| _ ->
check_rpc
"misc_shell"
~test_function:test_misc_shell
~parameter_overrides:consensus_threshold) ;
match test_mode_tag with
(* No chain RPCs in these modes *)
| `Client_data_dir_proxy_server | `Client_rpc_proxy_server -> ()
| _ ->
check_rpc
"chain"
~test_function:test_chain
~parameter_overrides:consensus_threshold
in
List.iter
(register protocols)
[
`Client;
`Light;
`Proxy;
`Client_data_dir_proxy_server;
`Client_rpc_proxy_server;
] ;
let addresses = ["localhost"; "127.0.0.1"] in
let mk_title list_type address =
Format.sprintf "Test RPC %s @@ %s" list_type address
in
List.iter
(fun addr ->
Test.register
~__FILE__
~title:(mk_title "whitelist" addr)
~tags:["rpc"; "acl"]
(test_whitelist addr) ;
Test.register
~__FILE__
~title:(mk_title "blacklist" addr)
~tags:["rpc"; "acl"]
(test_blacklist addr) ;
Test.register
~__FILE__
~title:(mk_title "no_service_at_valid_prefix" addr)
~tags:["rpc"]
(test_no_service_at_valid_prefix addr) ;
Test.register
~__FILE__
~title:(mk_title "node_binary_mode" addr)
~tags:["rpc"; "binary"]
(test_node_binary_mode addr))
addresses
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...