The Interblockchain Communication Protocol- An Overview
The Interblockchain Communication Protocol- An Overview
Christopher Goes
Interchain GmbH
Berlin, Germany
cwgoes@interchain.berlin
Abstract—The interblockchain communication proto- cross-shard communication [2]. This approach possesses
col (IBC) is an end-to-end, connection-oriented, state- advantages in simplicity and predictability, but faces hard
ful protocol for reliable, ordered, and authenticated technical problems in assuring the validity of state transi-
communication between modules on separate dis-
arXiv:2006.15918v1 [cs.DC] 29 Jun 2020
tributed ledgers. IBC is designed for interoperation tions [3], requires the adherence of all shards to a single
between heterogenous ledgers arranged in an unknown, validator set (or randomly elected subset thereof) and a
dynamic topology, operating with varied consensus single virtual machine, and faces challenges in upgrading
algorithms and state machines. The protocol realises itself over time due to the necessity of reaching global
this by specifying the sufficient set of data structures, consensus on alterations to the network topology or ledger
abstractions, and semantics of a communication pro-
tocol which once implemented by participating ledgers ruleset. Additionally, such sharded systems are brittle: if
will allow them to safely communicate. IBC is payload- the fault tolerance threshold is exceeded, the system needs
agnostic and provides a cross-ledger asynchronous com- to coordinate a global halt & restart, and possibly initiate
munication primitive which can be used as a constituent complex state transition rollback procedures — it is not
building block by a wide variety of applications. possible to safely isolate Byzantine portions of the network
Index Terms—ibc; interblockchain; dlt graph and continue operation.
The interblockchain communication protocol (IBC) pro-
I. Introduction vides a mechanism by which separate, sovereign replicated
ledgers can safely, voluntarily interact while sharing only a
By virtue of their nature as replicated state machines minimum requisite common interface. The protocol design
across which deterministic execution and thus continued approaches a differently formulated version of the scal-
agreement on an exact deterministic ruleset must be ing and interoperability problem: enabling safe, reliable
maintained, individual distributed ledgers are limited in interoperation of a network of heterogeneous distributed
their throughput & flexibility, must trade off application- ledgers, arranged in an unknown topology, preserving data
specific optimisations for general-purpose capabilities, and secrecy where possible, where the ledgers can diversify,
can only offer a single security model to applications develop, and rearrange independently of each other or
built on top of them. In order to support the transaction of a particular imposed topology or ledger design. In a
throughput, application diversity, cost efficiency, and fault wide, dynamic network of interoperating ledgers, sporadic
tolerance required to facilitate wide deployment of dis- Byzantine faults are expected, so the protocol must also
tributed ledger applications, execution and storage must detect, mitigate, and contain the potential damage of
be split across many independent ledgers which can run Byzantine faults in accordance with the requirements of
concurrently, upgrade independently, and each specialise the applications and ledgers involved without requiring the
in different ways, in a manner such that the ability of use of additional trusted parties or global coordination.
different applications to communicate with one another,
To facilitate this heterogeneous interoperation, the in-
essential for permissionless innovation and complex multi-
terblockchain communication protocol utilises a bottom-
part contracts, is maintained.
up approach, specifying the set of requirements, functions,
One multi-ledger design direction is to shard a single and properties necessary to implement interoperation be-
logical ledger across separate consensus instances, referred tween two ledgers, and then specifying different ways in
to as “shards”, which execute concurrently and store which multiple interoperating ledgers might be composed
disjoint partitions of the state. In order to reason globally which preserve the requirements of higher-level protocols.
about safety and liveness, and in order to correctly route IBC thus presumes nothing about and requires nothing
data and code between shards, these designs must take a of the overall network topology, and of the implement-
“top-down approach” — constructing a particular network ing ledgers requires only that a known, minimal set of
topology, usually a single root ledger and a star or tree functions with specified properties are available. Ledgers
of shards, and engineering protocol rules and incentives within IBC are defined as their light client consensus
to enforce that topology. Message passing can then be validation functions, thus expanding the range of what a
implemented on top of such a sharded topology by sys- “ledger” can be to include single machines and complex
tems such as Polkadot’s XCMP [1] and Ethereum 2.0’s consensus algorithms alike. IBC implementations are ex-
pected to be co-resident with higher-level modules and IBC, and sketch an example application-level protocol for
protocols on the host ledger. Ledgers hosting IBC must fungible token transfer. Finally, we recount testing and
provide a certain set of functions for consensus transcript deployment efforts of the protocol thus far. Appendices
verification and cryptographic commitment proof gener- include pseudocode for the connection handshake, channel
ation, and IBC packet relayers (off-ledger processes) are handshake, and packet relay algorithms.
expected to have access to network protocols and physical
data-links as required to read the state of one ledger and II. Protocol scope & properties
submit data to another.
The data payloads in IBC packets are opaque to the A. Scope
protocol itself — modules on each ledger determine the
semantics of the packets which are sent between them. For IBC handles authentication, transport, and ordering of
cross-ledger token transfer, packets could contain fungible opaque data packets relayed between modules on separate
token information, where assets are locked on one ledger ledgers — ledgers can be run on solo machines, replicated
to mint corresponding vouchers on another. For cross- by many nodes running a consensus algorithm, or con-
ledger governance, packets could contain vote information, structed by any process whose state can be verified. The
where accounts on one ledger could vote in the governance protocol is defined between modules on two ledgers, but
system of another. For cross-ledger account delegation, designed for safe simultaneous use between any number of
packets could contain transaction authorisation informa- modules on any number of ledgers connected in arbitrary
tion, allowing an account on one ledger to be controlled topologies.
by an account on another. For a cross-ledger decentralised
exchange, packets could contain order intent information B. Interfaces
or trade settlement information, such that assets on differ-
ent ledgers could be exchanged without leaving their host IBC sits between modules — smart contracts, other ledger
ledgers by transitory escrow and a sequence of packets. components, or otherwise independently executed pieces of
application logic on ledgers — on one side, and underlying
This bottom-up approach is quite similar to, and directly consensus protocols, blockchains, and network infrastruc-
inspired by, the TCP/IP specification [4] for interoperabil- ture (e.g. TCP/IP), on the other side.
ity between hosts in packet-switched computer networks.
Just as TCP/IP defines the protocol by which two hosts IBC provides to modules a set of functions much like the
communicate, and higher-level protocols knit many bidi- functions which might be provided to a module for inter-
rectional host-to-host links into complex topologies, IBC acting with another module on the same ledger: sending
defines the protocol by which two ledgers communicate, data packets and receiving data packets on an established
and higher-level protocols knit many bidirectional ledger- connection and channel, in addition to calls to manage
to-ledger links into gestalt multi-ledger applications. Just the protocol state: opening and closing connections and
as TCP/IP packets contain opaque payload data with channels, choosing connection, channel, and packet deliv-
semantics interpreted by the processes on each host, IBC ery options, and inspecting connection and channel status.
packets contain opaque payload data with semantics inter-
IBC requires certain functionalities and properties of the
preted by the modules on each ledger. Just as TCP/IP
underlying ledgers, primarily finality (or thresholding fi-
provides reliable, ordered data transmission between pro-
nality gadgets), cheaply-verifiable consensus transcripts
cesses, allowing a process on one host to reason about
(such that a light client algorithm can verify the results
the state of a process on another, IBC provides reliable,
of the consensus process with much less computation &
ordered data transmission between modules, allowing a
storage than a full node), and simple key/value store
module on one ledger to reason about the state of a module
functionality. On the network side, IBC requires only
on another.
eventual data delivery — no authentication, synchrony, or
This paper is intended as an overview of the abstractions ordering properties are assumed.
defined by the IBC protocol and the mechanisms by
which they are composed. We first outline the structure of C. Operation
the protocol, including scope, interfaces, and operational
requirements. Subsequently, we detail the abstractions The primary purpose of IBC is to provide reliable, authen-
defined by the protocol, including modules, ports, clients, ticated, ordered communication between modules running
connections, channels, packets, and relayers, and describe on independent host ledgers. This requires protocol logic in
the subprotocols for opening and closing handshakes, the areas of data relay, data confidentiality and legibility,
packet relay, edge-case handling, and relayer operations. reliability, flow control, authentication, statefulness, and
After explaining the internal structure of the protocol, multiplexing.
we define the interface by which applications can utilise
1) Data relay 4) Flow control
In the IBC architecture, modules are not directly sending IBC does not provide specific protocol-level provisions for
messages to each other over networking infrastructure, compute-level or economic-level flow control. The under-
but rather are creating messages to be sent which are lying ledgers are expected to have compute throughput
then physically relayed from one ledger to another by limiting devices and flow control mechanisms of their own
monitoring “relayer processes”. IBC assumes the existence such as gas markets. Application-level economic flow con-
of a set of relayer processes with access to an underly- trol — limiting the rate of particular packets according to
ing network protocol stack (likely TCP/IP, UDP/IP, or their content — may be useful to ensure security properties
QUIC/IP) and physical interconnect infrastructure. These and contain damage from Byzantine faults. For example,
relayer processes monitor a set of ledgers implementing an application transferring value over an IBC channel
the IBC protocol, continuously scanning the state of each might want to limit the rate of value transfer per block
ledger and requesting transaction execution on another to limit damage from potential Byzantine behaviour. IBC
ledger when outgoing packets have been committed. For provides facilities for modules to reject packets and leaves
correct operation and progress in a connection between particulars up to the higher-level application protocols.
two ledgers, IBC requires only that at least one correct
and live relayer process exists which can relay between 5) Authentication
the ledgers.
All data sent over IBC are authenticated: a block finalised
2) Data confidentiality and legibility by the consensus algorithm of the sending ledger must
commit to the outgoing packet via a cryptographic com-
The IBC protocol requires only that the minimum data mitment, and the receiving ledger’s IBC handler must
necessary for correct operation of the IBC protocol be verify both the consensus transcript and the cryptographic
made available and legible (serialised in a standardised for- commitment proof that the datagram was sent before
mat) to relayer processes, and the ledger may elect to make acting upon it.
that data available only to specific relayers. This data
consists of consensus state, client, connection, channel,
6) Statefulness
and packet information, and any auxiliary state structure
necessary to construct proofs of inclusion or exclusion of
particular key/value pairs in state. All data which must Reliability, flow control, and authentication as described
be proved to another ledger must also be legible; i.e., it above require that IBC initialises and maintains certain
must be serialised in a standardised format agreed upon status information for each datastream. This information
by the two ledgers. is split between three abstractions: clients, connections,
and channels. Each client object contains information
about the consensus state of the counterparty ledger. Each
3) Reliability
connection object contains a specific pair of named iden-
tifiers agreed to by both ledgers in a handshake protocol,
The network layer and relayer processes may behave in which uniquely identifies a connection between the two
arbitrary ways, dropping, reordering, or duplicating pack- ledgers. Each channel, specific to a pair of modules, con-
ets, purposely attempting to send invalid transactions, or tains information concerning negotiated encoding and mul-
otherwise acting in a Byzantine fashion, without compro- tiplexing options and state and sequence numbers. When
mising the safety or liveness of IBC. This is achieved by two modules wish to communicate, they must locate an
assigning a sequence number to each packet sent over existing connection and channel between their two ledgers,
an IBC channel, which is checked by the IBC handler or initialise a new connection and channel(s) if none
(the part of the ledger implementing the IBC protocol) yet exist. Initialising connections and channels requires a
on the receiving ledger, and providing a method for the multi-step handshake which, once complete, ensures that
sending ledger to check that the receiving ledger has in fact only the two intended ledgers are connected, in the case of
received and handled a packet before sending more packets connections, and ensures that two modules are connected
or taking further action. Cryptographic commitments are and that future datagrams relayed will be authenticated,
used to prevent datagram forgery: the sending ledger encoded, and sequenced as desired, in the case of channels.
commits to outgoing packets, and the receiving ledger
checks these commitments, so datagrams altered in transit
by a relayer will be rejected. IBC also supports unordered 7) Multiplexing
channels, which do not enforce ordering of packet receives
relative to sends but still enforce exactly-once delivery. To allow for many modules within a single host ledger
to use an IBC connection simultaneously, IBC allows
any number of channels to be associated with a single — each ledger checks that the other ledger is in fact
connection. Each channel uniquely identifies a datastream authenticating data using its consensus state.
over which packets can be sent in order (in the case
of an ordered channel), and always exactly once, to a 4) Timestamp access
destination module on the receiving ledger. Channels are
usually expected to be associated with a single module on
each ledger, but one-to-many and many-to-one channels In order to support timestamp-based timeouts, host
are also possible. The number of channels per connection ledgers must provide a current Unix-style timestamp.
is unbounded, facilitating concurrent throughput limited Timeouts in subsequent headers must be non-decreasing.
only by the throughput of the underlying ledgers with
only a single connection and pair of clients necessary 5) Port system
to track consensus information (and consensus transcript
verification cost thus amortised across all channels using Host ledgers must implement a port system, where the
the connection). IBC handler can allow different modules in the host ledger
to bind to uniquely named ports. Ports are identified by
III. Host ledger requirements an identifier, and must be permissioned so that:
b) Simple signatures
3) Desired properties
A client of a solo machine running a non-replicated ledger
Light clients must provide a secure algorithm to verify with a known public key checks signatures on messages
other ledgers’ canonical headers, using the existing consen- sent by that local machine. Multi-signature or threshold
sus state. The higher level abstractions will then be able to signature schemes can also be used in such a fashion.
verify sub-components of the state with the commitment
roots stored in the consensus state, which are guaranteed c) Proxy clients
to have been committed by the other ledger’s consensus
algorithm. Proxy clients verify another (proxy) ledger’s verification of
the target ledger, by including in the proof first a proof of
Validity predicates are expected to reflect the behaviour
the client state on the proxy ledger, and then a secondary
of the full nodes which are running the corresponding
proof of the sub-state of the target ledger with respect to
consensus algorithm. Given a consensus state and a list
the client state on the proxy ledger. This allows the proxy
of messages, if a full node accepts a new header, then the
client to avoid storing and tracking the consensus state
light client must also accept it, and if a full node rejects
of the target ledger itself, at the cost of adding security
it, then the light client must also reject it.
assumptions of proxy ledger correctness.
Light clients are not replaying the whole message tran-
script, so it is possible under cases of consensus misbe- d) BFT consensus and verifiable state
haviour that the light clients’ behaviour differs from the
full nodes’. In this case, a misbehaviour proof which proves For the immediate application of interoperability between
the divergence between the validity predicate and the full sovereign, fault-tolerant distributed ledgers, the most com-
mon and most useful client type will be light clients for packet relay (through channels). Connections are safely
instances of BFT consensus algorithms such as Tendermint established in an unknown, dynamic topology using a
[8], GRANDPA [9], or HotStuff [10], with ledgers utilising handshake subprotocol.
Merklized state trees such as an IAVL+ tree [11] or a
Merkle Patricia tree [12]. The client algorithm for such
instances will utilise the BFT consensus algorithm’s light 1) Motivation
client validity predicate and treat at minimum consensus
equivocation (double-signing) as misbehaviour, along with The IBC protocol provides authorisation and ordering se-
other possible misbehaviour types specific to the proof-of- mantics for packets: guarantees, respectively, that packets
authority or proof-of-stake system involved. have been committed on the sending ledger (and according
state transitions executed, such as escrowing tokens), and
6) Client lifecycle that they have been committed exactly once in a particular
order and can be delivered exactly once in that same order.
a) Creation The connection abstraction in conjunction with the client
abstraction defines the authorisation semantics of IBC.
Clients can be created permissionlessly by anyone at any Ordering semantics are provided by channels.
time by specifying an identifier, client type, and initial
consensus state.
2) Definitions
b) Update
A connection end is state tracked for an end of a connec-
Updating a client is done by submitting a new header. tion on one ledger, defined as follows:
When a new header is verified with the stored client state’s
validity predicate and consensus state, the client will enum ConnectionState {
update its internal state accordingly, possibly finalising INIT,
commitment roots and updating the signature authority TRYOPEN,
logic in the stored consensus state. OPEN,
}
If a client can no longer be updated (if, for example,
the unbonding period has passed), it will no longer be
interface ConnectionEnd {
possible to send any packets over connections and channels
state: ConnectionState
associated with that client, or timeout any packets in-flight
counterpartyConnectionIdentifier: Identifier
(since the height and timestamp on the destination ledger
counterpartyPrefix: CommitmentPrefix
can no longer be verified). Manual intervention must take
clientIdentifier: Identifier
place to reset the client state or migrate the connections
counterpartyClientIdentifier: Identifier
and channels to another client. This cannot safely be
version: string
done automatically, but ledgers implementing IBC could
}
elect to allow governance mechanisms to perform these
actions (perhaps even per-client/connection/channel with
a controlling multi-signature or contract). • The state field describes the current state of the
connection end.
• The counterpartyConnectionIdentifier field iden-
c) Misbehaviour
tifies the connection end on the counterparty ledger
If the client detects evidence of misbehaviour, the client associated with this connection.
can be take appropriate action, possibly invalidating pre- • The counterpartyPrefix field contains the prefix
viously valid commitment roots and preventing future used for state verification on the counterparty ledger
updates. What precisely constitutes misbehaviour will associated with this connection.
depend on the consensus algorithm which the validity • The clientIdentifier field identifies the client as-
predicate is validating the output of. sociated with this connection.
• The counterpartyClientIdentifier field identifies
the client on the counterparty ledger associated with
B. Connections
this connection.
The connection abstraction encapsulates two stateful ob- • The version field is an opaque string which can
jects (connection ends) on two separate ledgers, each be utilised to determine encodings or protocols for
associated with a light client of the other ledger, which channels or packets utilising this connection.
together facilitate cross-ledger sub-state verification and
3) Opening handshake validate ledger A has the correct consensus state for ledger
A.
The opening handshake subprotocol allows each ledger to ConnOpenConfirm, executed on ledger B, confirms opening
verify the identifier used to reference the connection on the of a connection on ledger A to ledger B. Ledger B sim-
other ledger, enabling modules on each ledger to reason ply checks that ledger A has executed ConnOpenAck and
about the reference on the other ledger. marked the connection as OPEN. Ledger B subsequently
marks its end of the connection as OPEN. After execution
The opening handshake consists of four datagrams:
of ConnOpenConfirm the connection is open on both ends
ConnOpenInit, ConnOpenTry, ConnOpenAck, and
and can be used immediately.
ConnOpenConfirm.
A correct protocol execution, between two ledgers A and 4) Versioning
B, with connection states formatted as (A, B), flows as
follows: During the handshake process, two ends of a connection
Datagram Prior state Posterior state come to agreement on a version bytestring associated with
that connection. At the moment, the contents of this
ConnOpenInit (-, -) (INIT, -) version bytestring are opaque to the IBC core protocol.
ConnOpenTry (INIT, none) (INIT, TRYOPEN) In the future, it might be used to indicate what kinds of
ConnOpenAck (INIT, TRYOPEN) (OPEN, TRYOPEN) channels can utilise the connection in question, or what
ConnOpenConfirm (OPEN, TRYOPEN) (OPEN, OPEN) encoding formats channel-related datagrams will use. Host
At the end of an opening handshake between two ledgers ledgers may utilise the version data to negotiate encod-
implementing the subprotocol, the following properties ings, priorities, or connection-specific metadata related to
hold: custom logic on top of IBC. Host ledgers may also safely
ignore the version data or specify an empty string.
• Each ledger has each other’s correct consensus state
as originally specified by the initiating actor. C. Channels
• Each ledger has knowledge of and has agreed to its
identifier on the other ledger. The channel abstraction provides message delivery se-
• Each ledger knows that the other ledger has agreed mantics to the interblockchain communication protocol
to the same data. in three categories: ordering, exactly-once delivery, and
module permissioning. A channel serves as a conduit for
Connection handshakes can safely be performed permis- packets passing between a module on one ledger and a
sionlessly, modulo anti-spam measures (paying gas). module on another, ensuring that packets are executed
only once, delivered in the order in which they were sent
ConnOpenInit, executed on ledger A, initialises a connec- (if necessary), and delivered only to the corresponding
tion attempt on ledger A, specifying a pair of identifiers for module owning the other end of the channel on the desti-
the connection on both ledgers and a pair of identifiers for nation ledger. Each channel is associated with a particular
existing light clients (one for each ledger). ledger A stores connection, and a connection may have any number of
a connection end object in its state. associated channels, allowing the use of common identifiers
ConnOpenTry, executed on ledger B, relays notice of a con- and amortising the cost of header verification across all the
nection attempt on ledger A to ledger B, providing the pair channels utilising a connection and light client.
of connection identifiers, the pair of client identifiers, and Channels are payload-agnostic. The modules which send
a desired version. Ledger B verifies that these identifiers and receive IBC packets decide how to construct packet
are valid, checks that the version is compatible, verifies data and how to act upon the incoming packet data, and
a proof that ledger A has stored these identifiers, and must utilise their own application logic to determine which
verifies a proof that the light client ledger A is using to state transactions to apply according to what data the
validate ledger B has the correct consensus state for ledger packet contains.
B. ledger B stores a connection end object in its state.
ConnOpenAck, executed on ledger A, relays acceptance of 1) Motivation
a connection open attempt from ledger B back to ledger
A, providing the identifier which can now be used to look The interblockchain communication protocol uses a cross-
up the connection end object. ledger A verifies that the ledger message passing model. IBC packets are relayed
version requested is compatible, verifies a proof that ledger from one ledger to the other by external relayer processes.
B has stored the same identifiers ledger A has stored, and Two ledgers, A and B, confirm new blocks independently,
verifies a proof that the light client ledger B is using to and packets from one ledger to the other may be delayed,
censored, or re-ordered arbitrarily. Packets are visible to • The ordering field indicates whether the channel is
relayers and can be read from a ledger by any relayer ordered or unordered. This is an enumeration instead
process and submitted to any other ledger. of a boolean in order to allow additional kinds of
ordering to be easily supported in the future.
The IBC protocol must provide ordering (for ordered • The counterpartyPortIdentifier identifies the
channels) and exactly-once delivery guarantees to allow ap- port on the counterparty ledger which owns the other
plications to reason about the combined state of connected end of the channel.
modules on two ledgers. For example, an application may • The counterpartyChannelIdentifier identifies the
wish to allow a single tokenised asset to be transferred channel end on the counterparty ledger.
between and held on multiple ledgers while preserving • The nextSequenceSend, stored separately, tracks the
fungibility and conservation of supply. The application can sequence number for the next packet to be sent.
mint asset vouchers on ledger B when a particular IBC • The nextSequenceRecv, stored separately, tracks the
packet is committed to ledger B, and require outgoing sequence number for the next packet to be received.
sends of that packet on ledger A to escrow an equal • The nextSequenceAck, stored separately, tracks the
amount of the asset on ledger A until the vouchers are sequence number for the next packet to be acknowl-
later redeemed back to ledger A with an IBC packet edged.
in the reverse direction. This ordering guarantee along • The connectionHops stores the list of connection
with correct application logic can ensure that total supply identifiers, in order, along which packets sent on this
is preserved across both ledgers and that any vouchers channel will travel. At the moment this list must be
minted on ledger B can later be redeemed back to ledger of length 1. In the future multi-hop channels may be
A. A more detailed explanation of this example is provided supported.
later on. • The version string stores an opaque channel version,
which is agreed upon during the handshake. This can
2) Definitions determine module-level configuration such as which
packet encoding is used for the channel. This version
A channel is a pipeline for exactly-once packet delivery is not used by the core IBC protocol.
between specific modules on separate ledgers, which has
at least one end capable of sending packets and one end Channel ends have a state:
capable of receiving packets. enum ChannelState {
An ordered channel is a channel where packets are deliv- INIT,
ered exactly in the order which they were sent. TRYOPEN,
OPEN,
An unordered channel is a channel where packets can be CLOSED,
delivered in any order, which may differ from the order in }
which they were sent.
• A channel end in INIT state has just started the
All channels provide exactly-once packet delivery, meaning
opening handshake.
that a packet sent on one end of a channel is delivered no
• A channel end in TRYOPEN state has acknowledged the
more and no less than once, eventually, to the other end.
handshake step on the counterparty ledger.
A channel end is a data structure storing metadata associ- • A channel end in OPEN state has completed the hand-
ated with one end of a channel on one of the participating shake and is ready to send and receive packets.
ledgers, defined as follows: • A channel end in CLOSED state has been closed and
can no longer be used to send or receive packets.
interface ChannelEnd {
state: ChannelState A Packet, encapsulating opaque data to be transferred
ordering: ChannelOrder from one module to another over a channel, is a particular
counterpartyPortIdentifier: Identifier interface defined as follows:
counterpartyChannelIdentifier: Identifier
nextSequenceSend: uint64 interface Packet {
nextSequenceRecv: uint64 sequence: uint64
nextSequenceAck: uint64 timeoutHeight: uint64
connectionHops: [Identifier] timeoutTimestamp: uint64
version: string sourcePort: Identifier
} sourceChannel: Identifier
destPort: Identifier
• The state is the current state of the channel end. destChannel: Identifier
data: bytes individual timeouts specified in terms of the destination
} ledger’s height or timestamp.
• The sequence number corresponds to the order of d) Permissioning
sends and receives, where a packet with an earlier
sequence number must be sent and received before Channels are permissioned to one module on each end,
a packet with a later sequence number. determined during the handshake and immutable after-
• The timeoutHeight indicates a consensus height on wards (higher-level logic could tokenise channel ownership
the destination ledger after which the packet will no by tokenising ownership of the port). Only the module
longer be processed, and will instead count as having which owns the port associated with a channel end is able
timed-out. to send or receive on the channel.
• The timeoutTimestamp indicates a timestamp on
the destination ledger after which the packet will no
4) Channel lifecycle management
longer be processed, and will instead count as having
timed-out.
• The sourcePort identifies the port on the sending a) Opening handshake
ledger.
• The sourceChannel identifies the channel end on the The channel opening handshake, between two ledgers A
sending ledger. and B, with state formatted as (A, B), flows as follows:
• The destPort identifies the port on the receiving Datagram Prior state Posterior state
ledger.
• The destChannel identifies the channel end on the ChanOpenInit (-, -) (INIT, -)
receiving ledger. ChanOpenTry (INIT, -) (INIT, TRYOPEN)
• The data is an opaque value which can be defined by ChanOpenAck (INIT, TRYOPEN) (OPEN, TRYOPEN)
the application logic of the associated modules. ChanOpenConfirm (OPEN, TRYOPEN) (OPEN, OPEN)
Note that a Packet is never directly serialised. Rather ChanOpenInit, executed on ledger A, initiates a channel
it is an intermediary structure used in certain function opening handshake from a module on ledger A to a module
calls that may need to be created or processed by modules on ledger B, providing the identifiers of the local channel
calling the IBC handler. identifier, local port, remote port, and remote channel
identifier. ledger A stores a channel end object in its state.
3) Properties ChanOpenTry, executed on ledger B, relays notice of a
channel handshake attempt to the module on ledger B,
a) Efficiency providing the pair of channel identifiers, a pair of port
identifiers, and a desired version. ledger B verifies a proof
As channels impose no flow control of their own, the speed that ledger A has stored these identifiers as claimed, looks
of packet transmission and confirmation is limited only by up the module which owns the destination port, calls that
the speed of the underlying ledgers. module to check that the version requested is compatible,
and stores a channel end object in its state.
b) Exactly-once delivery ChanOpenAck, executed on ledger A, relays acceptance of a
channel handshake attempt back to the module on ledger
IBC packets sent on one end of a channel are delivered
A, providing the identifier which can now be used to look
no more than exactly once to the other end. No network
up the channel end. ledger A verifies a proof that ledger
synchrony assumptions are required for exactly-once safety.
B has stored the channel metadata as claimed and marks
If one or both of the ledgers halt, packets may be delivered
its end of the channel as OPEN.
no more than once, and once the ledgers resume packets
will be able to flow again. ChanOpenConfirm, executed on ledger B, confirms open-
ing of a channel from ledger A to ledger B. Ledger B
c) Ordering simply checks that ledger A has executed ChanOpenAck
and marked the channel as OPEN. Ledger B subsequently
On ordered channels, packets are be sent and received in marks its end of the channel as OPEN. After execution of
the same order: if packet x is sent before packet y by a ChanOpenConfirm, the channel is open on both ends and
channel end on ledger A, packet x will be received before can be used immediately.
packet y by the corresponding channel end on ledger B.
When the opening handshake is complete, the module
On unordered channels, packets may be sent and received which initiates the handshake will own the end of the
in any order. Unordered packets, like ordered packets, have created channel on the host ledger, and the counterparty
module which it specifies will own the other end of the module to the corresponding module on the counterparty
created channel on the counterparty ledger. Once a chan- ledger.
nel is created, ownership can only be changed by changing
ownership of the associated ports. Calling modules must execute application logic atomically
in conjunction with calling sendPacket.
b) Versioning The IBC handler performs the following steps in order:
During the handshake process, two ends of a channel • Checks that the channel and connection are open to
come to agreement on a version bytestring associated with send packets
that channel. The contents of this version bytestring are • Checks that the calling module owns the sending port
opaque to the IBC core protocol. Host ledgers may utilise • Checks that the packet metadata matches the channel
the version data to indicate supported application-layer and connection information
protocols, agree on packet encoding formats, or negotiate • Checks that the timeout height specified has not
other channel-related metadata related to custom logic on already passed on the destination ledger
top of IBC. Host ledgers may also safely ignore the version • Increments the send sequence counter associated with
data or specify an empty string. the channel (in the case of ordered channels)
• Stores a constant-size commitment to the packet data
c) Closing handshake and packet timeout
The channel closing handshake, between two ledgers A and Note that the full packet is not stored in the state of the
B, with state formatted as (A, B), flows as follows: ledger — merely a short hash-commitment to the data and
timeout value. The packet data can be calculated from the
Datagram Prior state Posterior state transaction execution and possibly returned as log output
ChanCloseInit (OPEN, OPEN) (CLOSED, OPEN) which relayers can index.
ChanCloseConfirm (CLOSED, OPEN) (CLOSED, CLOSED)
6) Receiving packets
ChanCloseInit, executed on ledger A, closes the end of
the channel on ledger A.
The recvPacket function is called by a module in order
ChanCloseInit, executed on ledger B, simply verifies that to receive and process an IBC packet sent on the corre-
the channel has been marked as closed on ledger A and sponding channel end on the counterparty ledger.
closes the end on ledger B.
Calling modules must execute application logic atomically
Any in-flight packets can be timed-out as soon as a channel in conjunction with calling recvPacket, likely beforehand
is closed. to calculate the acknowledgement value.
Once closed, channels cannot be reopened and identifiers
The IBC handler performs the following steps in order:
cannot be reused. Identifier reuse is prevented because
we want to prevent potential replay of previously sent
• Checks that the channel and connection are open to
packets. The replay problem is analogous to using se-
receive packets
quence numbers with signed messages, except where the
• Checks that the calling module owns the receiving
light client algorithm “signs” the messages (IBC packets),
port
and the replay prevention sequence is the combination
• Checks that the packet metadata matches the channel
of port identifier, channel identifier, and packet sequence
and connection information
— hence we cannot allow the same port identifier and
• Checks that the packet sequence is the next sequence
channel identifier to be reused again with a sequence reset
the channel end expects to receive (for ordered chan-
to zero, since this might allow packets to be replayed. It
nels)
would be possible to safely reuse identifiers if timeouts
• Checks that the timeout height has not yet passed
of a particular maximum height/time were mandated and
• Checks the inclusion proof of packet data commitment
tracked, and future protocol versions may incorporate this
in the outgoing ledger’s state
feature.
• Sets the opaque acknowledgement value at a store
path unique to the packet (if the acknowledgement
5) Sending packets is non-empty or the channel is unordered)
• Increments the packet receive sequence associated
The sendPacket function is called by a module in order to with the channel end (for ordered channels)
send an IBC packet on a channel end owned by the calling
a) Acknowledgements executed and to allow the calling module to safely perform
appropriate state transitions.
The acknowledgePacket function is called by a module Calling modules may atomically execute appropriate appli-
to process the acknowledgement of a packet previously cation timeout-handling logic in conjunction with calling
sent by the calling module on a channel to a counterparty timeoutPacket.
module on the counterparty ledger. acknowledgePacket
also cleans up the packet commitment, which is no longer The IBC handler performs the following steps in order:
necessary since the packet has been received and acted
upon. • Checks that the channel and connection are open to
timeout packets
Calling modules may atomically execute appropriate ap- • Checks that the calling module owns the sending port
plication acknowledgement-handling logic in conjunction • Checks that the packet metadata matches the channel
with calling acknowledgePacket. and connection information
The IBC handler performs the following steps in order: • Checks that the packet was actually sent on this
channel
• Checks that the channel and connection are open to • Checks a proof that the packet has not been confirmed
acknowledge packets on the destination ledger
• Checks that the calling module owns the sending port • Checks a proof that the destination ledger has ex-
• Checks that the packet metadata matches the channel ceeded the timeout height or timestamp
and connection information • Deletes the packet commitment (cleaning up state
• Checks that the packet was actually sent on this and preventing replay)
channel
• Checks that the packet sequence is the next sequence In the case of an ordered channel, timeoutPacket ad-
the channel end expects to acknowledge (for ordered ditionally closes the channel if a packet has timed out.
channels) Unordered channels are expected to continue in the face
• Checks the inclusion proof of the packet acknowledge- of timed-out packets.
ment data in the receiving ledger’s state
• Deletes the packet commitment (cleaning up state If relations are enforced between timeout heights of subse-
and preventing replay) quent packets, safe bulk timeouts of all packets prior to a
• Increments the next acknowledgement sequence (for timed-out packet can be performed.
ordered channels)
b) Timing-out on close
7) Timeouts
If a channel is closed, in-flight packets can never
Application semantics may require some timeout: an upper be received and thus can be safely timed-out. The
limit to how long the ledger will wait for a transaction timeoutOnClose function is called by a module in or-
to be processed before considering it an error. Since the der to prove that the channel to which an unreceived
two ledgers have different local clocks, this is an obvious packet was addressed has been closed, so the packet
attack vector for a double spend — an attacker may delay will never be received (even if the timeoutHeight or
the relay of the receipt or wait to send the packet until timeoutTimestamp has not yet been reached). Appropri-
right after the timeout — so applications cannot safely ate application-specific logic may then safely be executed.
implement naive timeout logic themselves. In order to
avoid any possible “double-spend” attacks, the timeout c) Cleaning up state
algorithm requires that the destination ledger is running
and reachable. The timeout must be proven on the recip- If an acknowledgement is not written (as handling the
ient ledger, not simply the absence of a response on the acknowledgement would clean up state in that case),
sending ledger. cleanupPacket may be called by a module in order to
remove a received packet commitment from storage. The
a) Sending end receiving end must have already processed the packet
(whether regularly or past timeout).
The timeoutPacket function is called by a module which
originally attempted to send a packet to a counterparty In the ordered channel case, cleanupPacket cleans-up a
module, where the timeout height or timeout timestamp packet on an ordered channel by proving that the receive
has passed on the counterparty ledger without the packet sequence has passed the packet’s sequence on the other
being committed, to prove that the packet can no longer be end.
In the unordered channel case, cleanupPacket cleans-up based on the state of both ledgers. The relayer must
a packet on an unordered channel by proving that the possess prior knowledge of what subset of the IBC protocol
associated acknowledgement has been written. is implemented by the ledgers in the set for which they
are relaying (e.g. by reading the source code). Datagrams
D. Relayers can be submitted individually as single transactions or
atomically as a single transaction if the ledger supports
Relayer algorithms are the “physical” connection layer it.
of IBC — off-ledger processes responsible for relaying
Different relayers may relay between different ledgers —
data between two ledgers running the IBC protocol by
as long as each pair of ledgers has at least one correct and
scanning the state of each ledger, constructing appropriate
live relayer and the ledgers remain live, all packets flowing
datagrams, and executing them on the opposite ledger as
between ledgers in the network will eventually be relayed.
allowed by the protocol.
In the IBC protocol, one ledger can only record the a) Relaying packets in an ordered channel
intention to send particular data to another ledger — it
does not have direct access to a network transport layer. Packets in an ordered channel can be relayed in either
Physical datagram relay must be performed by off-ledger an event-based fashion or a query-based fashion. For the
infrastructure with access to a transport layer such as former, the relayer should watch the source ledger for
TCP/IP. This standard defines the concept of a relayer events emitted whenever packets are sent, then compose
algorithm, executable by an off-ledger process with the the packet using the data in the event log. For the latter,
ability to query ledger state, to perform this relay. the relayer should periodically query the send sequence
A relayer is an off-ledger process with the ability to read on the source ledger, and keep the last sequence number
the state of and submit transactions to some set of ledgers relayed, so that any sequences in between the two are
utilising the IBC protocol. packets that need to be queried and then relayed. In either
case, subsequently, the relayer process should check that
the destination ledger has not yet received the packet by
2) Properties
checking the receive sequence, and then relay it.
The relayer algorithm is defined over a set of ledgers Acknowledgements can most easily be relayed in an event-
implementing the IBC protocol. Each relayer may not based fashion. The relayer should watch the destination
necessarily have access to read state from and write data- ledger for events emitted whenever packets are received
grams to all ledgers in the multi-ledger network (especially and acknowledgements are written, then compose the
in the case of permissioned or private ledgers) — different acknowledgement using the data in the event log, check
relayers may relay between different subsets. whether the packet commitment still exists on the source
ledger (it will be deleted once the acknowledgement is
Every so often, although no more frequently than once per relayed), and if so relay the acknowledgement to the source
block on either ledger, a relayer calculates the set of all ledger.
valid datagrams to be relayed from one ledger to another
d) Relaying timeouts coordination may be ideal (such as assigning particular
relayers to particular packets or scanning mempools for
Timeout relay is slightly more complex since there is no pending transactions).
specific event emitted when a packet times-out — it is
simply the case that the packet can no longer be relayed,
since the timeout height or timestamp has passed on the V. Usage patterns
destination ledger. The relayer process must elect to track
a set of packets (which can be constructed by scanning
A. Call receiver
event logs), and as soon as the height or timestamp of the
destination ledger exceeds that of a tracked packet, check
whether the packet commitment still exists on the source Essential to the functionality of the IBC handler is an
ledger (it will be deleted once the timeout is relayed), and interface to other modules running on the same ledger, so
if so relay a timeout to the source ledger. that it can accept requests to send packets and can route
incoming packets to modules. This interface should be as
e) Ordering constraints minimal as possible in order to reduce implementation
complexity and requirements imposed on host ledgers.
There are implicit ordering constraints imposed on the For this reason, the core IBC logic uses a receive-only call
relayer process determining which datagrams must be pattern that differs slightly from the intuitive dataflow. As
submitted in what order. For example, a header must one might expect, modules call into the IBC handler to
be submitted to finalise the stored consensus state and create connections, channels, and send packets. However,
commitment root for a particular height in a light client instead of the IBC handler, upon receipt of a packet from
before a packet can be relayed. The relayer process is another ledger, selecting and calling into the appropriate
responsible for frequently querying the state of the ledgers module, the module itself must call recvPacket on the
between which they are relaying in order to determine IBC handler (likewise for accepting channel creation hand-
what must be relayed when. shakes). When recvPacket is called, the IBC handler will
check that the calling module is authorised to receive and
f) Bundling process the packet (based on included proofs and known
state of connections / channels), perform appropriate
If the host ledger supports it, the relayer process can state updates (incrementing sequence numbers to prevent
bundle many datagrams into a single transaction, which replay), and return control to the module or throw on error.
will cause them to be executed in sequence, and amortise The IBC handler never calls into modules directly.
any overhead costs (e.g. signature checks for fee payment).
Although a bit counterintuitive to reason about at first,
g) Race conditions this pattern has a few notable advantages:
Multiple relayers relaying between the same pair of mod- • It minimises requirements of the host ledger, since the
ules and ledgers may attempt to relay the same packet (or IBC handler need not understand how to call into
submit the same header) at the same time. If two relayers other modules or store any references to them.
do so, the first transaction will succeed and the second • It avoids the necessity of managing a module lookup
will fail. Out-of-band coordination between the relayers or table in the handler state.
between the actors who sent the original packets and the • It avoids the necessity of dealing with module return
relayers is necessary to mitigate this. data or failures. If a module does not want to
receive a packet (perhaps having implemented addi-
h) Incentivisation tional authorisation on top), it simply never calls
recvPacket. If the routing logic were implemented in
The relay process must have access to accounts on both the IBC handler, the handler would need to deal with
ledgers with sufficient balance to pay for transaction fees. the failure of the module, which is tricky to interpret.
Relayers may employ application-level methods to recoup
these fees, such by including a small payment to them- It also has one notable disadvantage: without an additional
selves in the packet data. abstraction, the relayer logic becomes more complex, since
off-ledger relayer processes will need to track the state
Any number of relayer processes may be safely run in of multiple modules to determine when packets can be
parallel (and indeed, it is expected that separate relayers submitted.
will serve separate subsets of the multi-ledger network).
However, they may consume unnecessary fees if they For this reason, ledgers may implement an additional IBC
submit the same proof multiple times, so some minimal “routing module” which exposes a call dispatch interface.
B. Call dispatch • Permissionless token transfers, no need to whitelist
connections, modules, or denominations
For common relay patterns, an “IBC routing module” can • Symmetric (all ledgers implement the same logic)
be implemented which maintains a module dispatch table • Fault containment: prevents Byzantine-inflation of
and simplifies the job of relayers. tokens originating on ledger A, as a result of ledger
In the call dispatch pattern, datagrams (contained within B’s Byzantine behaviour (though any users who sent
transaction types defined by the host ledger) are relayed tokens to ledger B may be at risk)
directly to the routing module, which then looks up the
appropriate module (owning the channel and port to which 3) Packet definition
the datagram was addressed) and calls an appropriate
function (which must have been previously registered with Only one packet data type, FungibleTokenPacketData,
the routing module). This allows modules to avoid han- which specifies the denomination, amount, sending ac-
dling datagrams directly, and makes it harder to acciden- count, receiving account, and whether the sending ledger
tally screw-up the atomic state transition execution which is the source of the asset, is required:
must happen in conjunction with sending or receiving a
packet (since the module never handles packets directly, interface FungibleTokenPacketData {
but rather exposes functions which are called by the denomination: string
routing module upon receipt of a valid packet). amount: uint256
sender: string
Additionally, the routing module can implement default receiver: string
logic for handshake datagram handling (accepting incom- }
ing handshakes on behalf of modules), which is convenient
for modules which do not need to implement their own The acknowledgement data type describes whether the
custom logic. transfer succeeded or failed, and the reason for failure (if
any):
VI. Example application-level module interface FungibleTokenPacketAcknowledgement {
success: boolean
The section specifies packet data structure and state ma- error: Maybe<string>
chine handling logic for the transfer of fungible tokens over }
an IBC channel between two modules on separate ledgers.
The state machine logic presented allows for safe multi- 4) Packet handling semantics
ledger denomination handling with permissionless channel
opening. This logic constitutes a “fungible token trans-
fer bridge module”, interfacing between the IBC routing The protocol logic is symmetric, so that denominations
module and an existing asset tracking module on the host originating on either ledger can be converted to vouchers
ledger. on the other, and then redeemed back again later.
A. Connection handshake
1) Initiating a handshake
function connOpenInit(
identifier: Identifier,
desiredCounterpartyConnectionIdentifier: Identifier,
counterpartyPrefix: CommitmentPrefix,
clientIdentifier: Identifier,
counterpartyClientIdentifier: Identifier) {
abortTransactionUnless(validateConnectionIdentifier(identifier))
abortTransactionUnless(provableStore.get(connectionPath(identifier)) == null)
state = INIT
connection = ConnectionEnd{state, desiredCounterpartyConnectionIdentifier, counterpartyPrefix,
clientIdentifier, counterpartyClientIdentifier, getCompatibleVersions()}
provableStore.set(connectionPath(identifier), connection)
}
function connOpenTry(
desiredIdentifier: Identifier,
counterpartyConnectionIdentifier: Identifier,
counterpartyPrefix: CommitmentPrefix,
counterpartyClientIdentifier: Identifier,
clientIdentifier: Identifier,
counterpartyVersions: string[],
proofInit: CommitmentProof,
proofConsensus: CommitmentProof,
proofHeight: uint64,
consensusHeight: uint64) {
abortTransactionUnless(validateConnectionIdentifier(desiredIdentifier))
abortTransactionUnless(consensusHeight <= getCurrentHeight())
expectedConsensusState = getConsensusState(consensusHeight)
expected = ConnectionEnd{INIT, desiredIdentifier, getCommitmentPrefix(), counterpartyClientIdentifier,
clientIdentifier, counterpartyVersions}
version = pickVersion(counterpartyVersions)
connection = ConnectionEnd{TRYOPEN, counterpartyConnectionIdentifier, counterpartyPrefix,
clientIdentifier, counterpartyClientIdentifier, version}
abortTransactionUnless(
connection.verifyConnectionState(proofHeight, proofInit, counterpartyConnectionIdentifier, expected))
abortTransactionUnless(connection.verifyClientConsensusState(
proofHeight, proofConsensus, counterpartyClientIdentifier, consensusHeight, expectedConsensusState))
previous = provableStore.get(connectionPath(desiredIdentifier))
abortTransactionUnless(
(previous === null) ||
(previous.state === INIT &&
previous.counterpartyConnectionIdentifier === counterpartyConnectionIdentifier &&
previous.counterpartyPrefix === counterpartyPrefix &&
previous.clientIdentifier === clientIdentifier &&
previous.counterpartyClientIdentifier === counterpartyClientIdentifier &&
previous.version === version))
identifier = desiredIdentifier
provableStore.set(connectionPath(identifier), connection)
}
3) Acknowledging the response
function connOpenAck(
identifier: Identifier,
version: string,
proofTry: CommitmentProof,
proofConsensus: CommitmentProof,
proofHeight: uint64,
consensusHeight: uint64) {
abortTransactionUnless(consensusHeight <= getCurrentHeight())
connection = provableStore.get(connectionPath(identifier))
abortTransactionUnless(connection.state === INIT || connection.state === TRYOPEN)
expectedConsensusState = getConsensusState(consensusHeight)
expected = ConnectionEnd{TRYOPEN, identifier, getCommitmentPrefix(),
connection.counterpartyClientIdentifier, connection.clientIdentifier,
version}
abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofTry,
connection.counterpartyConnectionIdentifier, expected))
abortTransactionUnless(connection.verifyClientConsensusState(
proofHeight, proofConsensus, connection.counterpartyClientIdentifier,
consensusHeight, expectedConsensusState))
connection.state = OPEN
abortTransactionUnless(getCompatibleVersions().indexOf(version) !== -1)
connection.version = version
provableStore.set(connectionPath(identifier), connection)
}
4) Finalising the connection
function connOpenConfirm(
identifier: Identifier,
proofAck: CommitmentProof,
proofHeight: uint64) {
connection = provableStore.get(connectionPath(identifier))
abortTransactionUnless(connection.state === TRYOPEN)
expected = ConnectionEnd{OPEN, identifier, getCommitmentPrefix(),
connection.counterpartyClientIdentifier,
connection.clientIdentifier, connection.version}
abortTransactionUnless(connection.verifyConnectionState(
proofHeight, proofAck, connection.counterpartyConnectionIdentifier, expected))
connection.state = OPEN
provableStore.set(connectionPath(identifier), connection)
}
B. Channel handshake
1) Initiating a handshake
function chanOpenInit(
order: ChannelOrder,
connectionHops: [Identifier],
portIdentifier: Identifier,
channelIdentifier: Identifier,
counterpartyPortIdentifier: Identifier,
counterpartyChannelIdentifier: Identifier,
version: string): CapabilityKey {
abortTransactionUnless(validateChannelIdentifier(portIdentifier, channelIdentifier))
abortTransactionUnless(connectionHops.length === 1)
abortTransactionUnless(provableStore.get(channelPath(portIdentifier, channelIdentifier)) === null)
connection = provableStore.get(connectionPath(connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(authenticateCapability(portPath(portIdentifier), portCapability))
channel = ChannelEnd{INIT, order, counterpartyPortIdentifier,
counterpartyChannelIdentifier, connectionHops, version}
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
channelCapability = newCapability(channelCapabilityPath(portIdentifier, channelIdentifier))
provableStore.set(nextSequenceSendPath(portIdentifier, channelIdentifier), 1)
provableStore.set(nextSequenceRecvPath(portIdentifier, channelIdentifier), 1)
provableStore.set(nextSequenceAckPath(portIdentifier, channelIdentifier), 1)
return channelCapability
}
2) Responding to a handshake initiation
function chanOpenTry(
order: ChannelOrder,
connectionHops: [Identifier],
portIdentifier: Identifier,
channelIdentifier: Identifier,
counterpartyPortIdentifier: Identifier,
counterpartyChannelIdentifier: Identifier,
version: string,
counterpartyVersion: string,
proofInit: CommitmentProof,
proofHeight: uint64): CapabilityKey {
abortTransactionUnless(validateChannelIdentifier(portIdentifier, channelIdentifier))
abortTransactionUnless(connectionHops.length === 1)
previous = provableStore.get(channelPath(portIdentifier, channelIdentifier))
abortTransactionUnless(
(previous === null) ||
(previous.state === INIT &&
previous.order === order &&
previous.counterpartyPortIdentifier === counterpartyPortIdentifier &&
previous.counterpartyChannelIdentifier === counterpartyChannelIdentifier &&
previous.connectionHops === connectionHops &&
previous.version === version)
)
abortTransactionUnless(authenticateCapability(portPath(portIdentifier), portCapability))
connection = provableStore.get(connectionPath(connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
expected = ChannelEnd{INIT, order, portIdentifier,
channelIdentifier,
[connection.counterpartyConnectionIdentifier],
counterpartyVersion}
abortTransactionUnless(connection.verifyChannelState(
proofHeight,
proofInit,
counterpartyPortIdentifier,
counterpartyChannelIdentifier,
expected
))
channel = ChannelEnd{TRYOPEN, order, counterpartyPortIdentifier,
counterpartyChannelIdentifier, connectionHops, version}
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
channelCapability = newCapability(channelCapabilityPath(portIdentifier, channelIdentifier))
provableStore.set(nextSequenceSendPath(portIdentifier, channelIdentifier), 1)
provableStore.set(nextSequenceRecvPath(portIdentifier, channelIdentifier), 1)
provableStore.set(nextSequenceAckPath(portIdentifier, channelIdentifier), 1)
return channelCapability
}
3) Acknowledging the response
function chanOpenAck(
portIdentifier: Identifier,
channelIdentifier: Identifier,
counterpartyVersion: string,
proofTry: CommitmentProof,
proofHeight: uint64) {
channel = provableStore.get(channelPath(portIdentifier, channelIdentifier))
abortTransactionUnless(channel.state === INIT || channel.state === TRYOPEN)
abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier),
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
expected = ChannelEnd{TRYOPEN, channel.order, portIdentifier,
channelIdentifier,
[connection.counterpartyConnectionIdentifier],
counterpartyVersion}
abortTransactionUnless(connection.verifyChannelState(
proofHeight,
proofTry,
channel.counterpartyPortIdentifier,
channel.counterpartyChannelIdentifier,
expected
))
channel.state = OPEN
channel.version = counterpartyVersion
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
4) Finalising a channel
function chanOpenConfirm(
portIdentifier: Identifier,
channelIdentifier: Identifier,
proofAck: CommitmentProof,
proofHeight: uint64) {
channel = provableStore.get(channelPath(portIdentifier, channelIdentifier))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === TRYOPEN)
abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier),
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
expected = ChannelEnd{OPEN, channel.order, portIdentifier,
channelIdentifier,
[connection.counterpartyConnectionIdentifier],
channel.version}
abortTransactionUnless(connection.verifyChannelState(
proofHeight,
proofAck,
channel.counterpartyPortIdentifier,
channel.counterpartyChannelIdentifier,
expected
))
channel.state = OPEN
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
5) Initiating channel closure
function chanCloseInit(
portIdentifier: Identifier,
channelIdentifier: Identifier) {
abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier),
channel = provableStore.get(channelPath(portIdentifier, channelIdentifier))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state !== CLOSED)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
channel.state = CLOSED
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
6) Confirming channel closure
function chanCloseConfirm(
portIdentifier: Identifier,
channelIdentifier: Identifier,
proofInit: CommitmentProof,
proofHeight: uint64) {
abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier),
channel = provableStore.get(channelPath(portIdentifier, channelIdentifier))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state !== CLOSED)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
expected = ChannelEnd{CLOSED, channel.order, portIdentifier,
channelIdentifier,
[connection.counterpartyConnectionIdentifier],
channel.version}
abortTransactionUnless(connection.verifyChannelState(
proofHeight,
proofInit,
channel.counterpartyPortIdentifier,
channel.counterpartyChannelIdentifier,
expected
))
channel.state = CLOSED
provableStore.set(channelPath(portIdentifier, channelIdentifier), channel)
}
C. Packet Handling
1) Sending a packet
function recvPacket(
packet: OpaquePacket,
proof: CommitmentProof,
proofHeight: uint64,
acknowledgement: bytes): Packet {
channel = provableStore.get(channelPath(packet.destPort, packet.destChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
abortTransactionUnless(
authenticateCapability(channelCapabilityPath(packet.destPort, packet.destChannel), capability))
abortTransactionUnless(packet.sourcePort === channel.counterpartyPortIdentifier)
abortTransactionUnless(packet.sourceChannel === channel.counterpartyChannelIdentifier)
abortTransactionUnless(provableStore.get(packetAcknowledgementPath(packet.destPort,
packet.destChannel, packet.sequence) === null))
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
abortTransactionUnless(packet.timeoutHeight === 0 || getConsensusHeight() < packet.timeoutHeight)
abortTransactionUnless(packet.timeoutTimestamp === 0 || currentTimestamp() < packet.timeoutTimestamp)
abortTransactionUnless(connection.verifyPacketData(
proofHeight,
proof,
packet.sourcePort,
packet.sourceChannel,
packet.sequence,
concat(packet.data, packet.timeoutHeight, packet.timeoutTimestamp)
))
if (acknowledgement.length > 0 || channel.order === UNORDERED)
provableStore.set(
packetAcknowledgementPath(packet.destPort, packet.destChannel, packet.sequence),
hash(acknowledgement)
)
if (channel.order === ORDERED) {
nextSequenceRecv = provableStore.get(nextSequenceRecvPath(packet.destPort, packet.destChannel))
abortTransactionUnless(packet.sequence === nextSequenceRecv)
nextSequenceRecv = nextSequenceRecv + 1
provableStore.set(nextSequenceRecvPath(packet.destPort, packet.destChannel), nextSequenceRecv)
}
return packet
}
3) Acknowledging a packet
function acknowledgePacket(
packet: OpaquePacket,
acknowledgement: bytes,
proof: CommitmentProof,
proofHeight: uint64): Packet {
channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
abortTransactionUnless(authenticateCapability(
channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability))
abortTransactionUnless(packet.destPort === channel.counterpartyPortIdentifier)
abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(connection.state === OPEN)
abortTransactionUnless(provableStore.get(packetCommitmentPath(packet.sourcePort,
packet.sourceChannel, packet.sequence))
=== hash(packet.data, packet.timeoutHeight, packet.timeoutTimestamp))
abortTransactionUnless(connection.verifyPacketAcknowledgement(
proofHeight,
proof,
packet.destPort,
packet.destChannel,
packet.sequence,
acknowledgement
))
if (channel.order === ORDERED) {
nextSequenceAck = provableStore.get(nextSequenceAckPath(packet.sourcePort, packet.sourceChannel))
abortTransactionUnless(packet.sequence === nextSequenceAck)
nextSequenceAck = nextSequenceAck + 1
provableStore.set(nextSequenceAckPath(packet.sourcePort, packet.sourceChannel), nextSequenceAck)
}
provableStore.delete(packetCommitmentPath(packet.sourcePort, packet.sourceChannel, packet.sequence))
return packet
}
4) Handling a timed-out packet
function timeoutPacket(
packet: OpaquePacket,
proof: CommitmentProof,
proofHeight: uint64,
nextSequenceRecv: Maybe<uint64>): Packet {
channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
abortTransactionUnless(authenticateCapability(
channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability))
abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(packet.destPort === channel.counterpartyPortIdentifier)
abortTransactionUnless(
(packet.timeoutHeight > 0 && proofHeight >= packet.timeoutHeight) ||
(packet.timeoutTimestamp > 0 &&
connection.getTimestampAtHeight(proofHeight) > packet.timeoutTimestamp))
abortTransactionUnless(provableStore.get(packetCommitmentPath(packet.sourcePort,
packet.sourceChannel, packet.sequence))
=== hash(packet.data, packet.timeoutHeight, packet.timeoutTimestamp))
if channel.order === ORDERED {
abortTransactionUnless(nextSequenceRecv <= packet.sequence)
abortTransactionUnless(connection.verifyNextSequenceRecv(
proofHeight,
proof,
packet.destPort,
packet.destChannel,
nextSequenceRecv
))
} else
abortTransactionUnless(connection.verifyPacketAcknowledgementAbsence(
proofHeight,
proof,
packet.destPort,
packet.destChannel,
packet.sequence
))
provableStore.delete(packetCommitmentPath(packet.sourcePort, packet.sourceChannel, packet.sequence))
if channel.order === ORDERED {
channel.state = CLOSED
provableStore.set(channelPath(packet.sourcePort, packet.sourceChannel), channel)
}
return packet
}
5) Cleaning up packet data
function cleanupPacket(
packet: OpaquePacket,
proof: CommitmentProof,
proofHeight: uint64,
nextSequenceRecvOrAcknowledgement: Either<uint64, bytes>): Packet {
channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel))
abortTransactionUnless(channel !== null)
abortTransactionUnless(channel.state === OPEN)
abortTransactionUnless(authenticateCapability(
channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability))
abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier)
connection = provableStore.get(connectionPath(channel.connectionHops[0]))
abortTransactionUnless(connection !== null)
abortTransactionUnless(packet.destPort === channel.counterpartyPortIdentifier)
abortTransactionUnless(nextSequenceRecv > packet.sequence)
abortTransactionUnless(provableStore.get(packetCommitmentPath(packet.sourcePort,
packet.sourceChannel, packet.sequence))
=== hash(packet.data, packet.timeoutHeight, packet.timeoutTimestamp))
if channel.order === ORDERED
abortTransactionUnless(connection.verifyNextSequenceRecv(
proofHeight,
proof,
packet.destPort,
packet.destChannel,
nextSequenceRecvOrAcknowledgement
))
else
abortTransactionUnless(connection.verifyPacketAcknowledgement(
proofHeight,
proof,
packet.destPort,
packet.destChannel,
packet.sequence,
nextSequenceRecvOrAcknowledgement
))
provableStore.delete(packetCommitmentPath(packet.sourcePort, packet.sourceChannel, packet.sequence))
return packet
}
References [16] GoZ Contributors, “Game of zones.”
https://goz.cosmosnetwork.dev/, May-2020.
[1] Alistair Stewart and Fatemeh Shirazi and Leon
Groot Bruinderink, “Web3 foundation research: XCMP.” [17] Bitquasar & Ztake, “Map of zones.”
https://mapofzones.com/,
https://research.web3.foundation/en/latest/polkadot/XCMP.html, May-2020.
May-2020. [18] P. Technologies, “Substrate: The
[2] E. 2.0 Contributors, “Ethereum sharding platform for blockchain innovators.”
research compendium: Cross-shard communication.” https://github.com/paritytech/substrate, 2020.
https://notes.ethereum.org/@serenity/H1PGqDhpm?type=view#Cross-shard-communication,
[19] Jae Kwon, Ethan Buchman, “Cos-
2020. mos: A network of distributed ledgers.”
[3] Near Protocol, “The authoritative https://cosmos.network/cosmos-whitepaper.pdf, Sep-
guide to blockchain sharding: Part 2.” 2016.
https://medium.com/nearprotocol/unsolved-problems-in-blockchain-sharding-2327d6517f43,
[20] Ethan Buchman, Jae Kwon, Zarko Milo-
Dec-2018. sevic, “The latest gossip on bft consensus.”
[4] “Transmission Control Protocol.” RFC 793; RFC Edi- https://arxiv.org/pdf/1807.04938, Nov-2019.
tor, Sep-1981. [21] Ethan Frey, “IBC protocol specification v0.3.1.”
[5] C. Morningstar, “What are capabilities?” https://github.com/cosmos/ics/blob/master/archive/v0_3_1_IBC.p
Nov-2017.
http://habitatchronicles.com/2017/05/what-are-capabilities/,
2017.
[6] I. Meckler and E. Shapiro, “Coda:
Decentralized cryptocurrency at scale.”
https://cdn.codaprotocol.com/v2/static/coda-whitepaper-05-10-2018-0.pdf,
2018.
[7] S. Nakamoto, “Bitcoin: A peer-to-peer electronic cash
system.” 2009.
[8] Jae Kwon, “Tendermint: Consensus without mining.”
https://tendermint.com/static/docs/tendermint.pdf, Sep-
2014.
[9] Alistair Stewart, “GRANDPA finality gadget.”
https://github.com/w3f/consensus/blob/master/pdf/grandpa.pdf,
May-2020.
[10] M. Yin, D. Malkhi, M. K. Reiter, G. G. Gueta, and
I. Abraham, “HotStuff: BFT consensus in the lens of
blockchain.” https://arxiv.org/pdf/1803.05069, 2018.
[11] Tendermint, “IAVL+ tree: A versioned, snap-
shottable (immutable) avl+ tree for persistent data.”
https://github.com/tendermint/iavl, 2020.
[12] Ethereum, “Ethereum modi-
fied merkle patricia trie specification.”
https://github.com/ethereum/wiki/wiki/Patricia-Tree,
2020.
[13] Cosmos SDK Contributors, “The cosmos sdk: X/ibc.”
https://github.com/cosmos/cosmos-sdk/tree/master/x/ibc,
May-2020.
[14] Informal Systems, “Rust imple-
mentation of ibc modules and relayer.”
https://github.com/informalsystems/ibc-rs, May-2020.
[15] Iqlusion, “Server-side ibc relayer.”
https://github.com/iqlusioninc/relayer, May-2020.