Module Tezt_mavryk.Node

Spawn Mavryk nodes and control them.

Convention: in this module, some functions implement node commands; those functions are named after those commands. For instance, Node.config_init corresponds to mavkit-node config init, and Node.run corresponds to mavkit-node run.

The arguments of those functions are also named after the actual arguments. For instance, ?network is named after --network, to make Node.config_init ~network:"carthagenet" look as close as possible to mavkit-node config init --network carthagenet.

Most options have default values which are not necessarily the default values of mavkit-node. Indeed, the latter are tailored for Mainnet, but here we use defaults which are tailored for the sandbox. In particular, the default value for ?network is "sandbox". However, if you specify an option such as ~network or ~history_mode, they are passed to the node unchanged, to reduce surprises.

These conventions are also followed in the Client module.

History modes for the node.

type history_mode =
  1. | Archive
  2. | Full of int option
  3. | Rolling of int option

The parameter for Full And Rolling mode is called additional_cycles.

For the Full (resp. Rolling) mode it controls the number of contexts (resp. blocks) we preserved behind the checkpoint (aka the no fork point). Default in sandbox mode is 2 and 5 for mainnet parameters (see preserved_cycles in the protocol parameters).

type media_type =
  1. | Json
  2. | Binary
  3. | Any

Values that can be passed to the node's --media-type argument

type argument =
  1. | Network of string
    (*

    --network

    *)
  2. | History_mode of history_mode
    (*

    --history-mode

    *)
  3. | Expected_pow of int
    (*

    --expected-pow

    *)
  4. | Singleprocess
    (*

    --singleprocess

    *)
  5. | Bootstrap_threshold of int
    (*

    --bootstrap-threshold (deprecated)

    *)
  6. | Synchronisation_threshold of int
    (*

    --synchronisation-threshold

    *)
  7. | Sync_latency of int
    (*

    --sync-latency

    *)
  8. | Connections of int
    (*

    --connections

    *)
  9. | Private_mode
    (*

    --private-mode

    *)
  10. | Disable_p2p_maintenance
    (*

    --disable-p2p-maintenance

    *)
  11. | Disable_p2p_swap
    (*

    --disable-p2p-swap

    *)
  12. | Peer of string
    (*

    --peer

    *)
  13. | No_bootstrap_peers
    (*

    --no-bootstrap-peers

    *)
  14. | Media_type of media_type
    (*

    --media-type

    *)
  15. | Metadata_size_limit of int option
    (*

    --metadata-size-limit

    *)
  16. | Metrics_addr of string
    (*

    --metrics-addr

    *)
  17. | Cors_origin of string
    (*

    --cors-origin

    *)
  18. | Disable_mempool
    (*

    --disable-mempool

    *)
  19. | Version
    (*

    --version

    *)
  20. | RPC_additional_addr of string
    (*

    --rpc-addr

    *)
  21. | RPC_additional_addr_external of string
    (*

    --external-rpc-addr

    *)
  22. | Max_active_rpc_connections of int
    (*

    --max-active-rpc-connections

    *)

Mavryk node command-line arguments.

Not all arguments are available here. Some are simply not implemented, and some are handled separately because they need special care. The latter are implemented as optional labeled arguments (e.g. ?net_port and ?data_dir).

About RPC_additional_addr, at least one RPC port is always passed by Node.run, which causes the RPC ports from the configuration file to be ignored. So these arguments are not written in the config file when using Node.init and kept in the list of arguments of the persistent state to make sure Node.run passes them too.

Singleprocess argument does not exist in the configuration file of the node. It is only known as a command-line option. Node.init will neither pass it to Node.config nor register it into node's arguments, but only use it for Node.run function.

type tls_config = {
  1. certificate_path : string;
  2. key_path : string;
}

A TLS configuration for the node: paths to a .crt and a .key file.

Passed to run like commands through the --rpc-tls argument.

type t

Mavryk node states.

val create : ?runner:Tezt_wrapper.Runner.t -> ?path:string -> ?name:string -> ?color:Tezt_wrapper.Log.Color.t -> ?data_dir:string -> ?event_pipe:string -> ?net_addr:string -> ?net_port:int -> ?advertised_net_port:int -> ?metrics_addr:string -> ?metrics_port:int -> ?rpc_external:bool -> ?rpc_host:string -> ?rpc_port:int -> ?rpc_tls:tls_config -> ?allow_all_rpc:bool -> ?max_active_rpc_connections:int -> argument list -> t

Create a node.

This function just creates the t value, it does not call identity_generate nor config_init nor run.

