- 16.02.2021: Proposed.
One major problem with planning for the evolution of Hermes is that presently there is insufficient clarity regarding its requirements. It is not known who are the typical Hermes users (is it human operators or automated pipelines?), and what are their primary use-cases.
This ADR proposes a few use-cases that seem interesting from the point of view of a general target base of users, and which will hopefully be a subset of the requirements of (any) future users.
Three elements that provide further context for this discussion are:
-
Hermes is still at an early stage of implementation, so these use-cases are not set in stone.
-
Some concrete use-cases are starting to emerge (#628), which Hermes v0.1.0 either does not cover altogether, or covers poorly (e.g., because of inconsistent UX), thus informing this proposal.
-
Hermes is one of three relayer binaries that are being developed roughly in parallel. The other two are being developed in Go and Typescript, respectively (see the references section). In this context, it is plausible that Hermes will focus on performance, robustness, and richness of features on a longer term.
This is a summary of the use-cases (commands) discussed in the rest of this ADR.
Note that the commands below omit the binary name hermes
, to keep the command
length to a minimum.
To create and update a client:
create client <host-chain-id> <target-chain-id>
- Optional params:
[--clock-drift <millis>] [--trusting-period <days>] [--trust-threshold <numerator/denominator>]
- Optional params:
update client <host-chain-id> <client-id>
To create a connection:
create connection <chain-a-id> <chain-b-id>
- Optional:
[--delay <delay>]
- Optional:
create connection <chain-a-id> --client-a <client-a-id> --client-b <client-b-id>
- Optional:
[--delay <delay>]
- Optional:
To create a channel:
create channel <chain-a-id> <chain-b-id> --port-a <port-id> --port-b <port-id>
- Optional:
[--order <order>] [--version <version>]
- Optional:
create channel <chain-a-id> --connection-a <connection-id> --port-a <port-id> --port-b <port-id>
- Optional:
[--order <order>] [--version <version>]
- Optional:
To start packet relaying:
start <chain-a-id> <chain-b-id> --port-a <port-id> --port-b <port-id>
- Optional:
[--order <order>] [--version <version>]
- Optional:
start <chain-a-id> --connection-a <connection-id> --port-a <port-id> --port-b <port-id>
- Optional:
[--order <order>] [--version <version>]
- Optional:
start <chain-a-id> --channel-a <channel-id> --port-a <port-id>
For finishing pre-initialized, but unfinished object handshakes, for connection and channel:
establish connection <chain-a-id> --connection-a <connection-id>
establish channel <chain-a-id> --channel-a <channel-id> --port-a <port-id>
The primary goal for the uses-cases we decided to cover is to prevent situations where users could get stuck. For example, the output of a command may be unclear, or there may be an error and thereby some CLI command finishes partially, or two relayers concurrently try to perform some operation(s) and interfere with each other, resulting in a chain state that is obscure to the user, and then the user could consequently be stuck.
The first of the patterns below seeks to help "unblock" a user. The second pattern is a variation on the first; this permits more efficiency because it allows the reuse of previously-created objects in the creation of new objects on a chain (e.g., reuse a client in the creation of a connection, or reuse a connection in the creation of a new channel).
We propose two basic patterns that Hermes should be able to fulfil.
-
Simple invocations to perform basic actions.
- By action here we mean doing the complete handshake for an object from scratch (specifically connection or channel) on two chains, or relaying packets between two chains.
- The focus here is for the command to include retrying mechanisms (perform it robustly) and have the simplest interface.
-
Allow reusing of pre-existing state for basic commands.
- The pre-existing state could be a client with some specific trust options, for instance, and in this case Hermes would provide support for creating a connection that uses this specific client.
- This pattern should also include a retrying mechanism.
Applying the above patterns to a few cases, we get the following concrete commands that Hermes v0.2.0 should fulfil.
- Minimal invocation: this will create the client from scratch:
create client <host-chain-id> <target-chain-id> [--clock-drift <millis>] [--trusting-period <days>] [--trust-threshold <numerator/denominator>]
Details:
Submits a transaction of type client create to chain
<host-chain-id>
(sometimes called the destination chain of this
transaction). The new client will be verifying headers for
chain <target-chain-id>
(often called the source chain).
See also the limitations section discussing the optional security parameters for this command.
- Update a client:
update client <host-chain-id> <client-id>
Details:
Submits a transaction to chain id <host-chain-id>
to update the client having
identifier <client-id>
with new consensus state from up-to-date headers.
Hermes will automatically infer the target chain of this client from
the client state.
- Upgrade a client:
upgrade client <host-chain-id> <client-id>
Details:
Submits a transaction to chain id <host-chain-id>
to upgrade the client having
identifier <client-id>
.
Hermes will automatically infer the target chain of this client from
the client state.
- Upgrade all clients that target a specific chain:
upgrade clients <target-chain-id>
Details:
Submits a transaction to upgrade clients of all chains in the config that target
chain id <target-chain-id>
.
- Minimal invocation: this will create the connection from scratch, using new clients:
create connection <chain-a-id> <chain-b-id> [--delay <delay>]
Details:
Starts a transaction to perform the connection open handshake protocol between
two chains.
The chains are called symbolically a
and b
, hence the option names
<chain-a-id>
and <chain-b-id>
. In all handshakes, Hermes submits the first
step (typically called init, e.g., ConnOpenInit
), to side a
, then the
second step (e.g., ConnOpenTry
) to side b
, and so on.
The optional parameter --delay
is the delay period that the new connection
should have. Note also the limitations around the
delay_period
feature.
- Reusing pre-existing state, concretely, with existing clients:
create connection <chain-a-id> --client-a <client-id> --client-b <client-id> [--delay <delay>]
Details:
Similar to the previous command, this command will perform the connection
open handshake protocol, but will reuse the client with identifier from
option --client-a
. This client is expected to exist on chain <chain-a-id>
.
The target chain of this client is identified in the
client state (concretely, the target chain is represented under
chain_id
field of the client state), which provides the identifier for the
side b
of the new connection. On the side b
chain, this command will
establish the connection using the client with identifier from the option
--client-b
, which must be verifying headers for chain <chain-a-id>
.
- With new connection and clients:
create channel <chain-a-id> <chain-b-id> --port-a <port-id> --port-b <port-id> [--order <order>] [--version <version>]
- With existing specific connection:
create channel <chain-a-id> --connection-a <connection-id> --port-a <port-id> --port-b <port-id> [--order <order>] [--version <version>]
- relay packets over a new channel, new connection, and new clients:
start <chain-a-id> <chain-b-id> --port-a <port-id> --port-b <port-id> [--order <order>] [--version <version>]
- relay packets over a new channel that re-uses an existing connection:
start <chain-a-id> --connection-a <connection-id> --port-a <port-id> --port-b <port-id> [--order <order>] [--version <version>]
- relay packets over an existing channel:
start <chain-a-id> --channel-a <channel-id> --port-a <port-id>
These commands serve the purpose of covering certain corner-cases where a handshake may be partially started.
- Finalize handshake for partially established connection:
establish connection <chain-a-id> --connection-a <connection-id>
- Finalize handshake for partially established channel:
establish channel <chain-a-id> --channel-a <channel-id> --port-a <port-id>
By default, the command will provide human-readable output, i.e., pretty
printing.
In practice, the final result of a Hermes command is captured in an
Output structure that has support for JSON serialization. To
enable JSON, we add a configuration parameter log_json
. The global section
of the config file will look as follows:
[global]
log_level = 'error'
log_json = 'false'
By default, this parameter is false
. When set to true
, all the Hermes output
will be in JSON.
Partially implemented.
- Simpler, more accurate CLI invocation: "create" is more precise than "tx" or "handshake"
- Improved output for human operators.
- Some commands will possibly turn out to be useless.
- Requires some rethinking of the Relayer architecture (mainly because of the limitations surrounding light clients.)
- Relayer in Go: https://github.com/cosmos/relayer
- Relayer in Typescript: https://github.com/confio/ts-relayer