- 2020-07-22: First draft.
This document provides a basic rundown of the structure of this repository, plus some plans for its evolution.
This repository comprises a Rust implementation of the IBC suite of protocols. To complement this implementation, this repository also comprises specifications, primarily written in TLA+, and sometimes in English.
At the moment we are invested mostly in the development of a relayer and several important modules (client, connection, channel, and packets). Eventually, we hope to cover the full IBC suite.
The ibc-rs
repository comprises three broad parts:
- The codebase for the IBC relayer implementation in Rust is in
relayer/
, which consists of craterelayer-cli
(the frontend application of the relayer) as well as craterelayer
(the core relayer functionality). - The codebase for IBC modules is in
modules/
, making up the crate calledrelayer-modules
. - English and TLA+ specs reside under
docs/spec
, classified by the component they target, e.g., relayer or connection handshake.
Following the work in #23, the crate
ibc-proto
(originally in a separate repo and documented here)
shall also become absorbed into the present repo.
In the following, we discuss the current state and proposed evolution of each of the Rust crates.
The basic concern of this crate is to provide user-facing functionality for the IBC relayer. This means implementing a CLI application that dispatches a command to a specific part of the relayer, and then outputs the result of executing that command. This crate builds on Abscissa to simplify command line parsing, application process lifecycle, and error handling.
This crate can accept various sub-commands, e.g. query
a chain for some specific part of their store, start
the
relayer, or start the light
client for a given chain. Note that most commands can be further refined with parameters
(for instance, the query
command can be issued for a connection
or channel
or client
). The bulk of data types
and logic resides in relayer/cli/commands
, grouped by each specific command.
This crate implements the core responsibilities of an IBC relayer. Briefly speaking, there are 3 high-level requirements on a IBC relayer, in no particular order:
- R1. ability to interface with IBC-enabled chains, with the purpose of reading their state and submitting transactions to these chains;
- R2. ability to run a light client for IBC-enabled chains, with the purpose of verifying headers and state of these chains;
- R3. implement the IBC relayer algorithms (ICS 018).
Some functionality described above overlaps with functionality of IBC Modules. For instance, some logic
that the relayer implements for handling connection handshakes (in ICS18) overlaps with logic in the IBC module specific
for connections (ICS3). Given this overlap, the relayer-modules
crate serves as the "ground truth" implementing the
said logic, while the relayer
crate has a natural dependency on relayer-modules
.
In addition to the dependency on the IBC Modules, the relayer also depends on the tendermint-rs
crate. This is
useful in particular for interfacing with the light client implementation from this crate, as well as core data types
such as SignedHeader
, Validator
, or ValidatorSet
.
ADR 002 captures more specific details on the relayer architecture.
The canonical IBC specification is modular in the sense of grouping different components of the specification in modules; for instance, specification ICS03 pertains to the abstraction of IBC connections and the IBC connection handshake protocol, while ICS04 pertains to IBC channels and packets. We group the code in this crate to reflect the modular separation in the canonical IBC specification.
A few common patterns we employ in this crate are as follows.
Many IBC protocols involve the receiving and processing of messages.
The protocols for establishing a connection (ICS03) or a channel (ICS04), for example, comprise
the processing of four different types of messages each.
In particular, the data structures representing these messages for connection handshake are MsgConnectionOpenInit
,
MsgConnectionOpenTry
, MsgConnectionOpenAck
, and MsgConnectionOpenConfirm
.
The creation and validation of protocol messages for each protocol resides in msgs.rs
within the respective ICS.
Each of these messages should implement the trait pub trait Msg
, ensuring that all messages implement a basic
interface allowing them to be routed correctly (via the IBC routing module and with the help of the route()
method)
or support basic validation.
Each ICS enumerates specific errors that may occur within icsX_NAME/error.rs
.
The error-handling pattern here build on thiserror and
anomaly for capturing the context of errors plus backtraces (optional).
Generally speaking, an IBC module constructs and propagates errors to the caller by two patterns:
return Err(Kind::MissingCounterparty.into())
or if a context can be supplied this is preferable:
return Err(Kind::InvalidConnectionHopsLength
.context("validate channel")
.into());
where the ICS itself defines Kind::InvalidConnectionHopsLength
and Kind::MissingCounterparty
.
See the details for the crate ibc-proto
below.
The ibc-proto
library gives a developer access to the Cosmos SDK IBC proto-defined structs directly in Rust.
The canonical IBC structs reside presently in cosmos-sdk,
defined in a proto3 syntax.
We compile these structs via prost directly to .rs files and import them into the other crates typically under the same
name prefixed with "Raw", for example:
use ibc_proto::channel::Channel as RawChannel;
For any Raw data type that is defined in ibc-proto
we implement the DomainType
trait, which serves as a translation
& validation layer between the proto ("Raw") types and the domain types. For example, for a Channel
we do as follows:
impl DomainType<RawChannel> for ChannelEnd {}
impl TryFrom<RawChannel> for ChannelEnd {
type Error = anomaly::Error<Kind>;
fn try_from(value: RawChannel) -> Result<Self, Self::Error> {
// Translate, validate each field from RawChannel into a Channel.
}
}
impl From<ChannelEnd> for RawChannel {
fn from(value: ChannelEnd) -> Self {
// Translate Channel into a RawChannel
}
}
This issue #130 is a good starting place for more context
on ibc-proto
.
The following resources serve as reference implementations or specifications that we use to guide the development of the present crates:
For the IBC relayer:
- A first implementation of the IBC relayer in Golang is under active development at iqlusioninc/relayer.
- The English specification of the relayer algorithm is captured in the ICS018 spec.
For IBC modules:
- A Golang implementation of IBC modules is under active development at cosmos/ibc-go.
- The English specifications for IBC modules reside in cosmos/ibc.
Proposed
Not applicable.