The standard output and standard error output of the node will be logged with prefix name and color color.

Default data_dir is a temporary directory which is always the same for each name.

Default event_pipe is a temporary file whose name is derived from name. It will be created as a named pipe so that node events can be received.

Default value for net_addr is either Constant.default_host if no runner is provided, or a value allowing the local Tezt program to connect to it if it is.

Default rpc_external is false. If rpc_external is true, the node will spawn a process for non-blocking RPCs.

Default values for net_port or rpc_port are chosen automatically with values starting from 16384 (configurable with `--starting-port`). They are used by config_init and by functions from the Client module. They are not used by run, so if you do not call config_init or generate the configuration file through some other means, your node will not listen.

Default value for allow_all_rpc is true.

Default value for max_active_rpc_connections is 500.

The argument list is a list of configuration options that the node should run with. It is passed to the first run of mavkit-node config init. It is also passed to all runs of mavkit-node run that occur before mavkit-node config init. If Expected_pow is given, it is also used as the default value for identity_generate.

If runner is specified, the node will be spawned on this runner using SSH.

val add_argument : t -> argument -> unit

Add an argument to a node as if it was passed to create.

The argument is passed to the next run of mavkit-node config init. It is also passed to all runs of mavkit-node run that occur before the next mavkit-node config init.

There are some exceptions, see definition of type argument.

val add_peer : t -> t -> unit

Add a --peer argument to a node.

Usage: add_peer node peer

Same as add_argument node (Peer "<HOST>:<PORT>") where <HOST> is given by Runner.address and <PORT> is the P2P port of peer.

val get_peers : t -> string list

Returns the list of address of all Peer <addr> arguments.

val add_peer_with_id : t -> t -> unit Lwt.t

Add a --peer argument to a node.

Usage: add_peer node peer

Same as add_argument node (Peer "<HOST>:<PORT>#<ID>") where <HOST> is given by Runner.address, <PORT> is the P2P port and <ID> is the identity of peer.

val remove_peers_json_file : t -> unit

Removes the file peers.json that is at the root of data-dir. This file contains the list of peers known by the node.

val point_and_id : ?from:t -> t -> string Lwt.t

Get the P2P point and id for a node.

Return "<ADDRESS>:<PORT>#<ID>" where <PORT> is the P2P port and <ID> is the identity of the node.

<ADDRESS> is obtained using Runner.address runner with ?from being the runner of from and runner is the runner of the node. In other words it is the address where from can contact node.

val point : ?from:t -> t -> string * int

Get the P2P point of a node.

Return (<ADDRESS>,<PORT>) where <PORT> is the P2P port and <ADDRESS> is the reachable address of the node.

<ADDRESS> is obtained using Runner.address runner with ?from being the runner of from and runner is the runner of the node. In other words it is the address where from can contact node.

val point_str : ?from:t -> t -> string

Same as point but returns a string representation of the point "<ADDRESS>:<PORT>".

val name : t -> string

See Daemon.Make.name

val net_port : t -> int

Get the network port given as --net-addr to a node.

val advertised_net_port : t -> int option

Get the network port given as --advertised-net-port to a node.

val rpc_scheme : t -> string

Get the RPC scheme of a node.

Returns https if node is started with --rpc-tls, otherwise http

val rpc_external : t -> bool

Returns True if RPCs are handled by a dedicated process.

val rpc_host : t -> string

Get the RPC host given as --rpc-addr to a node.

val rpc_port : t -> int

Get the RPC port given as --rpc-port to a node.

val rpc_endpoint : t -> string

Get the node's RPC endpoint URI.

These are composed of the node's --rpc-tls, --rpc-addr and --rpc-port arguments.

val data_dir : t -> string

Get the data-dir of a node.

val runner : t -> Tezt_wrapper.Runner.t option

Get the runner associated to a node.

Return None if the node runs on the local machine.

val check_error : ?exit_code:int -> ?msg:Tezt_wrapper.Base.rex -> t -> unit Lwt.t

Wait until a node terminates and check its status.

If the node is not running, or if the process returns an exit code which is not exit_code, or if msg does not match the stderr output, fail the test.

If exit_code is not specified, any non-zero code is accepted. If no msg is given, the stderr is ignored.

val wait : t -> Unix.process_status Lwt.t

Wait until a node terminates and return its status. If the node is not running, make the test fail.

val terminate : ?timeout:float -> t -> unit Lwt.t

Send SIGTERM and wait for the process to terminate.

Default timeout is 30 seconds, after which SIGKILL is sent.

val kill : t -> unit Lwt.t

Send SIGKILL and wait for the process to terminate.

Commands

val identity_generate : ?expected_pow:int -> t -> unit Lwt.t

Run mavkit-node identity generate.

val spawn_identity_generate : ?expected_pow:int -> t -> Tezt_wrapper.Process.t

Same as identity_generate, but do not wait for the process to exit.

val show_history_mode : history_mode -> string

Convert an history mode into a string.

The result is suitable to be passed to the node on the command-line.

val config_init : t -> argument list -> unit Lwt.t

Run mavkit-node config init.

val config_update : t -> argument list -> unit Lwt.t

Run mavkit-node config update.

val config_reset : t -> argument list -> unit Lwt.t

Run mavkit-node config reset.

val config_show : t -> Tezt_wrapper.JSON.t Lwt.t

Run mavkit-node config show. Returns the node configuration.

module Config_file : sig ... end

Node configuration files.

val spawn_config_init : t -> argument list -> Tezt_wrapper.Process.t

Same as config_init, but do not wait for the process to exit.

val spawn_config_update : t -> argument list -> Tezt_wrapper.Process.t

Same as config_update, but do not wait for the process to exit.

val spawn_config_reset : t -> argument list -> Tezt_wrapper.Process.t

Same as config_reset, but do not wait for the process to exit.

type snapshot_history_mode =
  1. | Rolling_history
  2. | Full_history

A snapshot history mode for exports

type export_format =
  1. | Tar
  2. | Raw

A snapshot file format for exports

val snapshot_export : ?history_mode:snapshot_history_mode -> ?export_level:int -> ?export_format:export_format -> t -> string -> unit Lwt.t

Run mavkit-node snapshot export.

val spawn_snapshot_export : ?history_mode:snapshot_history_mode -> ?export_level:int -> ?export_format:export_format -> t -> string -> Tezt_wrapper.Process.t

Same as snapshot_export, but do not wait for the process to exit.

val snapshot_info : ?json:bool -> t -> string -> unit Lwt.t

Run mavkit-node snapshot info.

val spawn_snapshot_info : ?json:bool -> t -> string -> Tezt_wrapper.Process.t

Same as snapshot_info, but do not wait for the process to exit.

val snapshot_import : ?no_check:bool -> ?reconstruct:bool -> t -> string -> unit Lwt.t

Run mavkit-node snapshot import.

val spawn_snapshot_import : ?no_check:bool -> ?reconstruct:bool -> t -> string -> Tezt_wrapper.Process.t

Same as snapshot_import, but do not wait for the process to exit.

val reconstruct : t -> unit Lwt.t

Run mavkit-node reconstruct.

val spawn_reconstruct : t -> Tezt_wrapper.Process.t

Same as reconstruct, but do not wait for the process to exit.

val run : ?patch_config:(Tezt_wrapper.JSON.t -> Tezt_wrapper.JSON.t) -> ?on_terminate:(Unix.process_status -> unit) -> ?event_level:Daemon.Level.default_level -> ?event_sections_levels:(string * Daemon.Level.level) list -> t -> argument list -> unit Lwt.t

Spawn mavkit-node run.

The resulting promise is fulfilled as soon as the node has been spawned. It continues running in the background.

event_level specifies the verbosity of the file descriptor sink. This must be at least `Notice, which is the level of event "node_is_ready.v0", needed for wait_for_ready. The default value is `Info which is also the default event level of the node.

event_sections_levels specifies the verbosity for events in sections whose prefix is in the list. For instance ~event_sections_levels:[("prevalidator", `Debug); ("validator.block", `Debug)] will activate the logs at debug level for events whose section starts with "prevalidator" or "validator.block". See Mavryk_stdlib_unix.File_descriptor_sink and the logging documentation for a more precise semantic.

val replay : ?on_terminate:(Unix.process_status -> unit) -> ?event_level:Daemon.Level.default_level -> ?event_sections_levels:(string * Daemon.Level.level) list -> ?strict:bool -> ?blocks:string list -> t -> unit Lwt.t

Spawn mavkit-node replay.

Same as run but for the replay command. In particular it also supports events. One key difference is that the node will eventually stop.

Note that the `--network` argument is infered by the `node replay` command itself, thanks to the configuration value.

See run for a description of the arguments.

Events

exception Terminated_before_event of {
  1. daemon : string;
  2. event : string;
  3. where : string option;
}
val wait_for_ready : t -> unit Lwt.t

Wait until the node is ready.

More precisely, wait until a node_is_ready event occurs. If such an event already occurred, return immediately.

val wait_for_level : t -> int -> int Lwt.t

Wait for a given chain level.

More precisely, wait until a head_increment or branch_switch with a level greater or equal to the requested level occurs. If such an event already occurred, return immediately.

val get_last_seen_level : t -> int

Get the current known level of the node.

Returns 0 if the node is not running or if no head_increment or branch_switch event was received yet. This makes this function equivalent to wait_for_level node 0 except that it does not actually wait for the level to be known.

Note that, as the node's status is updated only on head increments, this value is wrong for instance right after a node restart or snapshot import. Therefore it is recommended to use the function get_level instead, which does not have this problem. A use case for this function is to check for a level increase, when the exact level does not matter, and get_level's promise may not resolve.

val get_level : t -> int Lwt.t

Return a promise that is fulfilled as soon as the node is running and its level is known, which is then the value of the promise.

If the node is not running or if no head_increment or branch_switch event was received yet, then wait until one of these events occur. It is equivalent to wait_for_level node 0, and thus avoids the pitfalls of getting a misleading 0 value.

val wait_for_identity : t -> string Lwt.t

Wait for the node to read its identity.

More precisely, wait until a read_identity event occurs. If such an event already occurred, return immediately.

Return the identity.

val wait_for_request : request:[< `Flush | `Inject | `Notify | `Arrived ] -> t -> unit Lwt.t

wait_for_request ?level ~request node waits for request event on the node.

val wait_for_full : ?where:string -> t -> string -> (Tezt_wrapper.JSON.t -> 'a option) -> 'a Lwt.t

See Daemon.Make.wait_for_full.

val wait_for : ?where:string -> t -> string -> (Tezt_wrapper.JSON.t -> 'a option) -> 'a Lwt.t

See Daemon.Make.wait_for.

val wait_for_connections : t -> int -> unit Lwt.t

Wait for a node to receive a given number of connections.

wait_for_connections node n waits until node receives n "connection" Chain validator events.

val wait_for_disconnections : t -> int -> unit Lwt.t

Wait for a node to receive a given number of disconnections.

wait_for_disconnections node n waits until node receives n "disconnection" Chain validator events.

type event = {
  1. name : string;
  2. value : Tezt_wrapper.JSON.t;
  3. timestamp : float;
}

Raw events.

val on_event : t -> (event -> unit) -> unit

See Daemon.Make.on_event.

val log_events : ?max_length:int -> t -> unit

See Daemon.Make.log_events.

type observe_memory_consumption =
  1. | Observe of unit -> int option Lwt.t
val memory_consumption : t -> observe_memory_consumption Lwt.t

See Daemon.Make.memory_consumption.

High-Level Functions

val init : ?runner:Tezt_wrapper.Runner.t -> ?path:string -> ?name:string -> ?color:Tezt_wrapper.Log.Color.t -> ?data_dir:string -> ?event_pipe:string -> ?net_port:int -> ?advertised_net_port:int -> ?metrics_addr:string -> ?metrics_port:int -> ?rpc_external:bool -> ?rpc_host:string -> ?rpc_port:int -> ?rpc_tls:tls_config -> ?event_level:Daemon.Level.default_level -> ?event_sections_levels:(string * Daemon.Level.level) list -> ?patch_config:(Tezt_wrapper.JSON.t -> Tezt_wrapper.JSON.t) -> ?snapshot:(string * bool) -> argument list -> t Lwt.t

Initialize a node.

This creates a node, runs identity_generate, config_init and run, then waits for the node to be ready, and finally returns the node.

Arguments are passed to config_init. If you specified Expected_pow, it is also passed to identity_generate. Arguments are not passed to run. If you do not wish the arguments to be stored in the configuration file (which will affect future runs too), do not use init.

When patch_config is provided, the function is used to patch the configuration file generated by config_init before the call to run. This argument should only be used to test configuration options that cannot be set with command-line arguments.

To import a snapshot before calling run, specify ~snapshot:(file, do_reconstruct), where file is the path to the snapshot. If reconstruct is true, then --reconstruct is passed to the import command.

val send_raw_data : t -> data:string -> unit Lwt.t

send_raw_data node ~data writes ~data using an IP socket on the net port of node.

val upgrade_storage : t -> unit Lwt.t

upgrade_storage node upprades the given node storage.

val get_version : t -> string Lwt.t

Run mavkit-node --version and return the node's version.

val as_rpc_endpoint : t -> Endpoint.t

Expose the RPC server address of this node as a foreign endpoint.

module RPC : sig ... end