From f40335342f219150a3527b181ec14e9bf49c39c1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 12 Feb 2018 15:32:52 +0100 Subject: [PATCH 01/32] Started IBC spec --- docs/spec/README.md | 4 +++- docs/spec/ibc/README.md | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 docs/spec/ibc/README.md diff --git a/docs/spec/README.md b/docs/spec/README.md index 5f3942ff9b..6492dc9392 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -5,14 +5,16 @@ the Cosmos Hub. NOTE: the specifications are not yet complete and very much a work in progress. +<<<<<<< HEAD - [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for sending tokens. - [Staking](staking) - Proof of Stake related specifications including bonding and delegation transactions, inflation, fees, etc. - [Governance](governance) - Governance related specifications including proposals and voting. +- [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol. - [Other](other) - Other components of the Cosmos Hub, including the reserve pool, All in Bits vesting, etc. -The [specification for Tendermint](https://github.com/tendermint/tendermint/tree/develop/docs/specification/new-spec), +The [specification for Tendermint](https://github.com/tendermint/tendermint/tree/develop/docs/specification/new-spec), i.e. the underlying blockchain, can be found elsewhere. diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md new file mode 100644 index 0000000000..8fe1b7f3d7 --- /dev/null +++ b/docs/spec/ibc/README.md @@ -0,0 +1,7 @@ +# Inter Blockchain Communication protocol + +IBC was defined in the [cosmos whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), +and then in detail in a [specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). + +This package builds on that and includes detailed specifications, pseudocode and protocol specification. + From 06ec4b4a1ac305347ab7bff595e6111a2ccd9f66 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 12 Feb 2018 15:36:30 +0100 Subject: [PATCH 02/32] Added protobuf specifications for ibc messages --- docs/spec/ibc/protobuf/.gitignore | 1 + docs/spec/ibc/protobuf/Makefile | 7 +++ docs/spec/ibc/protobuf/merkle.proto | 79 +++++++++++++++++++++++++++ docs/spec/ibc/protobuf/messages.proto | 29 ++++++++++ docs/spec/ibc/protobuf/queue.proto | 57 +++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 docs/spec/ibc/protobuf/.gitignore create mode 100644 docs/spec/ibc/protobuf/Makefile create mode 100644 docs/spec/ibc/protobuf/merkle.proto create mode 100644 docs/spec/ibc/protobuf/messages.proto create mode 100644 docs/spec/ibc/protobuf/queue.proto diff --git a/docs/spec/ibc/protobuf/.gitignore b/docs/spec/ibc/protobuf/.gitignore new file mode 100644 index 0000000000..c61a5e8b01 --- /dev/null +++ b/docs/spec/ibc/protobuf/.gitignore @@ -0,0 +1 @@ +*.pb.go diff --git a/docs/spec/ibc/protobuf/Makefile b/docs/spec/ibc/protobuf/Makefile new file mode 100644 index 0000000000..a1a2ef9e11 --- /dev/null +++ b/docs/spec/ibc/protobuf/Makefile @@ -0,0 +1,7 @@ +.PHONEY: proto test + +proto: + protoc --gogo_out=. *.proto + +test: proto + go install . diff --git a/docs/spec/ibc/protobuf/merkle.proto b/docs/spec/ibc/protobuf/merkle.proto new file mode 100644 index 0000000000..866e0a8d08 --- /dev/null +++ b/docs/spec/ibc/protobuf/merkle.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; + +package protobuf; + + +// HashOp is the hashing algorithm we use at each level +enum HashOp { + RIPEMD160 = 0; + SHA224 = 1; + SHA256 = 2; + SHA384 = 3; + SHA512 = 4; + SHA3_224 = 5; + SHA3_256 = 6; + SHA3_384 = 7; + SHA3_512 = 8; + SHA256_X2 = 9; +}; + +// Op represents one hash in a chain of hashes. +// An operation takes the output of the last level and returns +// a hash for the next level: +// Op(last) => Operation(prefix + last + sufix) +// +// A simple left/right hash would simply set prefix=left or +// suffix=right and leave the other blank. However, one could +// also represent the a Patricia trie proof by setting +// prefix to the rlp encoding of all nodes before the branch +// we select, and suffix to all those after the one we select. +message Op { + bytes prefix = 1; + bytes suffix = 2; + HashOp op = 3; +} + +// Data is the end value stored, +// used to generate the initial hash store +message Data { + // optional prefix allows second preimage resistance + bytes prefix = 1; + bytes key = 2; + bytes value = 3; + HashOp op = 4; + // If it is KeyValue, this is the data we want + // If it is SubTree, key is name of the tree, + // value is root hash + enum DataType { + KeyValue = 0; + SubTree = 1; + } + DataType dataType = 5; +} + +// Branch will hash data and then pass it through operations +// from first to last in order to calculate the root node. +// +// Visualize Branch as representing the data closest to +// root as the first item, and the leaf as the last item. +message Branch { + // if either are non-empty, enforce this prefix on all + // leaf/inner nodes to provide second preimage resistence + bytes prefixLeaf = 1; + bytes prefixInner = 2; + // this is the data to get the original hash, + // and a set of operations to calculate the root hash + Data data = 3; + repeated Op operations = 4; +} + +message MerkleProof { + // TODO: root, height, chain_id, etc... + + // branches start from the value, and then may + // include multiple subtree branches to embed it + // + // The first branch must have dataType KeyValue + // Following branches must have dataType SubTree + repeated Branch branches = 1; +} diff --git a/docs/spec/ibc/protobuf/messages.proto b/docs/spec/ibc/protobuf/messages.proto new file mode 100644 index 0000000000..035c962e5b --- /dev/null +++ b/docs/spec/ibc/protobuf/messages.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package protobuf; + +import "merkle.proto"; + + +// IBCPacket sends a proven key/value pair from an IBCQueue. +// Depending on the type of message, we require a certain type +// of key (MessageKey at a given height, or StateKey). +// +// Includes src_chain and src_height to look up the proper +// header to verify the merkle proof. +message IBCPacket { + // chain id it is coming from + string src_chain = 1; + // height for the header the proof belongs to + uint64 src_height = 2; + // the message type, which determines what key/value mean + enum MsgType { + RECEIVE = 0; + RECEIPT = 1; + TIMEOUT = 2; + CLEANUP = 3; + } + MsgType msgType = 3; + // the proof of the message, includes key and value + MerkleProof proof = 6; +} diff --git a/docs/spec/ibc/protobuf/queue.proto b/docs/spec/ibc/protobuf/queue.proto new file mode 100644 index 0000000000..5d1c0ad33c --- /dev/null +++ b/docs/spec/ibc/protobuf/queue.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; + +package protobuf; + +import "google/protobuf/timestamp.proto"; + +message QueueName { + // chain_id is which chain this queue is + // associated with + string chain_id = 1; + enum Purpose { + SEND = 0; + RECEIPT = 1; + } + Purpose purpose = 2; +} + +// StateKey is a key for the head/tail of a given queue +message StateKey { + QueueName queue = 1; + // both encode into one byte with varint encoding + // never clash with 8 byte message indexes + enum State { + HEAD = 0; + TAIL = 0x7f; + } + State state = 2; +} + +// StateValue is the type stored under a StateKey +message StateValue { + fixed64 index = 1; +} + +// MessageKey is the key for message *index* in a given queue +message MessageKey { + QueueName queue = 1; + fixed64 index = 2; +} + +// SendValue is stored under a MessageKey in the SEND queue +message SendValue { + uint64 maxHeight = 1; + google.protobuf.Timestamp maxTime = 2; + // use kind instead of type to avoid keyword conflict + bytes kind = 3; + bytes data = 4; +} + +// ReceiptValue is stored under a MessageKey in the RECEIPT queue +message ReceiptValue { + // 0 is success, others are application-defined errors + int32 errorCode = 1; + // contains result on success, optional info on error + bytes data = 2; +} + From 7610cca7c9dc854ff16646f309e017b84cdb3a3b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 17:36:44 +0100 Subject: [PATCH 03/32] Rough import of google doc to markdown --- docs/spec/ibc/specification.md | 1189 ++++++++++++++++++++++++++++++++ 1 file changed, 1189 insertions(+) create mode 100644 docs/spec/ibc/specification.md diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md new file mode 100644 index 0000000000..c5ed3a75fc --- /dev/null +++ b/docs/spec/ibc/specification.md @@ -0,0 +1,1189 @@ +# IBC Protocol Specification + +v0.4.0 / Feb. 13, 2018 + +Ethan Frey + +## Abstract + +This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper[^1] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. + +Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. + +We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value are provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. + +The protocol makes no assumptions of block times or network delays in the transmission of the packets between chains and requires cryptographic proofs for every message, and thus is highly robust in a heterogeneous environment with Byzantine actors. This paper explains the requirements and structure of the Cosmos IBC protocol. It aims to provide enough detail to fully understand and analyze the security of the protocol. + +## Contents + +1. **Overview** + 1. Definitions + 1. Threat Models +1. **Proofs** + 1. Establishing a Root of Trust + 1. Following Block Headers +1. **Messaging Queue** + 1. Merkle Proofs for Queues + 1. Naming Queues + 1. Message Contents + 1. Sending a Packet + 1. Receipts + 1. Relay Process +1. **Optimizations** + 1. Cleanup + 1. Timeout + 1. Handling Byzantine Failures +1. **Conclusion** + +**Appendix A: Encoding Libraries** + +**Appendix B: IBC Queue Format** + +**Appendix C: Merkle Proof Format** + +**Appendix D: Universal IBC Packets** + +**Appendix E: Tendermint Header Proofs** + + + +## 1 Overview + +The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. + +The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[^2], used by StormMQ, RabbitMQ, etc. + +The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. + +In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. + +### 1.1 Definitions + +_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. + +_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. + +_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. + +_Knowledge_ - what is certain to be true. + +_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. + +_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. + +_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. + +_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack[^3]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. + +The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. + +### 1.2 Threat Models + +_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. + +_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. + +_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. + +## 2 Proofs + +The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh _is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. + +To support an IBC connection, two actors must be able to make the following proofs to each other: + +* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch' _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Ch_ and + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_ +* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ + +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. + +The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. + +_valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false]_ + +### 2.1 Establishing a Root of Trust + +As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_. + +Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. + +Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. + +### 2.2 Following Block Headers + +We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +vals(Cn, Ch ) < ⅓, that each step must have a change of less than one-third of the validator set[^4]. + +Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. + +Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. + +More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: + +_valid(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false | unknown]_ + +_if Hh-1 _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T then _ + +_valid(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false]_ + +_there must exist some Uh or Xh that evaluates to true_ + +_if Ch _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T then_ + +_ valid(T, Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ false_ + +and can process update transactions as follows: + +_update(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match valid(T, Xh | Uh )_ + +_ false _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ return Error("invalid proof")_ + +_ unknown _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ return Error("need a proof between current and h")_ + +_ true _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ T _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(Hh ,Ch )_ + +We define _max(T)_ as _max(h, where Hh_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T) _for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. + +We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. + +## 3 Messaging Queue + +Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. + +Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. + +This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. + +To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[^5], and avoid blocking. + +Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). + +Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ y_ means _x_ is causally before _y_, and chains A and B, and _a_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_b_ means _a_ implies _b_: + +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ B:receive(msgi )_ + +_B:receive(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ A:receipt(msgi )_ + +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:send(msgi+1 )_ + +_x_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + _x_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_B:receive(msgi )_ + +_y_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_B:receive(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + _y_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:receipt(msgi )_ + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC0.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC0.png "image_tooltip") + + +([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) + +In this section, we define an efficient implementation of a secure, reliable messaging queue. + +### 3.1 Merkle Proofs for Queues + +Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. + +We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: + +**init**: _qhead = qtail = 0_ + +**peek ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**m**: _if qhead = qtail { return None } else { return q[qhead] }_ + +**pop ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ + +**push(m)**: _q[qtail] = m; qtail++_ + +**advance(i)**: _qhead = i; qtail = max(qtail , i)_ + +**head ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**i**: _qhead_ + +**tail** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**i**: _qtail_ + +Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. + +**Key:_ (queue name, [head|tail|index])_** + +The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. + +A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _must refer to the same _v_. This property is essential to safely process asynchronous messages. + +Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. + +### 3.2 Naming Queues + +As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. + +The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). + +* _ibc::send_ - all outgoing packets destined to chain A +* _ibc::receipt_ - the results of executing the packets received from chain A + +These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ (_remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. + +### 3.3 Message Contents + +Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. + +We define every message in a _send queue _to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) + +_Vsend = (type, data)_ + +_Vreceipt = (result, [success|error code])_ + +### 3.4 Sending a Message + +A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. + +Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send_ _queue_, which enables all the proofs described above. + +The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. + +_(IBCsend(D, type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ Success)_ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ push(qD.send ,Vsend{type, data})_ + +We also consider how a given blockchain _A _is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: + +_A:IBCreceive(S, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qS.receipt =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, reciept, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a send"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ k = (_, send, i) and head(qS.receipt) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_i_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("out of order"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TS _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ valid(Hh ,Mk,v,h ) = false _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ v = (type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(result, err) :=ftype(data); push(qS.receipt , (result, err)); Success _ + +Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). + +### 3.5 Receipts + +When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. + +To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: + +_S:IBCreceipt(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.send =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a recipient"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ k = (_, receipt, head|tail)_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("only accepts message proofs"),_ + +_ k = (_, receipt, i) and head(qS.send) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_i_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("out of order"),_ + +_ v = (_, error) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(type, data) := pop(qS.send ); rollbacktype(data); Success_ + +_ v = (res, success) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(type, data) := pop(qS.send ); committype(data, res); Success_ + +This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC1.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC1.png "image_tooltip") + + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC2.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC2.png "image_tooltip") + + +### 3.6 Relay Process + +The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. + +The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. + +As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. + +_while true_ + +_ pending := tail(A:qB.send)_ + +_ received := tail(B:qA.receive)_ + +_ if pending > received_ + +_ Uh := A:latestHeader_ + +_ B:updateHeader(Uh)_ + +_ for i :=received...pending_ + +_ k := (B, send, i)_ + +_ packet := A:Mk,v,h_ + +_ B:IBCreceive(A, packet)_ + +_ sleep(desiredLatency)_ + +Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. + +In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. + +Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). + +## 4 Optimizations + +The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. + +### 4.1 Timeouts + +Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. + +One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. + +For a sending chain _A_ and a receiving chain _B_, with _k=(_, _, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not sent before height h_ + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was sent and receipt received before height h _ + + + _(and the receipts for all messages j < i were also handled)_ + +_A:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_otherwise (message result is not yet processed)_ + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not received before height h_ + +_B:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was received before height h_ + +_ (and all messages j < i were received)_ + +Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime._ + +_Vsend = (maxHeight, maxTime, type, data)_ + +_expired(Hh ,Vsend ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_[true/false]_ + +We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: + +_IBCreceive:_ + +_ …._ + +_ expired(latestHeader, v) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_push(qS.receipt , (None, TimeoutError));_ + +_ v = (_, _, type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(result, err) :=ftype(data); push(qS.receipt , (result, err)); _ + +and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. + +_S:IBCtimeout(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.send =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a receipt"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ k = (S, receipt, tail)_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_match_ + + + _tail _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_head(qS.send )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("receipt exists, no timeout proof")_ + + + _not expired(peek(qS.send )) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("message timeout not yet reached")_ + + + _default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(_, _, type, data) := pop(qS.send ); rollbacktype(data); Success_ + + + _default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a tail proof")_ + +which processes timeouts in order, and adds one more condition to the queues: + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was sent and timeout proven before height h_ + + + _(and the receipts for all messages j < i were also handled)_ + +Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. + +Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. + +### 4.2 Clean up + +While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. + +The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not received before height h_ + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was provably resolved on the sending chain before height h_ + +_B:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_otherwise (if message i was processed before height h,_ + +_ and no ack of receipt from the sending chain)_ + +Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. + +Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. + +_S:IBCcleanup(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.receipt =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unknown sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be for the send queue"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ k_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ (_, _, head) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("Need a proof of the head of the queue"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ head := v _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_match_ + +_ head <= head(qA.receipt) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("cleanup must go forward"),_ + +_ default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_advance(qA.receipt , head); Success_ + +This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC3.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC3.png "image_tooltip") + + +### 4.3 Handling Byzantine Failures + +While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). + +The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. + +If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). + +The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). + +The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? + +Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. + +## 5 Conclusion + +We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. + +The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. + +There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. + + + +## Appendix A: Encoding Libraries + +The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. + +In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[^6] and google's protobuf[^7]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. + +The tendermint-specific data structures are encoded with go-wire[^8], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. + +For the following appendixes, the data structure specifications will be in proto3[^9] format. + +## Appendix B: IBC Queue Format + +The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: + +_key = _(_remote id, [send|receipt], [head|tail|index])_ + +_Vsend = (maxHeight, maxTime, type, data)_ + +_Vreceipt = (result, [success|error code])_ + + +``` + message QueueName { + // chain_id is which chain this queue is + // associated with + string chain_id = 1; + enum Purpose { + SEND = 0; + RECEIPT = 1; + } + Purpose purpose = 2; + } + // StateKey is a key for the head/tail of a given queue + message StateKey { + QueueName queue = 1; + // both encode into one byte with varint encoding + // never clash with 8 byte message indexes + enum State { + HEAD = 0; + TAIL = 0x7f; + } + State state = 2; + } + // StateValue is the type stored under a StateKey + message StateValue { + fixed64 index = 1; + } + // MessageKey is the key for message *index* in a given queue + message MessageKey { + QueueName queue = 1; + fixed64 index = 2; + } + // SendValue is stored under a MessageKey in the SEND queue + message SendValue { + uint64 maxHeight = 1; + google.protobuf.Timestamp maxTime = 2; + // use kind instead of type to avoid keyword conflict + bytes kind = 3; + bytes data = 4; + } + // ReceiptValue is stored under a MessageKey in the RECEIPT queue + message ReceiptValue { + // 0 is success, others are application-defined errors + int32 errorCode = 1; + // contains result on success, optional info on error + bytes data = 2; + } +``` + +Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. + +## Appendix C: Merkle Proof Formats + +A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[^10]. + +There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[^11], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. + +We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. + +The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. + +A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. + +For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[^12], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. + +``` + // HashOp is the hashing algorithm we use at each level + enum HashOp { + RIPEMD160 = 0; + SHA224 = 1; + SHA256 = 2; + SHA384 = 3; + SHA512 = 4; + SHA3_224 = 5; + SHA3_256 = 6; + SHA3_384 = 7; + SHA3_512 = 8; + SHA256_X2 = 9; + }; + // Op represents one hash in a chain of hashes. + // An operation takes the output of the last level and returns + // a hash for the next level: + // Op(last) => Operation(prefix + last + sufix) + // + // A simple left/right hash would simply set prefix=left or + // suffix=right and leave the other blank. However, one could + // also represent the a Patricia trie proof by setting + // prefix to the rlp encoding of all nodes before the branch + // we select, and suffix to all those after the one we select. + message Op { + bytes prefix = 1; + bytes suffix = 2; + HashOp op = 3; + } + // Data is the end value stored, used to generate the initial hash + message Data { + bytes prefix = 1; + bytes key = 2; + bytes value = 3; + HashOp op = 4; + // If it is KeyValue, this is the data we want + // If it is SubTree, key is name of the tree, value is root hash + // Expect another branch to follow + enum DataType { + KeyValue = 0; + SubTree = 1; + } + DataType dataType = 5; + } + // Branch will hash data and then pass it through operations from + // last to first in order to calculate the root node. + // + // Visualize Branch as representing the data closest to root as the + // first item, and the leaf as the last item. + message Branch { + repeated Op operations = 1; + Data data = 2; + } + // MerkleProof shows a veriable path from the data to + // a root hash (potentially spanning multiple sub-trees). + message MerkleProof { + // identify the header this is rooted in + string chainId = 1; + uint64 height = 2; + // this hash must match the header as well as the + // calculation from below + bytes rootHash = 3; + // branches start from the value, and then may + // include multiple subtree branches to embed it + // + // The first branch must have dataType KeyValue + // Following branches must have dataType SubTree + repeated Branch branches = 1; + } + ``` + +## Appendix D: Universal IBC Packets + +The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. + +``` + // IBCPacket sends a proven key/value pair from an IBCQueue. + // Depending on the type of message, we require a certain type + // of key (MessageKey at a given height, or StateKey). + // + // Includes src_chain and src_height to look up the proper + // header to verify the merkle proof. + message IBCPacket { + // chain id it is coming from + string src_chain = 1; + // height for the header the proof belongs to + uint64 src_height = 2; + // the message type, which determines what key/value mean + enum MsgType { + RECEIVE = 0; + RECEIPT = 1; + TIMEOUT = 2; + CLEANUP = 3; + } + MsgType msgType = 3; + bytes key = 4; + bytes value = 5; + // the proof of the message + MerkleProof proof = 6; + } +``` + +## Appendix E: Tendermint Header Proofs + +TODO: clean this all up + +This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. + +In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. + +**Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet** + +**-> needs input/support from Tendermint Core team (and go-crypto)** + +**Registering Chain** + +**Updating Header** + +**Validator Changes** + +ROOT of trust + +As mentioned in the definitions, all proofs are based on an original assumption. The root of trust here is either the genesis block (if it is newer than the unbonding period) or any signed header of the other chain. + +When governance on a pair of chain, the respective chains must agree to a root of trust on the counterparty chain. This can be the genesis block on a chain that launches with an IBC channel or a later block header. + +From this signed header, one can check the validator set against the validator hash stored in the header, and then verify the signatures match. This provides internal consistency and accountability, but if 5 nodes provide you different headers (eg. of forks), you must make a subjective decision which one to trust. This should be performed by on-chain governance to avoid an exploitable position of trust. + +VERIFYING HEADERS + +Once we have a trusted header with a known validator set, we can quickly validate any new header with the same validator set. To validate a new header, simply verifying that the validator hash has not changed, and that over 2/3 of the voting power in that set has properly signed a commit for that header. We can skip all intervening headers, as we have complete finality (no forks) and accountability (to punish a double-sign). + +This is safe as long as we have a valid signed header by the trusted validator set that is within the unbonding period for staking. In that case, if we were given a false (forked) header, we could use this as proof to slash the stake of all the double-signing validators. This demonstrates the importance of attribution and is the same security guarantee of any non-validating full node. Even in the presence of some ultra-powerful malicious actors, this makes the cost of creating a fake proof for a header equal to at least one third of all staked tokens, which should be significantly higher than any gain of a false message. + +UPDATING VALIDATORS SET + +If the validator hash is different than the trusted one, we must simultaneously both verify that if the change is valid while, as well as use using the new set to validate the header. Since the entire validator set is not provided by default when we give a header and commit votes, this must be provided as extra data to the certifier. + +A validator change in Tendermint can be securely verified with the following checks: + + + +* First, that the new header, validators, and signatures are internally consistent + * We have a new set of validators that matches the hash on the new header + * At least 2/3 of the voting power of the new set validates the new header +* Second, that the new header is also valid in the eyes of our trust set + * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header + +In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). + + + +## Notes + +[^1]: + [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) + +[^2]: + [http://www.amqp.org/sites/amqp.org/files/amqp.pdf](http://www.amqp.org/sites/amqp.org/files/amqp.pdf) + +[^3]: + [https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) + +[^4]: + [https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104](https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104) + +[^5]: + [http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/](http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/) + +[^6]: + [https://github.com/ethereum/wiki/wiki/RLP](https://github.com/ethereum/wiki/wiki/RLP) + +[^7]: + [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) + +[^8]: + [https://github.com/tendermint/go-wire](https://github.com/tendermint/go-wire) + +[^9]: + [https://developers.google.com/protocol-buffers/docs/proto3](https://developers.google.com/protocol-buffers/docs/proto3) + +[^10]: + [https://en.wikipedia.org/wiki/Merkle_tree](https://en.wikipedia.org/wiki/Merkle_tree) + +[^11]: + [https://chainpoint.org/](https://chainpoint.org/) + +[^12]: + [https://github.com/tendermint/iavl](https://github.com/tendermint/iavl) From 97e61a6f7b83a2c2e9d07f1bff77bef581ee6c07 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 18:11:10 +0100 Subject: [PATCH 04/32] Break IBC spec into multiple md files --- docs/spec/ibc/README.md | 2 +- docs/spec/ibc/appendix-a.md | 11 + docs/spec/ibc/appendix-b.md | 62 ++ docs/spec/ibc/appendix-c.md | 87 +++ docs/spec/ibc/appendix-d.md | 33 + docs/spec/ibc/appendix-e.md | 49 ++ docs/spec/ibc/conclusion.md | 7 + docs/spec/ibc/footnotes.md | 37 + docs/spec/ibc/optimizations.md | 321 +++++++++ docs/spec/ibc/overview.md | 39 ++ docs/spec/ibc/proofs.md | 129 ++++ docs/spec/ibc/queues.md | 371 ++++++++++ docs/spec/ibc/specification.md | 1168 +------------------------------- 13 files changed, 1160 insertions(+), 1156 deletions(-) create mode 100644 docs/spec/ibc/appendix-a.md create mode 100644 docs/spec/ibc/appendix-b.md create mode 100644 docs/spec/ibc/appendix-c.md create mode 100644 docs/spec/ibc/appendix-d.md create mode 100644 docs/spec/ibc/appendix-e.md create mode 100644 docs/spec/ibc/conclusion.md create mode 100644 docs/spec/ibc/footnotes.md create mode 100644 docs/spec/ibc/optimizations.md create mode 100644 docs/spec/ibc/overview.md create mode 100644 docs/spec/ibc/proofs.md create mode 100644 docs/spec/ibc/queues.md diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 8fe1b7f3d7..43183251e9 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -3,5 +3,5 @@ IBC was defined in the [cosmos whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and then in detail in a [specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). -This package builds on that and includes detailed specifications, pseudocode and protocol specification. +This package builds on that and includes detailed specifications, pseudocode and protocol specification. Please read the [table of contents](specification.md) of the IBC specification. diff --git a/docs/spec/ibc/appendix-a.md b/docs/spec/ibc/appendix-a.md new file mode 100644 index 0000000000..289699aff1 --- /dev/null +++ b/docs/spec/ibc/appendix-a.md @@ -0,0 +1,11 @@ +## Appendix A: Encoding Libraries + +([Back to table of contents](specification.md#contents)) + +The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. + +In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./footnotes.md#6)] and google's protobuf[[7](./footnotes.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. + +The tendermint-specific data structures are encoded with go-wire[[8](./footnotes.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. + +For the following appendixes, the data structure specifications will be in proto3[[9](./footnotes.md#9)] format. diff --git a/docs/spec/ibc/appendix-b.md b/docs/spec/ibc/appendix-b.md new file mode 100644 index 0000000000..226f590606 --- /dev/null +++ b/docs/spec/ibc/appendix-b.md @@ -0,0 +1,62 @@ +## Appendix B: IBC Queue Format + +([Back to table of contents](specification.md#contents)) + +The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: + +_key = _(_remote id, [send|receipt], [head|tail|index])_ + +_Vsend = (maxHeight, maxTime, type, data)_ + +_Vreceipt = (result, [success|error code])_ + + +``` + message QueueName { + // chain_id is which chain this queue is + // associated with + string chain_id = 1; + enum Purpose { + SEND = 0; + RECEIPT = 1; + } + Purpose purpose = 2; + } + // StateKey is a key for the head/tail of a given queue + message StateKey { + QueueName queue = 1; + // both encode into one byte with varint encoding + // never clash with 8 byte message indexes + enum State { + HEAD = 0; + TAIL = 0x7f; + } + State state = 2; + } + // StateValue is the type stored under a StateKey + message StateValue { + fixed64 index = 1; + } + // MessageKey is the key for message *index* in a given queue + message MessageKey { + QueueName queue = 1; + fixed64 index = 2; + } + // SendValue is stored under a MessageKey in the SEND queue + message SendValue { + uint64 maxHeight = 1; + google.protobuf.Timestamp maxTime = 2; + // use kind instead of type to avoid keyword conflict + bytes kind = 3; + bytes data = 4; + } + // ReceiptValue is stored under a MessageKey in the RECEIPT queue + message ReceiptValue { + // 0 is success, others are application-defined errors + int32 errorCode = 1; + // contains result on success, optional info on error + bytes data = 2; + } +``` + +Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. diff --git a/docs/spec/ibc/appendix-c.md b/docs/spec/ibc/appendix-c.md new file mode 100644 index 0000000000..3eb870d715 --- /dev/null +++ b/docs/spec/ibc/appendix-c.md @@ -0,0 +1,87 @@ +## Appendix C: Merkle Proof Formats + +([Back to table of contents](specification.md#contents)) + +A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./footnotes.md#10)]. + +There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./footnotes.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. + +We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. + +The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. + +A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. + +For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./footnotes.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. + +``` + // HashOp is the hashing algorithm we use at each level + enum HashOp { + RIPEMD160 = 0; + SHA224 = 1; + SHA256 = 2; + SHA384 = 3; + SHA512 = 4; + SHA3_224 = 5; + SHA3_256 = 6; + SHA3_384 = 7; + SHA3_512 = 8; + SHA256_X2 = 9; + }; + // Op represents one hash in a chain of hashes. + // An operation takes the output of the last level and returns + // a hash for the next level: + // Op(last) => Operation(prefix + last + sufix) + // + // A simple left/right hash would simply set prefix=left or + // suffix=right and leave the other blank. However, one could + // also represent the a Patricia trie proof by setting + // prefix to the rlp encoding of all nodes before the branch + // we select, and suffix to all those after the one we select. + message Op { + bytes prefix = 1; + bytes suffix = 2; + HashOp op = 3; + } + // Data is the end value stored, used to generate the initial hash + message Data { + bytes prefix = 1; + bytes key = 2; + bytes value = 3; + HashOp op = 4; + // If it is KeyValue, this is the data we want + // If it is SubTree, key is name of the tree, value is root hash + // Expect another branch to follow + enum DataType { + KeyValue = 0; + SubTree = 1; + } + DataType dataType = 5; + } + // Branch will hash data and then pass it through operations from + // last to first in order to calculate the root node. + // + // Visualize Branch as representing the data closest to root as the + // first item, and the leaf as the last item. + message Branch { + repeated Op operations = 1; + Data data = 2; + } + // MerkleProof shows a veriable path from the data to + // a root hash (potentially spanning multiple sub-trees). + message MerkleProof { + // identify the header this is rooted in + string chainId = 1; + uint64 height = 2; + // this hash must match the header as well as the + // calculation from below + bytes rootHash = 3; + // branches start from the value, and then may + // include multiple subtree branches to embed it + // + // The first branch must have dataType KeyValue + // Following branches must have dataType SubTree + repeated Branch branches = 1; + } + ``` + diff --git a/docs/spec/ibc/appendix-d.md b/docs/spec/ibc/appendix-d.md new file mode 100644 index 0000000000..64b8565fcd --- /dev/null +++ b/docs/spec/ibc/appendix-d.md @@ -0,0 +1,33 @@ +## Appendix D: Universal IBC Packets + +([Back to table of contents](specification.md#contents)) + +The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. + +``` + // IBCPacket sends a proven key/value pair from an IBCQueue. + // Depending on the type of message, we require a certain type + // of key (MessageKey at a given height, or StateKey). + // + // Includes src_chain and src_height to look up the proper + // header to verify the merkle proof. + message IBCPacket { + // chain id it is coming from + string src_chain = 1; + // height for the header the proof belongs to + uint64 src_height = 2; + // the message type, which determines what key/value mean + enum MsgType { + RECEIVE = 0; + RECEIPT = 1; + TIMEOUT = 2; + CLEANUP = 3; + } + MsgType msgType = 3; + bytes key = 4; + bytes value = 5; + // the proof of the message + MerkleProof proof = 6; + } +``` + diff --git a/docs/spec/ibc/appendix-e.md b/docs/spec/ibc/appendix-e.md new file mode 100644 index 0000000000..f07360850d --- /dev/null +++ b/docs/spec/ibc/appendix-e.md @@ -0,0 +1,49 @@ +## Appendix E: Tendermint Header Proofs + +TODO: clean this all up + +This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. + +In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. + +**Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet** + +**-> needs input/support from Tendermint Core team (and go-crypto)** + +**Registering Chain** + +**Updating Header** + +**Validator Changes** + +ROOT of trust + +As mentioned in the definitions, all proofs are based on an original assumption. The root of trust here is either the genesis block (if it is newer than the unbonding period) or any signed header of the other chain. + +When governance on a pair of chain, the respective chains must agree to a root of trust on the counterparty chain. This can be the genesis block on a chain that launches with an IBC channel or a later block header. + +From this signed header, one can check the validator set against the validator hash stored in the header, and then verify the signatures match. This provides internal consistency and accountability, but if 5 nodes provide you different headers (eg. of forks), you must make a subjective decision which one to trust. This should be performed by on-chain governance to avoid an exploitable position of trust. + +VERIFYING HEADERS + +Once we have a trusted header with a known validator set, we can quickly validate any new header with the same validator set. To validate a new header, simply verifying that the validator hash has not changed, and that over 2/3 of the voting power in that set has properly signed a commit for that header. We can skip all intervening headers, as we have complete finality (no forks) and accountability (to punish a double-sign). + +This is safe as long as we have a valid signed header by the trusted validator set that is within the unbonding period for staking. In that case, if we were given a false (forked) header, we could use this as proof to slash the stake of all the double-signing validators. This demonstrates the importance of attribution and is the same security guarantee of any non-validating full node. Even in the presence of some ultra-powerful malicious actors, this makes the cost of creating a fake proof for a header equal to at least one third of all staked tokens, which should be significantly higher than any gain of a false message. + +UPDATING VALIDATORS SET + +If the validator hash is different than the trusted one, we must simultaneously both verify that if the change is valid while, as well as use using the new set to validate the header. Since the entire validator set is not provided by default when we give a header and commit votes, this must be provided as extra data to the certifier. + +A validator change in Tendermint can be securely verified with the following checks: + + + +* First, that the new header, validators, and signatures are internally consistent + * We have a new set of validators that matches the hash on the new header + * At least 2/3 of the voting power of the new set validates the new header +* Second, that the new header is also valid in the eyes of our trust set + * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header + +In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). + + diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md new file mode 100644 index 0000000000..37d555e6c4 --- /dev/null +++ b/docs/spec/ibc/conclusion.md @@ -0,0 +1,7 @@ +## 5 Conclusion + +We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. + +The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. + +There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. diff --git a/docs/spec/ibc/footnotes.md b/docs/spec/ibc/footnotes.md new file mode 100644 index 0000000000..eecd528412 --- /dev/null +++ b/docs/spec/ibc/footnotes.md @@ -0,0 +1,37 @@ +## Footnotes + +##### 1: +[https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) + +##### 2: +[http://www.amqp.org/sites/amqp.org/files/amqp.pdf](http://www.amqp.org/sites/amqp.org/files/amqp.pdf) + +##### 3: +[https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) + +##### 4: +[https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104](https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104) + +##### 5: +[http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/](http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/) + +##### 6: +[https://github.com/ethereum/wiki/wiki/RLP](https://github.com/ethereum/wiki/wiki/RLP) + +##### 7: +[https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) + +##### 8: +[https://github.com/tendermint/go-wire](https://github.com/tendermint/go-wire) + +##### 9: +[https://developers.google.com/protocol-buffers/docs/proto3](https://developers.google.com/protocol-buffers/docs/proto3) + +##### 10: +[https://en.wikipedia.org/wiki/Merkle_tree](https://en.wikipedia.org/wiki/Merkle_tree) + +##### 11: +[https://chainpoint.org/](https://chainpoint.org/) + +##### 12: +[https://github.com/tendermint/iavl](https://github.com/tendermint/iavl) diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md new file mode 100644 index 0000000000..5724554963 --- /dev/null +++ b/docs/spec/ibc/optimizations.md @@ -0,0 +1,321 @@ +## 4 Optimizations + +([Back to table of contents](specification.md#contents)) + +The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. + +### 4.1 Timeouts + +Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. + +One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. + +For a sending chain _A_ and a receiving chain _B_, with _k=(_, _, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not sent before height h_ + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was sent and receipt received before height h _ + + + _(and the receipts for all messages j < i were also handled)_ + +_A:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_otherwise (message result is not yet processed)_ + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not received before height h_ + +_B:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was received before height h_ + +_ (and all messages j < i were received)_ + +Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime._ + +_Vsend = (maxHeight, maxTime, type, data)_ + +_expired(Hh ,Vsend ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_[true/false]_ + +We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: + +_IBCreceive:_ + +_ …._ + +_ expired(latestHeader, v) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_push(qS.receipt , (None, TimeoutError));_ + +_ v = (_, _, type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(result, err) :=ftype(data); push(qS.receipt , (result, err)); _ + +and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. + +_S:IBCtimeout(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.send =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a receipt"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ k = (S, receipt, tail)_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_match_ + + + _tail _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_head(qS.send )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("receipt exists, no timeout proof")_ + + + _not expired(peek(qS.send )) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("message timeout not yet reached")_ + + + _default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(_, _, type, data) := pop(qS.send ); rollbacktype(data); Success_ + + + _default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a tail proof")_ + +which processes timeouts in order, and adds one more condition to the queues: + +_A:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was sent and timeout proven before height h_ + + + _(and the receipts for all messages j < i were also handled)_ + +Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. + +Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. + +### 4.2 Clean up + +While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. + +The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was not received before height h_ + +_B:Mk,v,h = _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_if message i was provably resolved on the sending chain before height h_ + +_B:Mk,v,h _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_otherwise (if message i was processed before height h,_ + +_ and no ack of receipt from the sending chain)_ + +Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. + +Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. + +_S:IBCcleanup(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.receipt =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unknown sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be for the send queue"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ k_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ (_, _, head) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("Need a proof of the head of the queue"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ head := v _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_match_ + +_ head <= head(qA.receipt) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("cleanup must go forward"),_ + +_ default _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_advance(qA.receipt , head); Success_ + +This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC3.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC3.png "image_tooltip") + + +### 4.3 Handling Byzantine Failures + +While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). + +The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. + +If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). + +The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). + +The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? + +Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md new file mode 100644 index 0000000000..9c07d83691 --- /dev/null +++ b/docs/spec/ibc/overview.md @@ -0,0 +1,39 @@ +## 1 Overview + +([Back to table of contents](specification.md#contents)) + +The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. + +The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by StormMQ, RabbitMQ, etc. + +The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. + +In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. + +### 1.1 Definitions + +_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. + +_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. + +_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. + +_Knowledge_ - what is certain to be true. + +_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. + +_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. + +_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. + +_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. + +The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. + +### 1.2 Threat Models + +_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. + +_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. + +_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md new file mode 100644 index 0000000000..59ab961428 --- /dev/null +++ b/docs/spec/ibc/proofs.md @@ -0,0 +1,129 @@ +## 2 Proofs + +([Back to table of contents](specification.md#contents)) + +The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh _is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. + +To support an IBC connection, two actors must be able to make the following proofs to each other: + +* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch' _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Ch_ and + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_ +* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ + +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. + +The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. + +_valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false]_ + +### 2.1 Establishing a Root of Trust + +As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(now, Hh) < P_. + +Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. + +Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. + +### 2.2 Following Block Headers + +We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +vals(Cn, Ch ) < ⅓, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. + +Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. + +Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. + +More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: + +_valid(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false | unknown]_ + +_if Hh-1 _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T then _ + +_valid(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ [true | false]_ + +_there must exist some Uh or Xh that evaluates to true_ + +_if Ch _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T then_ + +_ valid(T, Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ false_ + +and can process update transactions as follows: + +_update(T, Xh | Uh ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match valid(T, Xh | Uh )_ + +_ false _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ return Error("invalid proof")_ + +_ unknown _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ return Error("need a proof between current and h")_ + +_ true _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ T _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(Hh ,Ch )_ + +We define _max(T)_ as _max(h, where Hh_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_T) _for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. + +We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md new file mode 100644 index 0000000000..f118361e65 --- /dev/null +++ b/docs/spec/ibc/queues.md @@ -0,0 +1,371 @@ +## 3 Messaging Queue + +([Back to table of contents](specification.md#contents)) + +Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. + +Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. + +This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. + +To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[[5](./footnotes.md#5)], and avoid blocking. + +Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). + +Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ y_ means _x_ is causally before _y_, and chains A and B, and _a_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_b_ means _a_ implies _b_: + +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ B:receive(msgi )_ + +_B:receive(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ A:receipt(msgi )_ + +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:send(msgi+1 )_ + +_x_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:send(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + _x_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_B:receive(msgi )_ + +_y_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_B:receive(msgi )_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + _y_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A:receipt(msgi )_ + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC0.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC0.png "image_tooltip") + + +([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) + +In this section, we define an efficient implementation of a secure, reliable messaging queue. + +### 3.1 Merkle Proofs for Queues + +Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. + +We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: + +**init**: _qhead = qtail = 0_ + +**peek ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**m**: _if qhead = qtail { return None } else { return q[qhead] }_ + +**pop ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ + +**push(m)**: _q[qtail] = m; qtail++_ + +**advance(i)**: _qhead = i; qtail = max(qtail , i)_ + +**head ** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**i**: _qhead_ + +**tail** + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +**i**: _qtail_ + +Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. + +**Key:_ (queue name, [head|tail|index])_** + +The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. + +A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _must refer to the same _v_. This property is essential to safely process asynchronous messages. + +Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. + +### 3.2 Naming Queues + +As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. + +The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). + +* _ibc::send_ - all outgoing packets destined to chain A +* _ibc::receipt_ - the results of executing the packets received from chain A + +These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ (_remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. + +### 3.3 Message Contents + +Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. + +We define every message in a _send queue _to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) + +_Vsend = (type, data)_ + +_Vreceipt = (result, [success|error code])_ + +### 3.4 Sending a Message + +A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. + +Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send_ _queue_, which enables all the proofs described above. + +The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. + +_(IBCsend(D, type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ Success)_ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ push(qD.send ,Vsend{type, data})_ + +We also consider how a given blockchain _A _is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: + +_A:IBCreceive(S, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qS.receipt =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, reciept, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a send"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_A_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_ k = (_, send, i) and head(qS.receipt) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_i_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("out of order"),_ + +_ Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TS _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ valid(Hh ,Mk,v,h ) = false _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ v = (type, data) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(result, err) :=ftype(data); push(qS.receipt , (result, err)); Success _ + +Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). + +### 3.5 Receipts + +When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. + +To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: + +_S:IBCreceipt(A, Mk,v,h) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_ match_ + +_qA.send =_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ + + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("unregistered sender"), _ + +_ k = (_, send, _) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must be a recipient"),_ + +_ k = (d, _, _) and d _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_S_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("sent to a different chain"),_ + +_Hh _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_TA _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("must submit header for height h"),_ + +_ not valid(Hh ,Mk,v,h ) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("invalid merkle proof"),_ + +_ k = (_, receipt, head|tail)_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("only accepts message proofs"),_ + +_ k = (_, receipt, i) and head(qS.send) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_i_ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_Error("out of order"),_ + +_ v = (_, error) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(type, data) := pop(qS.send ); rollbacktype(data); Success_ + +_ v = (res, success) _ + +

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+ +_(type, data) := pop(qS.send ); committype(data, res); Success_ + +This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC1.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC1.png "image_tooltip") + + + + +

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC2.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

+ + +![alt_text](images/Cosmos-IBC2.png "image_tooltip") + + +### 3.6 Relay Process + +The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. + +The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. + +As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. + +_while true_ + +_ pending := tail(A:qB.send)_ + +_ received := tail(B:qA.receive)_ + +_ if pending > received_ + +_ Uh := A:latestHeader_ + +_ B:updateHeader(Uh)_ + +_ for i :=received...pending_ + +_ k := (B, send, i)_ + +_ packet := A:Mk,v,h_ + +_ B:IBCreceive(A, packet)_ + +_ sleep(desiredLatency)_ + +Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. + +In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. + +Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md index c5ed3a75fc..21cbe0b6b2 100644 --- a/docs/spec/ibc/specification.md +++ b/docs/spec/ibc/specification.md @@ -1,12 +1,12 @@ # IBC Protocol Specification -v0.4.0 / Feb. 13, 2018 +_v0.4.0 / Feb. 13, 2018_ -Ethan Frey +**Ethan Frey** ## Abstract -This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper[^1] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. +This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. @@ -16,1174 +16,32 @@ The protocol makes no assumptions of block times or network delays in the transm ## Contents -1. **Overview** +1. **[Overview](overview.md)** 1. Definitions 1. Threat Models -1. **Proofs** +1. **[Proofs](proofs.md)** 1. Establishing a Root of Trust 1. Following Block Headers -1. **Messaging Queue** +1. **[Messaging Queue](queues.md)** 1. Merkle Proofs for Queues 1. Naming Queues 1. Message Contents 1. Sending a Packet 1. Receipts 1. Relay Process -1. **Optimizations** +1. **[Optimizations](optimizations.md)** 1. Cleanup 1. Timeout 1. Handling Byzantine Failures -1. **Conclusion** +1. **[Conclusion](conclusion.md)** -**Appendix A: Encoding Libraries** +**[Appendix A: Encoding Libraries](appendix-a.md)** -**Appendix B: IBC Queue Format** +**[Appendix B: IBC Queue Format](appendix-b.md)** -**Appendix C: Merkle Proof Format** +**[Appendix C: Merkle Proof Format](appendix-c.md)** -**Appendix D: Universal IBC Packets** +**[Appendix D: Universal IBC Packets](appendix-d.md)** -**Appendix E: Tendermint Header Proofs** +**[Appendix E: Tendermint Header Proofs](appendix-e.md)** - - -## 1 Overview - -The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. - -The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[^2], used by StormMQ, RabbitMQ, etc. - -The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. - -In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. - -### 1.1 Definitions - -_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. - -_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. - -_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. - -_Knowledge_ - what is certain to be true. - -_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. - -_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. - -_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. - -_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack[^3]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. - -The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. - -### 1.2 Threat Models - -_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. - -_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. - -_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. - -## 2 Proofs - -The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh _is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. - -To support an IBC connection, two actors must be able to make the following proofs to each other: - -* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_ -* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch' _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Ch_ and - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_ -* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ - -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. - -The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. - -_valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ [true | false]_ - -### 2.1 Establishing a Root of Trust - -As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_. - -Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. - -Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. - -### 2.2 Following Block Headers - -We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -vals(Cn, Ch ) < ⅓, that each step must have a change of less than one-third of the validator set[^4]. - -Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. - -Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. - -More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: - -_valid(T, Xh | Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ [true | false | unknown]_ - -_if Hh-1 _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T then _ - -_valid(T, Xh | Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ [true | false]_ - -_there must exist some Uh or Xh that evaluates to true_ - -_if Ch _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T then_ - -_ valid(T, Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ false_ - -and can process update transactions as follows: - -_update(T, Xh | Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match valid(T, Xh | Uh )_ - -_ false _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ return Error("invalid proof")_ - -_ unknown _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ return Error("need a proof between current and h")_ - -_ true _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ T _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(Hh ,Ch )_ - -We define _max(T)_ as _max(h, where Hh_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T) _for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. - -We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. - -## 3 Messaging Queue - -Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. - -Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. - -This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. - -To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[^5], and avoid blocking. - -Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). - -Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ y_ means _x_ is causally before _y_, and chains A and B, and _a_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_b_ means _a_ implies _b_: - -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ B:receive(msgi )_ - -_B:receive(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ A:receipt(msgi )_ - -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:send(msgi+1 )_ - -_x_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - _x_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_B:receive(msgi )_ - -_y_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_B:receive(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - _y_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:receipt(msgi )_ - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC0.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC0.png "image_tooltip") - - -([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) - -In this section, we define an efficient implementation of a secure, reliable messaging queue. - -### 3.1 Merkle Proofs for Queues - -Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. - -We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: - -**init**: _qhead = qtail = 0_ - -**peek ** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**m**: _if qhead = qtail { return None } else { return q[qhead] }_ - -**pop ** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ - -**push(m)**: _q[qtail] = m; qtail++_ - -**advance(i)**: _qhead = i; qtail = max(qtail , i)_ - -**head ** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**i**: _qhead_ - -**tail** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**i**: _qtail_ - -Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. - -**Key:_ (queue name, [head|tail|index])_** - -The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. - -A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _must refer to the same _v_. This property is essential to safely process asynchronous messages. - -Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. - -### 3.2 Naming Queues - -As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. - -The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). - -* _ibc::send_ - all outgoing packets destined to chain A -* _ibc::receipt_ - the results of executing the packets received from chain A - -These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ (_remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. - -### 3.3 Message Contents - -Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. - -We define every message in a _send queue _to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) - -_Vsend = (type, data)_ - -_Vreceipt = (result, [success|error code])_ - -### 3.4 Sending a Message - -A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. - -Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send_ _queue_, which enables all the proofs described above. - -The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. - -_(IBCsend(D, type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ Success)_ - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ push(qD.send ,Vsend{type, data})_ - -We also consider how a given blockchain _A _is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: - -_A:IBCreceive(S, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qS.receipt =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, reciept, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a send"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ k = (_, send, i) and head(qS.receipt) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_i_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("out of order"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TS _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ valid(Hh ,Mk,v,h ) = false _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ v = (type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(result, err) :=ftype(data); push(qS.receipt , (result, err)); Success _ - -Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). - -### 3.5 Receipts - -When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. - -To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: - -_S:IBCreceipt(A, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.send =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a recipient"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ k = (_, receipt, head|tail)_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("only accepts message proofs"),_ - -_ k = (_, receipt, i) and head(qS.send) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_i_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("out of order"),_ - -_ v = (_, error) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(type, data) := pop(qS.send ); rollbacktype(data); Success_ - -_ v = (res, success) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(type, data) := pop(qS.send ); committype(data, res); Success_ - -This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC1.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC1.png "image_tooltip") - - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC2.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC2.png "image_tooltip") - - -### 3.6 Relay Process - -The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. - -The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. - -As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. - -_while true_ - -_ pending := tail(A:qB.send)_ - -_ received := tail(B:qA.receive)_ - -_ if pending > received_ - -_ Uh := A:latestHeader_ - -_ B:updateHeader(Uh)_ - -_ for i :=received...pending_ - -_ k := (B, send, i)_ - -_ packet := A:Mk,v,h_ - -_ B:IBCreceive(A, packet)_ - -_ sleep(desiredLatency)_ - -Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. - -In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. - -Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). - -## 4 Optimizations - -The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. - -### 4.1 Timeouts - -Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. - -One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. - -For a sending chain _A_ and a receiving chain _B_, with _k=(_, _, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: - -_A:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was not sent before height h_ - -_A:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was sent and receipt received before height h _ - - - _(and the receipts for all messages j < i were also handled)_ - -_A:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_otherwise (message result is not yet processed)_ - -_B:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was not received before height h_ - -_B:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was received before height h_ - -_ (and all messages j < i were received)_ - -Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime._ - -_Vsend = (maxHeight, maxTime, type, data)_ - -_expired(Hh ,Vsend ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_[true/false]_ - -We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: - -_IBCreceive:_ - -_ …._ - -_ expired(latestHeader, v) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_push(qS.receipt , (None, TimeoutError));_ - -_ v = (_, _, type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(result, err) :=ftype(data); push(qS.receipt , (result, err)); _ - -and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. - -_S:IBCtimeout(A, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.send =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a receipt"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ k = (S, receipt, tail)_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_match_ - - - _tail _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_head(qS.send )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("receipt exists, no timeout proof")_ - - - _not expired(peek(qS.send )) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("message timeout not yet reached")_ - - - _default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(_, _, type, data) := pop(qS.send ); rollbacktype(data); Success_ - - - _default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a tail proof")_ - -which processes timeouts in order, and adds one more condition to the queues: - -_A:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was sent and timeout proven before height h_ - - - _(and the receipts for all messages j < i were also handled)_ - -Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. - -Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. - -### 4.2 Clean up - -While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. - -The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: - -_B:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was not received before height h_ - -_B:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was provably resolved on the sending chain before height h_ - -_B:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_otherwise (if message i was processed before height h,_ - -_ and no ack of receipt from the sending chain)_ - -Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. - -Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. - -_S:IBCcleanup(A, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.receipt =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unknown sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be for the send queue"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ k_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ (_, _, head) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("Need a proof of the head of the queue"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ head := v _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_match_ - -_ head <= head(qA.receipt) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("cleanup must go forward"),_ - -_ default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_advance(qA.receipt , head); Success_ - -This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC3.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC3.png "image_tooltip") - - -### 4.3 Handling Byzantine Failures - -While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). - -The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. - -If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). - -The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). - -The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? - -Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. - -## 5 Conclusion - -We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. - -The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. - -There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. - - - -## Appendix A: Encoding Libraries - -The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. - -In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[^6] and google's protobuf[^7]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. - -The tendermint-specific data structures are encoded with go-wire[^8], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. - -For the following appendixes, the data structure specifications will be in proto3[^9] format. - -## Appendix B: IBC Queue Format - -The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: - -_key = _(_remote id, [send|receipt], [head|tail|index])_ - -_Vsend = (maxHeight, maxTime, type, data)_ - -_Vreceipt = (result, [success|error code])_ - - -``` - message QueueName { - // chain_id is which chain this queue is - // associated with - string chain_id = 1; - enum Purpose { - SEND = 0; - RECEIPT = 1; - } - Purpose purpose = 2; - } - // StateKey is a key for the head/tail of a given queue - message StateKey { - QueueName queue = 1; - // both encode into one byte with varint encoding - // never clash with 8 byte message indexes - enum State { - HEAD = 0; - TAIL = 0x7f; - } - State state = 2; - } - // StateValue is the type stored under a StateKey - message StateValue { - fixed64 index = 1; - } - // MessageKey is the key for message *index* in a given queue - message MessageKey { - QueueName queue = 1; - fixed64 index = 2; - } - // SendValue is stored under a MessageKey in the SEND queue - message SendValue { - uint64 maxHeight = 1; - google.protobuf.Timestamp maxTime = 2; - // use kind instead of type to avoid keyword conflict - bytes kind = 3; - bytes data = 4; - } - // ReceiptValue is stored under a MessageKey in the RECEIPT queue - message ReceiptValue { - // 0 is success, others are application-defined errors - int32 errorCode = 1; - // contains result on success, optional info on error - bytes data = 2; - } -``` - -Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. - -## Appendix C: Merkle Proof Formats - -A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[^10]. - -There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[^11], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. - -We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. - -The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. - -A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. - -For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[^12], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. - -``` - // HashOp is the hashing algorithm we use at each level - enum HashOp { - RIPEMD160 = 0; - SHA224 = 1; - SHA256 = 2; - SHA384 = 3; - SHA512 = 4; - SHA3_224 = 5; - SHA3_256 = 6; - SHA3_384 = 7; - SHA3_512 = 8; - SHA256_X2 = 9; - }; - // Op represents one hash in a chain of hashes. - // An operation takes the output of the last level and returns - // a hash for the next level: - // Op(last) => Operation(prefix + last + sufix) - // - // A simple left/right hash would simply set prefix=left or - // suffix=right and leave the other blank. However, one could - // also represent the a Patricia trie proof by setting - // prefix to the rlp encoding of all nodes before the branch - // we select, and suffix to all those after the one we select. - message Op { - bytes prefix = 1; - bytes suffix = 2; - HashOp op = 3; - } - // Data is the end value stored, used to generate the initial hash - message Data { - bytes prefix = 1; - bytes key = 2; - bytes value = 3; - HashOp op = 4; - // If it is KeyValue, this is the data we want - // If it is SubTree, key is name of the tree, value is root hash - // Expect another branch to follow - enum DataType { - KeyValue = 0; - SubTree = 1; - } - DataType dataType = 5; - } - // Branch will hash data and then pass it through operations from - // last to first in order to calculate the root node. - // - // Visualize Branch as representing the data closest to root as the - // first item, and the leaf as the last item. - message Branch { - repeated Op operations = 1; - Data data = 2; - } - // MerkleProof shows a veriable path from the data to - // a root hash (potentially spanning multiple sub-trees). - message MerkleProof { - // identify the header this is rooted in - string chainId = 1; - uint64 height = 2; - // this hash must match the header as well as the - // calculation from below - bytes rootHash = 3; - // branches start from the value, and then may - // include multiple subtree branches to embed it - // - // The first branch must have dataType KeyValue - // Following branches must have dataType SubTree - repeated Branch branches = 1; - } - ``` - -## Appendix D: Universal IBC Packets - -The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. - -``` - // IBCPacket sends a proven key/value pair from an IBCQueue. - // Depending on the type of message, we require a certain type - // of key (MessageKey at a given height, or StateKey). - // - // Includes src_chain and src_height to look up the proper - // header to verify the merkle proof. - message IBCPacket { - // chain id it is coming from - string src_chain = 1; - // height for the header the proof belongs to - uint64 src_height = 2; - // the message type, which determines what key/value mean - enum MsgType { - RECEIVE = 0; - RECEIPT = 1; - TIMEOUT = 2; - CLEANUP = 3; - } - MsgType msgType = 3; - bytes key = 4; - bytes value = 5; - // the proof of the message - MerkleProof proof = 6; - } -``` - -## Appendix E: Tendermint Header Proofs - -TODO: clean this all up - -This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. - -In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. - -**Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet** - -**-> needs input/support from Tendermint Core team (and go-crypto)** - -**Registering Chain** - -**Updating Header** - -**Validator Changes** - -ROOT of trust - -As mentioned in the definitions, all proofs are based on an original assumption. The root of trust here is either the genesis block (if it is newer than the unbonding period) or any signed header of the other chain. - -When governance on a pair of chain, the respective chains must agree to a root of trust on the counterparty chain. This can be the genesis block on a chain that launches with an IBC channel or a later block header. - -From this signed header, one can check the validator set against the validator hash stored in the header, and then verify the signatures match. This provides internal consistency and accountability, but if 5 nodes provide you different headers (eg. of forks), you must make a subjective decision which one to trust. This should be performed by on-chain governance to avoid an exploitable position of trust. - -VERIFYING HEADERS - -Once we have a trusted header with a known validator set, we can quickly validate any new header with the same validator set. To validate a new header, simply verifying that the validator hash has not changed, and that over 2/3 of the voting power in that set has properly signed a commit for that header. We can skip all intervening headers, as we have complete finality (no forks) and accountability (to punish a double-sign). - -This is safe as long as we have a valid signed header by the trusted validator set that is within the unbonding period for staking. In that case, if we were given a false (forked) header, we could use this as proof to slash the stake of all the double-signing validators. This demonstrates the importance of attribution and is the same security guarantee of any non-validating full node. Even in the presence of some ultra-powerful malicious actors, this makes the cost of creating a fake proof for a header equal to at least one third of all staked tokens, which should be significantly higher than any gain of a false message. - -UPDATING VALIDATORS SET - -If the validator hash is different than the trusted one, we must simultaneously both verify that if the change is valid while, as well as use using the new set to validate the header. Since the entire validator set is not provided by default when we give a header and commit votes, this must be provided as extra data to the certifier. - -A validator change in Tendermint can be securely verified with the following checks: - - - -* First, that the new header, validators, and signatures are internally consistent - * We have a new set of validators that matches the hash on the new header - * At least 2/3 of the voting power of the new set validates the new header -* Second, that the new header is also valid in the eyes of our trust set - * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header - -In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). - - - -## Notes - -[^1]: - [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) - -[^2]: - [http://www.amqp.org/sites/amqp.org/files/amqp.pdf](http://www.amqp.org/sites/amqp.org/files/amqp.pdf) - -[^3]: - [https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) - -[^4]: - [https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104](https://blog.cosmos.network/light-clients-in-tendermint-consensus-1237cfbda104) - -[^5]: - [http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/](http://scattered-thoughts.net/blog/2012/08/16/causal-ordering/) - -[^6]: - [https://github.com/ethereum/wiki/wiki/RLP](https://github.com/ethereum/wiki/wiki/RLP) - -[^7]: - [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - -[^8]: - [https://github.com/tendermint/go-wire](https://github.com/tendermint/go-wire) - -[^9]: - [https://developers.google.com/protocol-buffers/docs/proto3](https://developers.google.com/protocol-buffers/docs/proto3) - -[^10]: - [https://en.wikipedia.org/wiki/Merkle_tree](https://en.wikipedia.org/wiki/Merkle_tree) - -[^11]: - [https://chainpoint.org/](https://chainpoint.org/) - -[^12]: - [https://github.com/tendermint/iavl](https://github.com/tendermint/iavl) From cc17a3e9636dea69e8412b7e9be6b19a14cf01a9 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 18:46:13 +0100 Subject: [PATCH 05/32] Clean up all formulas in proofs --- docs/spec/ibc/proofs.md | 105 +++++++--------------------------------- 1 file changed, 17 insertions(+), 88 deletions(-) diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md index 59ab961428..c402eae393 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/proofs.md @@ -2,43 +2,23 @@ ([Back to table of contents](specification.md#contents)) -The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh _is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. +The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh_ is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. To support an IBC connection, two actors must be able to make the following proofs to each other: -* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_ -* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch' _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Ch_ and - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ * given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. -_valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ [true | false]_ +_valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ ### 2.1 Establishing a Root of Trust -As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(now, Hh) < P_. +As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. @@ -48,9 +28,7 @@ Development of a fully open and decentralized PKI for tracking blockchains is an We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -vals(Cn, Ch ) < ⅓, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. +Δ_vals(Cn, Ch ) < ⅓_, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. @@ -58,72 +36,23 @@ Since these messages _Uh_ and _Xh_ provide all knowledge o More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: -_valid(T, Xh | Uh ) _ +_valid(T, Xh | Uh )_ ⇒ _[true | false | unknown]_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+_if Hh-1_ ∈ _T then_: +* _valid(T, Xh | Uh )_ ⇒ _[true | false]_ +* _there must exist some Uh or Xh that evaluates to true_ -_ [true | false | unknown]_ - -_if Hh-1 _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T then _ - -_valid(T, Xh | Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ [true | false]_ - -_there must exist some Uh or Xh that evaluates to true_ - -_if Ch _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T then_ - -_ valid(T, Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ false_ +_if Ch_ ∉ _T then_ +* _valid(T, Uh )_ ⇒ _false_ and can process update transactions as follows: -_update(T, Xh | Uh ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- +_update(T, Xh | Uh )_ ⇒ _ match valid(T, Xh | Uh )_ +* _false_ ⇒ _return Error("invalid proof")_ +* _unknown_ ⇒ _return Error("need a proof between current and h")_ +* _true_ ⇒ _T_ ∪ _(Hh ,Ch )_ -_ false _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ return Error("invalid proof")_ - -_ unknown _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ return Error("need a proof between current and h")_ - -_ true _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ T _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(Hh ,Ch )_ - -We define _max(T)_ as _max(h, where Hh_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_T) _for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. +We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. From 1feb84e2726c268e183ba6e4aa2f5a2ca2631b68 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 20:20:22 +0100 Subject: [PATCH 06/32] Cleaned up queue section --- docs/spec/ibc/images/ReceiptError.png | Bin 0 -> 26687 bytes docs/spec/ibc/images/Receipts.png | Bin 0 -> 26870 bytes docs/spec/ibc/queues.md | 317 +++++--------------------- docs/spec/ibc/specification.md | 3 +- 4 files changed, 55 insertions(+), 265 deletions(-) create mode 100644 docs/spec/ibc/images/ReceiptError.png create mode 100644 docs/spec/ibc/images/Receipts.png diff --git a/docs/spec/ibc/images/ReceiptError.png b/docs/spec/ibc/images/ReceiptError.png new file mode 100644 index 0000000000000000000000000000000000000000..6ffa3f9558eaf0bc27f54ab449484320e24ea527 GIT binary patch literal 26687 zcmeFYbyQr>ucK9P1Il*94_pmjP=x%*DDD2$$v&{`uM=e zz&VKX*!b>a5)Ccz$w}+vkd%Ub_=61t9y@({+JW&n!X|wH39JuozZ?eFW1qO#|Gxu8 z@4QD!Ly(wcCE?NF^l>Oo4n=Kenp+J`_i$@}CKA`3Qn!q39xc&ZLAHiWluffAO-=qy zcANcmwvO|{Ge#`zH#v@kS+7^G_zX4ZK{V70X~)-eN~+c}9d}jVPbFDbUVomvv3~l@ zUe+nDQ^5uw7dP4hSfad{e0};ekW6mSYv4-@LUHd+eB##;Xu65V8t}9fdo951(u^9p zRKbU9^z=y;{orhfWYBvXw|snQEO;8CX=}yC_bXIR*p1)B{UqSy@Efu~@D02n0qWnn z!yqK#wELBkAci;X+>zY845pC+nc<|~D|<4(i$p&I99;xBYAhf;D0^t%gjz>xF9AY% zgO_X_$#2-LtYSuIq&Mpq)*gf`Ce5MmV}Gr5HIU*Gj3`k%`u_7&7M{eLsqZ=tmzkIV zCGi}CILT~|Nn;W>IPJk=Q$qUTtw&B`(MWzut1K)(lNVNo7pkbBPsaKkO(a#hcpV*%0yo)i7CH6 z#fb2r;+G0?XX*$e^-}$m^-2dVQa50UtguqyR`FrqR)2_>+7jnoA3Qi8$^RW0Js<^P zInkjB9A;L(iEI8etIWqV(d-n=;F2daoG-YK|F$AbiU7%q= z?-w=0=W$;S1ChBYeP(FHhp~zA#`)f)JvB}wYLmr}j-sMW^?HBdUd%DkX;lRZ!H0jg z5~)NVVjqkW619f<(@XuL6sk6{`aB^XM63Ta>dXTaKnJ>6DnlLXt)c$eF5mcvAeixu zFq&U6u`=c;EoHCS4W?SI;cMBlx&7noH@G1dQ#LA#o=6?Ie!e2LCh*^8ulZ9+{Zv7{ z)$Z34oSjDE_zO?`nBfS#2w4LZdV8;);70fb|4@LF8?Z zzb3|+7UQLYZ=s$V92E2kw*w=3hm>Dg_b0!ZR4Ac%bU13ccqqR3&)YxGnFe27XmfA- z&^N?pXyCF92R6`0)Qg%}T5378P)M*z zkA+$?QpNl(xFN(A%XwvynrhAk*aS9U+rQM^AcD)k{i%^N8NFVh!{cI6x`28l`Z{ht2SMV~u5I~-0p?Nd~`?SViReEfj-=4;)}$zxGLT{w0- z{9KlB@3OOM5BvDE$)lB+?w4t6b#F&1ECZA0kCc7i$*Q%9VeYNCf}Xr{#&!3$KM2Ll z)45Fc;OR4b{-;uW77{e$T8GVJ7$~|N#)F58yGacgor!NEX3q~RHJ}4ub2{O(@oDfw z=_nOT{3z*TI^=Upr*K$RsMCIW>b++ed{tgFjmC8`oa`Oq^;WD_LJ9AieV&lnbGSqt zg$(?aYkn?AHj8vn_ULo!xijV8%+idclKD#BY2>E#bW+b*TbwvXaj;QV*)Lmvf~+4wExaXBo*EfUaj~IY z^@|dg!*jzljKr*N2mPuS<+A#yReZr!iQEyUDpu>G6fM^JOnkOUZf~sX?KyKZw_|BM z7b;iZT~qzgp#adEo|s-j5C5i}+q;u5vkO=DRoy<aH9&?CJl7pTK|M+wAIMLEP+4F5`^od@T(w6gK7&(^#}sk zS4#9mAvL7H6&C&CB7?(z`dkO;CiMDQ~1AVd9qJRp68nkl?L-Pl*5W z)39qzT&@@8jzkaeA^iLm6t>{J%aP)zhS!T&vtcs>JPc@WX@}}bXGkD}7^q00X|BCd z*7k%?a8L=`*--iJ%T1AdktCMgg(s)#8$WR+z^vEOTP_COajjmS+Fjk0WSBoJf-U#O z?q7~D)F_Fj0*TE)b`~RPIvj5w>_5U3R zu#x|#pwZvV#!DA)BzNQW2s;>%$^Llgksl`Hf0_)@)KZU+{-MNqC1S15N$SR{(6OV+ zFnB0X>;qxv?Js~t2B`?To-@7lXy5;x7DPk#HZt{#kXW#d0INA@4 zrjuEpEjKcCPUi_W?54k4pHLRrv)$L`CS5s9^Tu-%MZ{t=GqFgYy(4K};-qL$4}bl2 z``v!%$g2kQbb`y4#TSJaqO@-grZn!aAFf&gdqUC6_bXfI$_=Y`dZ^WR-&iMwKU=#x zo|W`BpDCfx10SeWHYlZba2WZdn;4bX+_SE(qNztrFVCjR7I{wWmrT;`gX{F`7NJ3m zRXHe!9mFd}->Hl=Sxc2WsJfz1z;;d=ma4?2FRjhyH21YWAdrVtd}?ggI}>+aF51>BGLuym*m#ONIOGa+ldN>_pi1meYUAmHX6RhU4;LX7Etk2BAYfAPta6 z`5eSsl39ze@N_ht&*L_3PPa%In7LSI8<=^1ELY~l4tnV^5&5iWnq4l&{`9w8Wkb>? z=b&-#vI3RS%nMO_bAo}7RaTqk`%6Mg`)t#Um!f;>Z07(kZ90Q+UEH_D9$zjEq+)4J z;xrmO{Fu%nZP-~T~I>gST9nJCMhkil)a z-_-4e+BM?&5mfLWGs|`@-r0WyBk?t+n3M}NAH6Ng6zu&kGat003(}M`SwFsAWz-?0 z^JUE6)R}X@D5)>1zjvZ_jhiJ@-Q>4LWrJq!EVM{>ZW$(~<)(*$b0ZM5$5g`%IC-gy zCDQP@WWU)ZB37x1)-T|Jt09y-1Tr!oe*n`uKm6RON_<1ct5*c-IpG3sbJ&ZrYk(m5>$%wHxQn<#jdN_?G z@hhTI|TzuQ~_ zDxiSP^(WG(=p6qqE(D1r>-1EZo7@?{6o%Ll&|W!ueOhq~!Z^gZnXbtu9oaE7#rY^R zz2p63fi8&TC|L0tlRO>CZyT;>Nu={~=SW#HlDfr85JZ!u!wc6oq_(H!v+9U&x)huB ztXMz&i*VlI2(9wPY{bl344b&z*CnT6NsE!UO2pbZHLPFgegkB1PZ#=wpL~n`ackFP z+CEb(yr`-HFEdRevi}t1AnYSJ4KE`iBX{Lw|L(f+vbvZU$?ZxUgmL%8I>sxCg0Vz< z>yg8b`7+buA5Q4gPM=MUR6Q!}j$!w`Ls)#q$0Rw?!!?XChFAhX#Q<%X7>8a$5dG5y za`{xg2NK=9_H)`Enet0UGmlnOzE%d)Jmj{RCRCT(oo~;F)L(nMqK-Q zm@y6Cr=HHQbt#?35lrY{q#Hb{iA48yuLz9$GINS?G!0ZgS_lQ_ZTM&EuAn~q1i z@kyTd{dWCJrFXW$+Sj~F^}=4W(HE`JIE}gsbl}~WkUCi!>+a9ocn?!SSce#!hhT|9 z?e36rkYBUd`cS6WAwQwz)Twir|68Z5FP}fpSCE#=wfIU}M&9#o&3M}CgO+U*bNDLi zZ_8)P-qNhxUhVQ2^l0*Z%bfg>NLY{h=4uOP`U2DD#zKWT|iphft$=82nnY4>-AFPqXd$7}>bGLGvl^HCp zDVohqw_<%8gXDLOdSCv0zWglncxe&-Q;mLGi-A_tR+UzZ&-ey^XDnrCWG%kRh;x>@ zaP>{8y(|`z)3k0i72Ay~5}^>9Or#9%jf^6rFeE7K8a3_#>ZfkCFpa28*SOESoOxcu zQ^nwCj$#H)&6356dsU@NxCbKur~RBWEWY_)YDf0hCa-pdE z2VWPhh)289)_#^$Nt!c|zowGhU*HZ}o*{Vy?Hdi-G>jQruIe#VdYTP=mQ*>s7s4qZGG7Q{QrU z*TJ|m27hzKZ?GpwGXKZWo>5r&i!RDvS=w z+>TcwbkRcons+yHK%=dO8iAXKa+&gAjkp|c_Wo(w=_X@FJ>2f_90;=+UnTMfE^k75 zlA4c!vGf7YeFtjYf>BT>A+??^uKCmf33iP;(|x(X!3sa6ZJqGq5t~=_XP!5%)q$T@ zey3bLi+>1L7UWyeh2PH%ST8}4s7Lc2x;^V#2r$Rp*FE|<#~=sZ+Zv1$gE-OQkRkt= zBXbxy;p0eMAN}}+>g`XC(Ah~;k_H@e7G?sG>OwPH%UCs-} z3}gv&YYY>!Zh3gj%l!%0Eb@+>g_dPO8Z6%k+3GWFjwlVbI>ziFqJK-!n__<60>sP1H3%g=~6e}E< z_>tru_zFtyDp!&hUj3&fhxs$F>f}ek(BEZNF)!Q!Xydsb2ZXr&jXvjK9@Uk7z;+y2`C z;fCGwT^zBViyb66iqC7x{@-73;acv{?wx8S@I&(Jm63fbgGXj5h2It86}1R5vrISp z>24~zx;;HzYA4a%T>E6r&XRJ5a0UHC%?h2<7v^7G&K=VSsGZs#kO3UmW3w_jSE?wt z#ei|Oy~0clQ8(m6+oyiLzK{qa+_#qcMkDER+|CQXEZBNyS}?gOGTX+Azbii98cd$p zJ_Fo+Q4wP$j6u_rCt9HB3?sxX6fI6sgCw~Y!hJ)`f`aMyvk0}itpLG1Q}bdonM`?GtBY~Mt3ZWj3@m=^Lkp`I$`&vHjlv!i{E$u?5fN9 ztwk)YItp(!t1Ptu+{PC4ygeB@hc9Ghdq>B;Uwgqegii^mI-Hx3HXpQH$w=J}A26aNpx2~#AiU}1*#f{vs?w= zXxQX?Hb1n^wL_^0uXvG%2d||6rVg8{7`(r6Pc8Td1%qqHIl)a+GH(_O2yPVZf_r?W z?O#0gi!vCvqScAF|EM=hp@-ytbDJmOl*6ZkCpB4yeZwQy(&JZ2+!og-E14(jt%CnL zZx(qDh#z&50G6OB-inU)%GvJ|)L4pgAK|9DMUp$CkYCe=_qmoLt!X2}!!m_EBZ9;i zzgz>1k1N|p%3DHOXCyjcDxY`&ErhOKAbe_jWiGcj^Wr((`J6VS#kpAQx@~}ZPL6k~?bFFS3!4TCC*`2w@GIQBx%67K%}zTbYRc*jjg z9@sJTYl15`@0!Fb)oxdKW7nbubL@xF@*Ad~PjzcUXcr1hm2}aIKY4TJM~JU)U*>s# z=6Omm5$65bX`tF{#@g>YtNCeDpU2n>7!0-Ay)Ft&o))=u@Qh+TLj*jTV4-kPfRGYB zio5bgjh=%jc??At5WVCM@RIRPqnd0f5B_S1_cE;+^q$qbYzDsXP=*}tDSWLl8JaIA z#KWIQHsUFyuKpJYjesm9m-Lr8r*#Ec-SK9aNb=%X{l(BL{1>`PjV^ecoLDofLXwOFpi?ubY$6()16gtCF-%-NHt~`Hsk3ei2p4Mz@I5K z5A_~n%SQI&tzR)iwuo&wZgX6}$c(qn9Ya6VO~Otj-4P?MNo)751&?yIx}l6nY=0(a zAKzU_6>_s*)xsnf*Gj{WtCSr`GN=1lPhk+f0TvdPjT4}5@ZmmE^|kMN7sqpa5Jt%4 z_wkDMJ3EqIYS=xwEY~L29ej^dwWS66o%jw9ak-J8w-NeU)g}#CTOMp1>`*39jW-_y z97}}#XUm6zL}s77X=4~INKodX^yX)dcDZ}g+te*tto?9!V&6BD_2n(s!e;|c32Z&& zp9`u%6br$#=!xRZ2OYZ?23Ib8-m5|^IGfTm!P@rttK;bi&D^0(K5al}`_^h4yKkvAgVJRjr&x)f_KCNMSaX@? zQ7$d^C4y+X33IlZ5K7<|l8_&)0rhS)2+m*p+y`9fBWzxOiQ=@U2%W;vLFYwm5amE- zYr+iX-@w&*`gX24MO+QcBC`8Zwh!VN%4vW2&eQH}TECP%M2JuS8fn)%9wh4YEYEg? z#&d8xbSAQtgpZ;0mo?O=T=Qq)?lp_w>V0eb86pjUor_J}G(+3BqOx3K!0X?j_XB^z zpEa>z)K~|KMrf}+k$w0eIcVjJbK9c6{fydA60CnfK(`-l)uaxBXA(`i5aqUla8#14 z37+mAg?>0kd{^MBc3mwrE5lj>fKek|(rb*8q6~T-K7N)vq%l@GybngS#Iq{ga`#!w z^qgf4fFz2Iu7J%bDr2ywYJcv67TwpBeeQ}Ft*oI=Wxq#RRDksklcLBkYL%rokXBb! zIN7GSKLAXk_>kD*bTBU-L*ID49?IvmugM&cjErp36@Cm)H^^uk$ENcW%+p{rN=iaq z^$f5MSFd}o7cazF>h$lc>h)ilty&(4b0;DXTf?FY+D1KF)7V^TRK^qcUH~G{E(=ui z3&jVyand@L&`(eDa$yFNQ0uLGtz${P)s_(=lxaebw=i@uej>fb_cRy@_^z+fGOCF9 zXl0n5_ZjD0?6%@V^!v70#6J$apsy~HaDNAn|C86J0dF(`!>4$`H;CdbJp`LV=8F;$ z=Mx>(!^zy?h=IlkDZ?_KQ9|B$GV)9z{)EATTrdiMlM%vgQI{MHwsK52Z zOr#MiL_atVK??iTgU*2?i@025L@(7BOlxQP!0OFsd~wT+_04prKZ?2L^{Z@9v-ZVj zZG0^gVgc}8%=?>?ozB6ULZW6GDy$jJ8Rj6QVSQcV@r z57Q(b1_ZRF1g$2q)Ol2GkXe^b?9OobPjXG07H^T;)FEHHa7iX{t9uBiwKG3F;NBCU z8aT@#B5DM4+AG-QWj@bJP>}iZw((CnD?6F5CBliasLv$xPxkl1H4Vd`Qpz8=c0zAk zZ(6!rlkfg^Atb}tMFNkQ1OQIKUSt*atweH@%>~H?@SIfpasZxn#qYiG-u-N<;!i^V zW-X3l>Ij`+frkGc$V(_o+V;Lt9(oBU7Mdj+z1n~JDp&EjbCvebi3g&JmvCcru7!(2gF={C$vF^_nQt&wvGZa-^x zNx3x0**8{$#>9yvneuPkFW^r<=T8}SUl-|LM{@=d4=kR>;IcTu_ar>W)Zq&A-a|H8 zEilhpiVqUebc5s*BR8DugASMDU)evu*r|u=Q&=tEj^wjnx;zOsB9KEgvxNcX;-4Fp zl%{;5WxJ1iWY`|t3RI^8d6@8z`c6YBTc46#I6O57NCYlesr-SxUP8=)kOrspt|=@e z>O+7E{?D`U@BrCtj6Q2mI)OGgQ!zH8*#>U zwY4$NyYPE>Fh3rSPDQgG)i}!3lv2%LVbkK0H-N69IqjzpwuU}{LvKHJfs9rcKdEPi z&;94lfJJtgGUu@Q9=IRRHGS$kU=&}bLoT{icQE4dh5WT`9dXPkiiAvCVGzYVDPJ=| zxpNGPEX2WP2eE99QtaGdN}!td!P9U^G~k@n=Xg5h!YWe(Q|H#IqvkEn*{}TN&})@cYlQ)Dxg{o6;1y* z=|cJ1-zfg$EEqSkkCHY!@)-P!`Hv9l-|?ip{>L5_p3n~%nF>But_ZW2Smcf+@1D=u z`q?R)jPEhyQn)ds@jyz(B_22zI~eH3Mj!ZIp-|Qh6O8<(>JWCN`9*lBZ*vR6IHLm?_g}WHL%e|*GzxU&~#8x~tMaKzostB@Z|MN&6{U)K0=}P~f$J757ue=ZV zEBF8egE~Q64PG`It5Nm2#utS7Zq;H-)kxViq5(?Cw4b)gRy*0G0>1c1+4&uHjNq=Q z2W+34MU~3hhp`p5h+=u5MUJETe`Y@4JYbSMPhXM1O;>9Cq7iyoN@*(M<@)?c@H`ur zZ&#!S{iM!l^5;w6Q=o_wXUEq{Q{WwsWNg9ZOm)=$$==3u3t>N=*A8ym_IA zeZT1IlSNin!9OBaiwv*D>K?8@wi7d3EqY*iMqb)lhPv<%IFuqd{t#O~gpUw?rx(D*EHdv#8Y1P6}}Tq z2bUdh*%(U0B1Q986QwIQX?rn--WtU~r&GAHKU%Y!R%@v>l)K$b#{%@Tfr;X&4s^y_ zD6-b%*fry*8BBW@CU#WViHijOstFQH4fx*hx`5*g$`pbl&&UD0Rq#3rqQ{pUmsqr! zWi;-Gi}2-9<+=nk3wwK}Q?H!DKXqK0==8L;=HEq`kpQ9I7;Vv(docN6-xJ3iTm?}M zhr@Cns5J6a9hy4;q=LmFfxy?-sK0e@5X7aw7rQU8^$9eFDt%Usz<^E5St3Zk<&r0I-C2IB>G)tA_)7j_Sx>hV}y<*wugxC(=N9*H$GT&E<>yghK2Y;4DGB7?ZFJ%Cf zBXQaKSht|trh0w3i`%WH?{*B_PtlyHZo*rODV5&2^p*3ba}>PbnD7}9vY0Dh6Aeh4 zd~SW!L8BWgwsgT$o=Ns0;be-p9@;&Ssk z_+~FF8elg@+R0lY?!NI!>c>4iRvmLl;(@ooCQ;P>2#vaP12(&ifi=^|z&#NWY|3wV zLaGU50}G(4X)7Mu>rn;!*@pJRMpWBGjJ^xpU$JgD&AI^|=P9mt8mSI=0B|KgwqhK2 zWSnyW@ztS}i#kmy<|WeS0=Wb%Xt!kRTI6a>sieJK$Vjok;H!Ig_b<+}65D$eHRZ!@ zg$Ye4(I^6Kmpz+uBSU^49?F*keqX(+&#d9DC%^FBqpm|xiWL=Yz_@A@<{yKZTj9YZ z(TMVKRUqKeBb-KaYkbP_mwUK{l)f0;e^RZ;;rS4EfA55p{&YWg27ydJKyo)^-Nem` zRWjXy+4u&EBcfvlCeN_DAGEgfy?eoVD9{_Rg)?WxV%F~%r7LdEa7BA7roo8#N0Ljg zkkyT3*Ga?Oeg;{ts(Q5p`ShiGly|h zmb%91R+yqpHo10fVfw1ZDA$@(*?*#<#Rd$aTf5-1u-WcUQs0FTe$X3GH6v&*K^*w3 z{gOQ}4-2nn52MmcshzGtWx`0Ml45)eV!~gM<}2FD`exc{%(*7RRo5JQ0qZ#fHvN-^ zbnzS4V1xPg32MRtVs#7Zk41_uuKw&pUGGuJC2D>>iTmHUtHOkNFKEAl__(0NaR@p0>uYjVKNxxiO-X-Dj2A_O^}M@4y*|{vSww z^!FF6{}d>@rC&0nHNtr>bAZNC`XRJ3OTERDf!64@J$qZ@B<8oReG4CYaKL3x^pj&o zw2*`&I~VXl>XTB{0Gy}zGWypHB4|HY+PEFK>@7trltndu4u|Ez!yBXx-b1E@!C8vYbup?(2nL4-2e0q zEJt2<-dUDN{Bgx!qrhzZ(FaL$<=^ehD65lcO|{b*8XOa~5UgzZACrQXf2FDZm9zx3 z8BA{YcaG;pi);5eG+T40u)j2e^S>lO_YQVVZaMwpn|FZ+C!$KBfMkrHNrE7T=A|cT z0{s=56rwS$w_mZ7mbys)mD<&HS@W0O&wmBcY`28Wm)mf$)o;`OGpT&JJ#g)$&u_m= zJn+%c_=waxvf0v!$O`{1(~+47me6*Z(RDWL)6h@yWHGGr^tkCVW87X@*=IBTior!v z;XLtb|3xNTn#gd4=CkRDj^=BOf24Rn)Q!RdDzGwv@*Yag$H_?r&S|b8p8uR1C_GT2 ztjFW?@G&WWk-5{hGw}n+R)>FtR@!b#z+%%86oLW3@i+N#>U6imIzGpco~ak~0VrUpaKBJ=FeS*3|zErd3T3^H#*dmVP@u`09I8L5U9`F}%wh z4OUgCz`7GOxv=z0p+9z@vE{?fP8vvMRtHFx4U;5>(8_n4A7OI`V37E1Wq5R-8=>#* z?~Q;T@F+d64J#Yor?vql2%Ra;Wo8ra;eKfHYrF9%OpS>5iT|topv@P>=yz|T!6j$F z)F~{_GPSd4u-A%mXVr}n@c|EuvT3XsYRZV;qV2Yfyo~?$)_jK37!5->s}R zl)h@h1j-&JRTvaAI)RD-B_$=VP_}wdliO7Qj!(SFRD-h}kk6OiUTG1%_Y_sU0`^?4b5r9r0i)2FS@DI=+Jt3w+6+8=|JIppWeF z{gL5XaC@XE3c+8w8FoJIW5`UJeNiC*Om3-~@%0ah(A|C|Y?Zw)BdA0pGVgygB)zgw zDeh^x`ID->7QjgHI4TnxTlWBai?tIMQ2V(&{_U;9^4BdI!qDU;e9K%|4}}*jES*49 zdl1I;VNzaO@{*=YZD6f7L!vqGi_g>LMNjF#s%2m&v1PL zrD=0sP)4FCirnM@D|i!}%Y&=@R87s{;btopkPEFUg{UWaqQ2+XCNjgPCS<^rIRgKt zG_Y;QTx(Jf^9CCnhUc$)Oq-6Dx&=L#!9b@pBfG*Cwdm*>0 zY_2J&>W$II<2m|T30PS=jb+W-ip>rpNrnSS~JoY(2MMiPBySv36!fckd{Uo9E|3Q~ap z6GaAcdssqHR_C)0b!)$HDLA&dfOZ3n0#eDWQRKdClmKnR=ZAr?(whiLH6sw=i>M_- zbGS(Z5n~y(-k>%sjaAec$m_gvj$9h`b)WTPn(y$Iy-)fMtsZzKF#Sll*Zp;ST?I)k z1V4(*nlQMpAx*k~^a7C@*!3t(pPs-ih9D@awObj3Pf`Jc)vx|2CsHA^*J865qj1|R z;aEk?!WD z3RXd(H_N&aJ+a(QBcU;1e>q$(H+OlvLTgb@jj-lNX}a%kzko#VoL|oxe!vb0@1OIA zeKuWeBL*xO@ckGKhi3DX{FwtB=|{a(PlRTA5+Kij_~5dXnfwfgWjL_I)^{lxyXI{P zDwk~5BvS{0JoRCX==FRAsE*H49ru-PM2bKjNp_G-AiJLa(e~NYGS9Xlz+5@q&NS!Q zF>rSz;FL!2O*F?aYD~DD8R>$4TAI?Zxg-$^f9ioXE_a9$1#~B?Ao`h&I>f?@N>#lb}nr= z{X&4W`-Pd1KRsT;C`1{R$|o+_6|Vz%*FM~8;}LRW{B;TmM0xNcdZ*>Xti-*$r-2=7 z>0uRJ6#Utgp6t|PEf373hI`_+s&wuo2UMHu^IdqDlA>~j*XT+XO(F)eQ2Svt{CJoe zEO>6Hmi=t%0pa6ugE6SDs}I$rhUbezROQ_8dm@m9`^qF1mjrOTL?$Z#wg)>^16%6e zG~nf<8&Y<-K0sT<1hYy?&E;Y0lILwMB;O*?gde5O8@{f11|e5O@32WD>*bJ4?glJQ zT*!A?$`?MxZNX@`Ch4#_670+h(EVOj+3orzYxAyzx0xkgfX@w+vS9+FeIt&!DVbnx zpV$q!Wn69(CC)9wyC(_VU*%X%*8ar| zu7YH>VzL`YKi!7I{O)5wPc0j7(~`|E__-PlKqgp#QBfwGZ2^mV9{W-O&?0AXI)Dx9 z6VF2O?jr>PD}#KE$A1>6r|QR3o0nkkz%H68C(Jf}mt;bV^E0)1{wM# zs65VbJ8cxE64B+`P00!j{rf?V{jQG;Mp@VrumaD{;`zIs9S81>|uJc~H z$gHw5m`Bzkg^i9y{L&bt=W(I-NY$5(aNdvS+hc%-?L7q$UeG5gadG%%waxd`u8!lB8VpOooMPM=fU^PtLkZhdQy$l)oGTOAO&2KyLsXyof%EQIlN$^&G0MPDves-S z^xh1EjHl@KC|%Gw2RIweR+>cy7t{8?W7a6?gfZ!n?-Ei&u=|z94w3;FZH1-HXmBp; z!3@VsN1jA>O&1A$zzb|g&3A0%Sb;DRa=|)JA_EEhRp2-P_-qPlKd*t!mkn}KS{q|B zR4}OV2_L3kWM!s9r1$4PMEzL?a3od%_{ItAv<7gnJzqH&1|zF*9(g2uz3N-L>Vs|% zz<0?Q*dq5()JmG!>$=&>)Q5!2e>xc-np7Xk z5GEtibD?oMTDAeU#x~0%ri4c)WD#regxCW|uywbUJ4!zs*erv#gv#CF#GD8{e6KfK`nBQBxnW>-eMa%HMS zoYPW+bI%I&!5OB!#gEnzGb9|05}N6PA(J~$ejVI{Lk5kQQ=okl8qcgD0fNa^>o4AX zdaqWVe=Tl(y+FoCs;j3<_HckT`JDU+3ZJO~Z8{D5;a z3$*{T8RA;y>;yVHVrsp-`8;I%Vob~Jd`w1JNeQO2j6<-^JMX1t^qThezLKy0<-Dm(@~q(|0M6onZ*v?9E3hEhh9gzz-HSOH_7p%AVbm@Muq zP+L~(HaKMihrlE*Gk^}791NK4s>mN|W4d%Jb&LOUw;gsF7~svg)c zpMJlcy?4O*5kbYb)YFg)$oa6(diTsZDd?HPS`h|;U-toHJ3uj0}}e~kXCN%kVf++9Ju$9 zldIRB!Ji$b!xJArck4QhQA94>zR&iWkbn$|1>3^qj^$~9%6$E*n1Zp+XS4*YIgS*# zRWsP)42R`;Ve*VZU?(H7iktVtBu;4GV^Vm<=E$l z|GwnV%zL`QM)w!YO;y)Sr2wfcdArM;ccSPU*SC)R`oP0k#w(Im2cQfV5VIi=J$wo1 zBRRAFK!y3x?m`c2iRJyGXk$eBttS@*35DCC><00fS9x+o5nR)35nNK3uv;*|9!c`t zc@JC<@x@3lE^zCyP8UM$;eD@JUHvYj(``HtwFkJo0_Xw&$c=l8nmy!O+HruCvUtF7 z_n%c6&GS(zdzY?H3^7fJwBBk>< zUDYHYD!;1Irmzh%-d}avck=@P^O*r29>CPk?ccg4Pt`>3FsxLJYmggn%Xhx%G3=YrBVYLyZ8912qbVppg6l4~&*DGU z;=EgQPJlvU8L{iuBTpQ;k;+@XJK2r80Z6m*KbX5xn_)7xPA#eMv)OqtF>}Mk9gkW1 z0DAg)Irh6hAiM@Z-?+m7r7^uYFuJvy(Ar_fdO4Y-_AnOo;K&!z!&{l6HP(XF^8#vK zDO#V_{@;bS(g>8e1LI@zYm)TbyP+8D#r5$i*VSDVendn5jS7m^IWV}J6O-4U54#$< z1D83I1bri5)de%TZtD7D4~&=D#HW@!_8SkjBwpoNl!S$Ih3db?R&H34OLtrMXJSrq zKbk4kc~UpyLN1i+`1-g{jKTWg+_5haN_ym8Uu~MKn$dlbSnZ2*VA$eF-2V}ih3*pm z&|KpRN&rtIu;H&r=K?tm$iTx{R|&iQBIH`tfS*e^t@mddH&L?w{+abApQQ1NN3oHd zfn$f?C^|%C*Tb~Edu_^uPnkqfQO(^Co?;<)rQvPa4{L(UxZ?KeuXxAT1PC#E3i}qj z5ECvA$P$S{3)|)`1F_+Ag#`_g9^hR-%?tn@lf2{^X7PL?Jg*xuBO1QG7q9HzFU0a&rHpB zht5L`K1%-G#i|Qnk}Mq$b`c#QF}r+Zc=U-qLH*T9JuAWNv9>;+02*ivH=;SWto|eA zYyILD^)Fx6Ix5a>t!`#D%6}(|rb5O{s{pyaJ?A(Zj``h_q3FsIaACLXzbE)>qJ?Sy z0($)OWWH3t3GGxZa>GAWQ1U6l>9?Bo3ldHverC>_7w|XG@2dvmw^*JcxS8(1RG>#{ zp(i`7Z)vV>$(keVy4zZZq6D!!#~r(^MC%FO1U3{vOG+H^9AxgWh~GDr;8)oSAUm2z z3W~gOU8+|R9(32ctNI{AY8bGA5&aV|_r~fgB0G08S_wOViSWvE>v0z#W2jdD-ZD*Q zXlf~aVHB9~g!9tuYuOT6XLfNsU->@wdH^E^be-G&n%A9$#`9Lhjc8XC1O2Am-E*Y!mOW|MlPnXbh_c z0}5J5814#m)<^p}AjVQWnpkBso_cNnrvQ1TVT<(#WyUjxZnkl%h2zAxG!T}Z553>$ z)}lzStIKG;Jik(KnE$Eot||VAjO=I~>DiVyqb4uLzM~SR_x`SG;k7Q-PDu1@w?jbh z`@igT<~fv^b>J7U)yu~nG*5pp*niwh7@WE_K-?Mx1r0Q6HtB(fs>d|M9_BvGs$OJF z*=1`iPq~=QCFF5T%>FVNa_WZ)k5uvHjp{qTxAF{OsB7RFvl>;pKaFJIVn4gun%;a`Yz; zw?8Q5U$n;023|v;mxm%nKkp8yT+|DHe6~{IrCE5BQoK6O^x=E0N)_v_cCnMBHLja} z3*lYaZnayN?F{$+uF(CW+O!YI#|tS3M@$~a3ZWd~o8_?w z)FFnLV^xxq<^)n-GUAy0+wI*u%H!B8{=S=#wfyT>mV_u0F4hxO7jt%dp1;am1#|K| zgS6d7tB0yzh|o~BeF>}dC;5?^u2I0dCPxEjx8J*0VZHpp8z+E0(Ay)R7f8}9N?E@= zTypFCq+i~*m>yQg-~06$DXagJL+qCk92@^3mVCY`-#Tb&ARii= z5V-4Z)=gxRc5HOGBbtceZoGx`c?+VUnd<0L9ObeJdFGbtCOapTcJ3%UrZZ9lHJ z?3pItMy!TBs0uMrL^$>cUy~DKGJYV8JOvoDO)(BOHZ!|&ZCJRK?|YH&zowb~0X|l} zDCKv}aXvMHujAndB~)*hjS2UpU;LUxhbUUJFNm#$U(-f^93Ph_{&h2Z_L17JwMNMh z3Ksmzs`5(LP0i{x{qJ^JdSm>K_c3!O9vZ(=aoOT&-PJ_2tN1k`N%*Hk6QxIn-2YE| z*BK4h*Y5QQqDM(|iCzZLJ3*pHi5^DpJ%rIk)F>gM*XYsB=p=d=q6?!%?~HB)_waw; zyWS6X-L>xbyVvB7Jwl%2BP5$~wcFcsxRq zWhhkgga3zO8v+66yvP_=Qx9ophiBn+AYFT~7*7Bw;k-3M!he4t?RDIH$G=6ktznlA zbJT>XrXo_}Ulq2I#$Q{g(7=`$1d5myIss8^CFF@qf*^8B2IsG67tv|kQGdntIcbK)^K#sn~w@txE8bu*A9}|NJfhN`>igbdQ&4zcMhG z-q!D(<=bp_Eg0FDE}e727eMh$rGM&?iC0%t{`_wGVA|o`Xh3q!E8eBrd>GR7H{cOG z=n>p{l|V%Z)iFn~S7SVcDjM5KynoN9_2NBW?B$_O=Fb;L7ov7wwb-#_W&HeF1`u@n7}s!r7qB|V4C z9=k5ka!cs?GoY0nOM7pEHvtYoQoNi>aBa>c{ z_F3;JmW^FO>Eo7!^=Ny8rLpCN0%%n~D%R~~5TuTU7fjCmiN9>!Xz<&WpH@e5 zqD`ab>5ZI{%SS{$mO;Z(^G$jmk%r4twPE1=qa1D?9>P$Z2O-ayeqa(w1eItP<>loq zHMzarY>pQ`bFm%M^*IiOWb4+0mUkx#%jD)~+jDv5gx9}LH@Gq55YZ9H61y${03D_M z?2L`RGw&%i(-g`oCE*2@>KpB#LKfQ5wPFUg^$x^Q3=2Po-jGmJQNI*Nx(x_TmW!%ax5Vft4x4YJm(c zdSFMjn48Imq$gi3^%`4D$54DQNl%NaCe4f_x-fRiEWf%D+O z16y0$?*JzF%G5NjsYz^naxxDFqwsB|6BHCIuBcFa`&LjrQ!FfzRYR_5Otxq&x3m-w z2*?lHLyL=xWfT;!)iXqb4-egwK7Y>r_Kl#Rpg<(xuZXg;vZk?d%+iuMjkp(qf`S4Y z7uRzzm@$@4GAbklgGSUH>*C^~@7FIS0KRMcd#FMqK6=Pg^a?&l#Pvz zeLsKx0B9|4i{H_A7TW1A&-RtPyo73MYH~(wfh#^WH4=a;!Ta;A0AxvYx-%X6OZtYU z*?V76Q4xoqpFch;E4Hrg+0G0Uzu9Y7&fcCwOG~TDV_p3TD=V|8D7CoP&iC)Zn9V!o z{)@r)4vxn(mUpIW(Sq`x;=h7gf4;nqozpB#a_z&se*Eyt%m`IeI{8g*gmfVz(RyPz zcdAWlhuogWv~6czs?!|;K#7fBHXW)OMWB|SAZE7ZT#s+Olw+3e@DpI4L}W}5bn_de z^soGvqQQ*J(opCoSDni}xl=er3hnX@CKQBcq@ES#GsuRE?jrBdUM_|GM@zbEr?R>6 zvVym3ehQNjD{`B7pA*~Z6roX}R1Cs+xjOenW%Ey6>X>WQypOSv_2o+vXs;b)tN!?J z*5d@+{Kr{Gl;P4Ckbt9NhA0&K3ft_u6j)UKD8cS@KI9e8;>E1>BO9=&v@E&H)5ICRaY(9FWDq z(NSo7yYz~?C~yTFCT1@dNnf!CoYrRl`EifFE0jnoA}9!rjT}$ncv$}3Ms9eH4hQlm z`-Z0dyqovnpp$6%PdL4znHfV!NC?RBHweI^3EKaz^5$xl902i%#RMg~AC&E`5l_wMW(R`&}g3P>r>ql7m&-n z762Ur2>dez+*~d!mlS1nXlqD@6=erJN@P*%SoxXA=W}Q&aRe7g;kUvoRL_jE$@ZuC zC3VSleGo3a7#H}Lxudg_gpMxOZmbYEr4VDRNTc_7a|}7_@#_x&O)FViJ^|qCU}^zt z1`ZB9_r{j(eK;2S*{lZ<2M32{gRPXkkc;cWLe=9^r@|$b-8}Hn_1Bz;%tA<&~_HQqqm$P$&mo;>xY7zL)aZ+a($#8tIMK zRZi(nx)s|f*|H)KGZq62v}&Nn2e5fp?G$h#(%Hbk05GQr=B`_d1V*(zenkoX+L>i~ zTYce*Qo*wQC}x3E+9xVsc)ANzzED~FwE(l6akGV~Ky>B4@5x(J0qt(_j+m$11RIzBE(5g)*&s+mf*a9Tc4pSk+*6n-0#Wn9+#QT=scWTZGRz(W;qGnDN zW%tfVqcpsYer@VJe|ZJEl+ie|pszP25P)R%WX?x{7;)Wtn_>kkXwbo3m!PIQKhYVm z*xPZ259obdhIPfgZl??II=yW7g$04_!m?J>xCy`=SSMbFm-~NMJvGl}bRT2}<23nb z;^n^Jg68D2Qyz_DJmFYhfcJrX)Y#LwPWm$PLvq~lgx@HWQKN6`tFiRmvx`TZ6JV|87vtl(u(mRcrO- zpXHTb3JLF!{<4N`zyHS(>_q2-?}+buW=g_6O@PxwUPmF^!lTuCAi6p_W_|ID`V9wb zrz?nG*4Do>_mD)w-yE#YC6+5h(x-D|Lo4w4yqtFhxauh{2@)K#?hDw;&2T-$io^=x zCpZ#ma>=ULJzGiA}Bj@f942`KvpZ}RBD za3IY`&C6+cPg`)Pp_{=`uSIUgA&)sLTi1GEm#QPXb5(isqY_5Ix!IF9`f~YAalZMN zvAEOP`wJ0$k6o{$wu_XnRdW>81Tz9WunTwvoklt}$_+Z!5DEj0Eqcxg0n@p+Nf8p$ zD8;SQV~EA$j6F~q-D>};C{DV{FH$5dsLH9RJCk(ongXjEt83R|KGhYkd%&7Whw)+W zi%?>zz_M+$g{m{uLcz;W;_JQOZEv1gOQ=&i+H~;)&_cia10xa^+OYAhHpqPc{3TI8 z8JWaYa>Ft(`T4fCYM|h${t<0^XVDl*7N)&#b~7(-YPne-_2*m+&$}XEk^7lxSv@)o zc00ITYt1E7O^vr*XZsQ)&tu2LY$lj#b7A}63q`?4$)lg7P~ShXen{PYu%vz;jP-h5 zuhUrjL9GxG#O9vt66MDh0n;D8ZRkF=59433GB_{GZa{lATl}S1F79i#L*T37<80E> z&x>>;w#!66ED(x-W9Bb1(p_VS7V?{@JOrg^La2<;7_=U_}yS z_R>gr#8xV2uH={?yl%BUTGNi$e6)+XWbN4{iNXC9#!6;g&UsV02s3f*KkxU+ZSCs= zKD4y=E*eFz_VA9H(eGut)&1~kQ5%}6RDp#cWhMn36t%v}e3-HrhTCR0 zX!|Gu6+&;RJ=)c}szf34^nt8#6UEvmK(`b*hMGIL0zIGNNcQ%_i`5L>=4RqZSacbD zwWZ)FKSv)~bjEko$E|xN?H3$|=R%&36D6)bor(L8+Qh&>`FtW34rTDJEjKN;b<4a# z#}Mct8S-S#4{L3P88&q$xiQG1kMFqunSdMK>$Lwl4A^u%mh6BpV9f`A@kC1%<;@ZU zRjTqKF3o}dm}t9M{Mqu z6fvNF7VfyjLS@2*=#i2iU%$m&fbrL%Hgz?>n?)YYjDs@_SZ$cnUM~rqwb50FQDc6a z(J+QnuEtGo`u1kIGp>-OxParD%NjFuYGIwW{6Fl6JBud}J@#%OMRf3vS6wOrTUL3{ z-!Rp>>B$K%OETD3hY^@Rlcg*#gE3<;$g0`GD25(OUdU?<>NMsem+}njrD39WO5@4; zdReEb4A2^FgOVcCt$-2@lxluYw~O_Ycmg9_r+kr~1`etkw{$R!SG(T8&%Z2&Eh|6h zFZP2O$9m{P$DZVv{x`{d`MbkhY~kGRVbcUhGsJu?A~06WKU&!4gw$ZCS;jW+E#GOc zKx}h-=PS3fHh+(tB=xR0h%2x5Zzfb&)8n*L$>oNGP~s5ow^MOe*^FC|e}DiP8x?^8 zrD(Me84tM)B1+??W_#xKj8dk-L4fLt!x*~klyjutU|b=Y+2$o4+m~SQHR7ZQBVysk z_1-947}w47*~B@g%C|}F(s@Gk)#V!a9$)wjzYeA1zKzEyjgQ2oXAJ{6i_gGuXfl|> zwicdFIk_NWBjaCSy>A0yyE9^y#ig4GKF<00N@Q(^L5&r5ZUDAqhIGeh_Xgd9324*iOSH8v7rx-WfaEwH5aGmqp8F z%O{ZX{J&Rx&4_acNTECf{+kF11fAv48gXCGu8dJFNjI-I0uf*~`nV1d!~Q~9teg5Bg=HXjA_i$E(;X9wwwpx+ZpuO^oHxoxHY z@+Wmtc3|KswEH~8^O=^$8EhA?i(($Nym!F|&AB?r z&ei!#{Z#F=cl^YCH4hxGHWz7qS;I<}L!oG~y}8|6^NaXI@&mOnNqWpbqN|AT7=?#` z7wGiU(_Qj*z=eR^pqltU^i4GHd1UINV#1dEUKxC>5)x+Gx59oh>J~AVf$e0+ekk-y zp_n1q9*jQIRN2foKwtm9>=D(+HEsTq%& zqGJ{LR)bdEQ;qjrmICm>!K!2)N{tt;#lUNIaX;fQvow;gHDT;z3J}P-(i{W#g;R(>KkE0L4=J*4QlHQAKGYk!zWP*k}%&M56HHh#TC zor1LqQZ-5n?xAZ7AzR&#n>Kyc)V7Zz=!#92VUKjvbp1kFNM($%0~Z%TaCs6z3)x zXi{82y?TE@6KI%5yXTt%z&~odGEbQftQa^MG@=VGCyxhLTgJo64 zNr(3nWrrHF1V3jA-D)Iy~2E$%R)LWPe4^=#FZ8*x?W5CBfJpyN zzJ3kN&=q{}hDcye$)s$zzV+FL2VLPWYyCMTuEZ;^^`VG*_q~+;@06$*X83Wa)22(e zl>Ex0e<^I*H{ovVsF$nfyRQ(vmVXoyW>uD_a3tyt_r7~sDEI&H)MWqRhj z!P&^&+vU^0+?s|N=rekuB4|t;RU6ndb}p)7y3|JPR*Xmp<&uQL<5~dH07c_Bz;PEWC?rN$#{Pg*xUX? zYPlJB^|$~q%#{qh2z8bxt$5Q0Kdde_JiEVjwsU%H6yulTul`8;&#SoXSDV)TI`5J^ zdf%a4#_UsE61@5ai1O>vp_S!eOxFabp1#!UH0m)oXzdu#;wF6ZTMd4NHlWlX?c64# zhC{T=jDYft0Od&nde0xbJk8DHoo&4$J5Z@&6^1$=}_lRF^%?u4IWRVr=Jo&?Y>xmfLdT~qlwRAaQ zEp>9D*v4dKsCAEL#vjJOE!LQD_xPh3E)zt0q*^JI@G@-ecga=XHTEuMY?J zgjN`q7vD%=u5#)zoSo=t*1baqeCKZguN71%m~Q`Q<0m>=aW2jTS)i3;J7<+@Y+(J{Kvg=1!<6kLTl0BHloXixZW*XG(b_ zANbrv$cF#)G-535w3M5d{^vFTPykiJrC8N0hJF~O6<6}DGpxrlGUWxt0gS~Bp4+Cc z?z1jhvuxdol#@)d^(U|yM);P+>G{Q0B8ijEsif?!=*xxfCPO3};qAjrygzR5Qo6xk zCT2}qbx0x41T(}xL`1Xz2hEd=m~5;Ro`{?dF#$rSf!L6!yYEpkF&)a?HpQD=+khR9 zl6P^I@A>l;=q(iyD1Dr_d`zGu=>03F2UF#318&Qkf9LdQ`FBnapQpE6K%g8d-&P5f zmCyfQ(VZawR~ez$;LpGx4S~g`Zb_BnJ`I~La`>YDslLXGfAJ(fd`Q7C zu}u8QU9B#)Zp(_dICKcZV%z0jbGhbn0w_PJ59Aa|NSln&RrWN#uMlkHe~%0MD2H@h ze+(moKi5!UYG?lDg+NGdxO%*d{CvSU>qTz47J&}mK z%X^bRR0Zr~5bc^R4R~xlT!)^^FSuqs*=at)=uv?eB9_pedYAX+5FOA05KC(+C$^Xh z>?8;(gg4@PWDGhI!X}Oy?(*Lx@b>lhqX4x62+%)01?T_NWj1fnCa*P%e>(A=1Brpq NUMi?RuaGkf`7eI6vKIgV literal 0 HcmV?d00001 diff --git a/docs/spec/ibc/images/Receipts.png b/docs/spec/ibc/images/Receipts.png new file mode 100644 index 0000000000000000000000000000000000000000..19306867c5d1e03338a8809f2407e9af5ac8904b GIT binary patch literal 26870 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 Cisyj< literal 0 HcmV?d00001 diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index f118361e65..8a2b9beb36 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -12,70 +12,22 @@ To build useful algorithms upon a provable asynchronous messaging primitive, we Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). -Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x _ +Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: - +_A:send(msgi )_ → _B:receive(msgi )_ -_ y_ means _x_ is causally before _y_, and chains A and B, and _a_ +_B:receive(msgi )_ → _A:receipt(msgi )_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+_A:send(msgi )_ → _A:send(msgi+1 )_ -_b_ means _a_ implies _b_: +_x_ → _A:send(msgi )_ ⇒ +_x_ → _B:receive(msgi )_ -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ B:receive(msgi )_ - -_B:receive(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ A:receipt(msgi )_ - -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:send(msgi+1 )_ - -_x_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:send(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - _x_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_B:receive(msgi )_ - -_y_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_B:receive(msgi )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - _y_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A:receipt(msgi )_ - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC0.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC0.png "image_tooltip") +_y_ → _B:receive(msgi )_ ⇒ +_y_ → _A:receipt(msgi )_ +![Vector Clock image](https://upload.wikimedia.org/wikipedia/commons/5/55/Vector_Clock.svg) ([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) In this section, we define an efficient implementation of a secure, reliable messaging queue. @@ -88,37 +40,21 @@ We can visualize a queue as a slice pointing into an infinite sized array. It ma **init**: _qhead = qtail = 0_ -**peek ** +**peek** ⇒ **m**: _if qhead = qtail { return None } else { return q[qhead] }_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**m**: _if qhead = qtail { return None } else { return q[qhead] }_ - -**pop ** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ +**pop** ⇒ **m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ **push(m)**: _q[qtail] = m; qtail++_ **advance(i)**: _qhead = i; qtail = max(qtail , i)_ -**head ** +**head** ⇒ **i**: _qhead_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**i**: _qhead_ - -**tail** - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -**i**: _qtail_ +**tail** ⇒ **i**: _qtail_ Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. -**Key:_ (queue name, [head|tail|index])_** +**Key:** _(queue name, [head|tail|index])_ The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. @@ -132,10 +68,10 @@ As mentioned above, in order for the receiver to unambiguously interpret the mer The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). -* _ibc::send_ - all outgoing packets destined to chain A -* _ibc::receipt_ - the results of executing the packets received from chain A +* _ibc::send_ - all outgoing packets destined to chain A +* _ibc::receipt_ - the results of executing the packets received from chain A -These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ (_remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. +These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. ### 3.3 Message Contents @@ -151,86 +87,23 @@ _Vreceipt = (result, [success|error code])_ A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. -Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send_ _queue_, which enables all the proofs described above. +Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send queue_, which enables all the proofs described above. The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. -_(IBCsend(D, type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ Success)_ - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ push(qD.send ,Vsend{type, data})_ +_(IBCsend(D, type, data)_ ⇒ _Success)_ + ⇒ _push(qD.send ,Vsend{type, data})_ We also consider how a given blockchain _A _is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: -_A:IBCreceive(S, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qS.receipt =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, reciept, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a send"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_A_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ k = (_, send, i) and head(qS.receipt) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_i_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("out of order"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TS _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ valid(Hh ,Mk,v,h ) = false _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ v = (type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(result, err) :=ftype(data); push(qS.receipt , (result, err)); Success _ +_A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ + * _qS.receipt =_ ∅ ⇒ _Error("unregistered sender"),_ + * _k = (\_, reciept, \_)_ ⇒ _Error("must be a send"),_ + * _k = (d, \_, \_) and d_ ≠ _A_ ⇒ _Error("sent to a different chain"),_ + * _k = (\_, send, i) and head(qS.receipt)_ ≠ _i_ ⇒ _Error("out of order"),_ + * _Hh_ ∉ _TS_ ⇒ _Error("must submit header for height h"),_ + * _valid(Hh ,Mk,v,h ) = false_ ⇒ _Error("invalid merkle proof"),_ + * _v = (type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err)); Success_ Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). @@ -240,98 +113,22 @@ When we wish to create a transaction that atomically commits or rolls back acros To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: -_S:IBCreceipt(A, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.send =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a recipient"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ k = (_, receipt, head|tail)_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("only accepts message proofs"),_ - -_ k = (_, receipt, i) and head(qS.send) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_i_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("out of order"),_ - -_ v = (_, error) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(type, data) := pop(qS.send ); rollbacktype(data); Success_ - -_ v = (res, success) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(type, data) := pop(qS.send ); committype(data, res); Success_ +_S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ + * _qA.send =_ ∅ ⇒ _Error("unregistered sender"), _ + * _k = (\_, send, \_)_ ⇒ _Error("must be a recipient"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ + * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ + * _k = (\_, receipt, head|tail)_ ⇒ _Error("only accepts message proofs"),_ + * _k = (\_, receipt, i) and head(qS.send)_ ≠ _i_ ⇒ _Error("out of order"),_ + * _v = (\_, error)_ ⇒ _(type, data) := pop(qS.send ); rollbacktype(data); Success_ + * _v = (res, success)_ ⇒ _(type, data) := pop(qS.send ); committype(data, res); Success_ This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. +![Successful Transaction](images/Receipts.png) - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC1.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC1.png "image_tooltip") - - - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC2.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC2.png "image_tooltip") +![Rejected Transaction](images/ReceiptError.png) ### 3.6 Relay Process @@ -342,27 +139,19 @@ The relay process must have access to accounts on both chains with sufficient ba As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. -_while true_ - -_ pending := tail(A:qB.send)_ - -_ received := tail(B:qA.receive)_ - -_ if pending > received_ - -_ Uh := A:latestHeader_ - -_ B:updateHeader(Uh)_ - -_ for i :=received...pending_ - -_ k := (B, send, i)_ - -_ packet := A:Mk,v,h_ - -_ B:IBCreceive(A, packet)_ - -_ sleep(desiredLatency)_ +``` +while true + pending := tail(A:qB.send) + received := tail(B:qA.receive) + if pending > received + Uh := A:latestHeader + B:updateHeader(Uh) + for i :=received...pending + k := (B, send, i) + packet := A:Mk,v,h + B:IBCreceive(A, packet) + sleep(desiredLatency) +``` Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md index 21cbe0b6b2..9674abbb32 100644 --- a/docs/spec/ibc/specification.md +++ b/docs/spec/ibc/specification.md @@ -10,7 +10,8 @@ This paper specifies the IBC (inter blockchain communication) protocol, which wa Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. -We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value are provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. +We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value ar +e provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. The protocol makes no assumptions of block times or network delays in the transmission of the packets between chains and requires cryptographic proofs for every message, and thus is highly robust in a heterogeneous environment with Byzantine actors. This paper explains the requirements and structure of the Cosmos IBC protocol. It aims to provide enough detail to fully understand and analyze the security of the protocol. From f5a45a94f6e5741bedf7c3da4f1a0ac92ad82bb1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 21:05:23 +0100 Subject: [PATCH 07/32] Clean up optimization section --- docs/spec/ibc/images/CleanUp.png | Bin 0 -> 38843 bytes docs/spec/ibc/optimizations.md | 288 ++++--------------------------- 2 files changed, 36 insertions(+), 252 deletions(-) create mode 100644 docs/spec/ibc/images/CleanUp.png diff --git a/docs/spec/ibc/images/CleanUp.png b/docs/spec/ibc/images/CleanUp.png new file mode 100644 index 0000000000000000000000000000000000000000..b9b5a1d348bdf5deb6a38fd80bca90bfd67f870e GIT binary patch literal 38843 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.send_ or _B:qA.receipt_ we currently have the following guarantees: -_A:Mk,v,h = _ +_A:Mk,v,h =_ ∅ _if message i was not sent before height h_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+_A:Mk,v,h =_ ∅ _if message i was sent and receipt received before height h (and the receipts for all messages j < i were also handled)_ -_if message i was not sent before height h_ +_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet processed)_ -_A:Mk,v,h = _ +_B:Mk,v,h =_ ∅ _if message i was not received before height h_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+_B:Mk,v,h _ ≠ ∅ _if message i was received before height h (and all messages j < i were received)_ -_if message i was sent and receipt received before height h _ - - - _(and the receipts for all messages j < i were also handled)_ - -_A:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_otherwise (message result is not yet processed)_ - -_B:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was not received before height h_ - -_B:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was received before height h_ - -_ (and all messages j < i were received)_ - -Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime._ +Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime_. _Vsend = (maxHeight, maxTime, type, data)_ -_expired(Hh ,Vsend ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_[true/false]_ +_expired(Hh ,Vsend )_ ⇒ _[true/false]_ We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: _IBCreceive:_ - -_ …._ - -_ expired(latestHeader, v) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_push(qS.receipt , (None, TimeoutError));_ - -_ v = (_, _, type, data) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(result, err) :=ftype(data); push(qS.receipt , (result, err)); _ + * …. + * _expired(latestHeader, v)_ ⇒ _push(qS.receipt , (None, TimeoutError)),_ + * _v = (\_, \_, type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err));_ and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. -_S:IBCtimeout(A, Mk,v,h) _ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.send =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unregistered sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a receipt"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ k = (S, receipt, tail)_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_match_ - - - _tail _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_head(qS.send )_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("receipt exists, no timeout proof")_ - - - _not expired(peek(qS.send )) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("message timeout not yet reached")_ - - - _default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_(_, _, type, data) := pop(qS.send ); rollbacktype(data); Success_ - - - _default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be a tail proof")_ +_S:IBCtimeout(A, Mk,v,h)_ ⇒ _match_ + * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ + * _k = (\_, send, \_)_ ⇒ _Error("must be a receipt"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ + * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ + * _k = (S, receipt, tail)_ ⇒ _match_ + * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt exists, no timeout proof")_ + * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout not yet reached")_ + * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); rollbacktype(data); Success_ + * _default_ ⇒ _Error("must be a tail proof")_ which processes timeouts in order, and adds one more condition to the queues: -_A:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was sent and timeout proven before height h_ - - - _(and the receipts for all messages j < i were also handled)_ +_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven before height h (and the receipts for all messages j < i were also handled)_ Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. @@ -192,118 +64,30 @@ While we clean up the _send queue_ upon getting a receipt, if left to run indefi The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: -_B:Mk,v,h = _ +_B:Mk,v,h =_ ∅ _if message i was not received before height h_ -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

+_B:Mk,v,h =_ ∅ _if message i was provably resolved on the sending chain before height h_ -_if message i was not received before height h_ - -_B:Mk,v,h = _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_if message i was provably resolved on the sending chain before height h_ - -_B:Mk,v,h _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_otherwise (if message i was processed before height h,_ - -_ and no ack of receipt from the sending chain)_ +_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed before height h, and no ack of receipt from the sending chain)_ Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. -_S:IBCcleanup(A, Mk,v,h) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ match_ - -_qA.receipt =_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- - - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("unknown sender"), _ - -_ k = (_, send, _) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must be for the send queue"),_ - -_ k = (d, _, _) and d _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_S_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("sent to a different chain"),_ - -_ k_ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_ (_, _, head) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("Need a proof of the head of the queue"),_ - -_ Hh _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_TA _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("must submit header for height h"),_ - -_ not valid(Hh ,Mk,v,h ) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("invalid merkle proof"),_ - -_ head := v _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_match_ - -_ head <= head(qA.receipt) _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_Error("cleanup must go forward"),_ - -_ default _ - -

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(Back to top)(Next alert)
>>>>>

- -_advance(qA.receipt , head); Success_ +_S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ + * _qA.receipt =_ ∅ ⇒ _Error("unknown sender"),_ + * _k = (\_, send, \_)_ ⇒ _Error("must be for the send queue"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ + * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of the queue"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ + * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ + * _head := v_ ⇒ _match_ + * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go forward"),_ + * _default_ ⇒ _advance(qA.receipt , head); Success_ This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. - - -

>>>>> gd2md-html alert: inline image link here (to images/Cosmos-IBC3.png). Store image on your image server and adjust path/filename if necessary.
(Back to top)(Next alert)
>>>>>

- - -![alt_text](images/Cosmos-IBC3.png "image_tooltip") +![Cleaning up Packets](images/CleanUp.png) ### 4.3 Handling Byzantine Failures From cdf08ecdb7b719eaf02eb95a4ea563ed7d330efc Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 21:10:30 +0100 Subject: [PATCH 08/32] Fix typos --- docs/spec/ibc/optimizations.md | 4 ++-- docs/spec/ibc/queues.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index a6e45d3589..e6c2f8cbcf 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -10,7 +10,7 @@ Sometimes it is desirable to have some timeout, an upper limit to how long you w One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. -For a sending chain _A_ and a receiving chain _B_, with _k=(_, _, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: +For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: _A:Mk,v,h =_ ∅ _if message i was not sent before height h_ @@ -26,7 +26,7 @@ Based on these guarantees, we can make a few modifications of the above protocol _Vsend = (maxHeight, maxTime, type, data)_ -_expired(Hh ,Vsend )_ ⇒ _[true/false]_ +_expired(Hh ,Vsend )_ ⇒ _[true|false]_ We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index 8a2b9beb36..b0f459c40a 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -58,7 +58,7 @@ Based upon this needed functionality, we define a set of keys to be stored in th The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. -A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _must refer to the same _v_. This property is essential to safely process asynchronous messages. +A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. This property is essential to safely process asynchronous messages. Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. @@ -77,7 +77,7 @@ These two queues have different purposes and store messages of different types. Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. -We define every message in a _send queue _to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) +We define every message in a _send queue_ to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) _Vsend = (type, data)_ @@ -94,7 +94,7 @@ The permissioning of which module can write which packet can be defined per type _(IBCsend(D, type, data)_ ⇒ _Success)_ ⇒ _push(qD.send ,Vsend{type, data})_ -We also consider how a given blockchain _A _is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: +We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ * _qS.receipt =_ ∅ ⇒ _Error("unregistered sender"),_ @@ -114,7 +114,7 @@ When we wish to create a transaction that atomically commits or rolls back acros To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: _S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ - * _qA.send =_ ∅ ⇒ _Error("unregistered sender"), _ + * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ * _k = (\_, send, \_)_ ⇒ _Error("must be a recipient"),_ * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ From ecb1f93e190d22822b2f1150697a3898b25b84c7 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 13 Feb 2018 21:17:27 +0100 Subject: [PATCH 09/32] Join appendices into one file --- docs/spec/ibc/appendix-a.md | 11 ---- docs/spec/ibc/appendix-b.md | 62 ---------------------- docs/spec/ibc/appendix-c.md | 87 ------------------------------ docs/spec/ibc/appendix-d.md | 33 ------------ docs/spec/ibc/appendix-e.md | 49 ----------------- docs/spec/ibc/appendix.md | 97 ++++++++++++++++++++++++++++++++++ docs/spec/ibc/specification.md | 10 ++-- 7 files changed, 102 insertions(+), 247 deletions(-) delete mode 100644 docs/spec/ibc/appendix-a.md delete mode 100644 docs/spec/ibc/appendix-b.md delete mode 100644 docs/spec/ibc/appendix-c.md delete mode 100644 docs/spec/ibc/appendix-d.md delete mode 100644 docs/spec/ibc/appendix-e.md create mode 100644 docs/spec/ibc/appendix.md diff --git a/docs/spec/ibc/appendix-a.md b/docs/spec/ibc/appendix-a.md deleted file mode 100644 index 289699aff1..0000000000 --- a/docs/spec/ibc/appendix-a.md +++ /dev/null @@ -1,11 +0,0 @@ -## Appendix A: Encoding Libraries - -([Back to table of contents](specification.md#contents)) - -The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. - -In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./footnotes.md#6)] and google's protobuf[[7](./footnotes.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. - -The tendermint-specific data structures are encoded with go-wire[[8](./footnotes.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. - -For the following appendixes, the data structure specifications will be in proto3[[9](./footnotes.md#9)] format. diff --git a/docs/spec/ibc/appendix-b.md b/docs/spec/ibc/appendix-b.md deleted file mode 100644 index 226f590606..0000000000 --- a/docs/spec/ibc/appendix-b.md +++ /dev/null @@ -1,62 +0,0 @@ -## Appendix B: IBC Queue Format - -([Back to table of contents](specification.md#contents)) - -The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: - -_key = _(_remote id, [send|receipt], [head|tail|index])_ - -_Vsend = (maxHeight, maxTime, type, data)_ - -_Vreceipt = (result, [success|error code])_ - - -``` - message QueueName { - // chain_id is which chain this queue is - // associated with - string chain_id = 1; - enum Purpose { - SEND = 0; - RECEIPT = 1; - } - Purpose purpose = 2; - } - // StateKey is a key for the head/tail of a given queue - message StateKey { - QueueName queue = 1; - // both encode into one byte with varint encoding - // never clash with 8 byte message indexes - enum State { - HEAD = 0; - TAIL = 0x7f; - } - State state = 2; - } - // StateValue is the type stored under a StateKey - message StateValue { - fixed64 index = 1; - } - // MessageKey is the key for message *index* in a given queue - message MessageKey { - QueueName queue = 1; - fixed64 index = 2; - } - // SendValue is stored under a MessageKey in the SEND queue - message SendValue { - uint64 maxHeight = 1; - google.protobuf.Timestamp maxTime = 2; - // use kind instead of type to avoid keyword conflict - bytes kind = 3; - bytes data = 4; - } - // ReceiptValue is stored under a MessageKey in the RECEIPT queue - message ReceiptValue { - // 0 is success, others are application-defined errors - int32 errorCode = 1; - // contains result on success, optional info on error - bytes data = 2; - } -``` - -Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. diff --git a/docs/spec/ibc/appendix-c.md b/docs/spec/ibc/appendix-c.md deleted file mode 100644 index 3eb870d715..0000000000 --- a/docs/spec/ibc/appendix-c.md +++ /dev/null @@ -1,87 +0,0 @@ -## Appendix C: Merkle Proof Formats - -([Back to table of contents](specification.md#contents)) - -A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./footnotes.md#10)]. - -There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./footnotes.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. - -We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. - -The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. - -A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. - -For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./footnotes.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. - -``` - // HashOp is the hashing algorithm we use at each level - enum HashOp { - RIPEMD160 = 0; - SHA224 = 1; - SHA256 = 2; - SHA384 = 3; - SHA512 = 4; - SHA3_224 = 5; - SHA3_256 = 6; - SHA3_384 = 7; - SHA3_512 = 8; - SHA256_X2 = 9; - }; - // Op represents one hash in a chain of hashes. - // An operation takes the output of the last level and returns - // a hash for the next level: - // Op(last) => Operation(prefix + last + sufix) - // - // A simple left/right hash would simply set prefix=left or - // suffix=right and leave the other blank. However, one could - // also represent the a Patricia trie proof by setting - // prefix to the rlp encoding of all nodes before the branch - // we select, and suffix to all those after the one we select. - message Op { - bytes prefix = 1; - bytes suffix = 2; - HashOp op = 3; - } - // Data is the end value stored, used to generate the initial hash - message Data { - bytes prefix = 1; - bytes key = 2; - bytes value = 3; - HashOp op = 4; - // If it is KeyValue, this is the data we want - // If it is SubTree, key is name of the tree, value is root hash - // Expect another branch to follow - enum DataType { - KeyValue = 0; - SubTree = 1; - } - DataType dataType = 5; - } - // Branch will hash data and then pass it through operations from - // last to first in order to calculate the root node. - // - // Visualize Branch as representing the data closest to root as the - // first item, and the leaf as the last item. - message Branch { - repeated Op operations = 1; - Data data = 2; - } - // MerkleProof shows a veriable path from the data to - // a root hash (potentially spanning multiple sub-trees). - message MerkleProof { - // identify the header this is rooted in - string chainId = 1; - uint64 height = 2; - // this hash must match the header as well as the - // calculation from below - bytes rootHash = 3; - // branches start from the value, and then may - // include multiple subtree branches to embed it - // - // The first branch must have dataType KeyValue - // Following branches must have dataType SubTree - repeated Branch branches = 1; - } - ``` - diff --git a/docs/spec/ibc/appendix-d.md b/docs/spec/ibc/appendix-d.md deleted file mode 100644 index 64b8565fcd..0000000000 --- a/docs/spec/ibc/appendix-d.md +++ /dev/null @@ -1,33 +0,0 @@ -## Appendix D: Universal IBC Packets - -([Back to table of contents](specification.md#contents)) - -The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. - -``` - // IBCPacket sends a proven key/value pair from an IBCQueue. - // Depending on the type of message, we require a certain type - // of key (MessageKey at a given height, or StateKey). - // - // Includes src_chain and src_height to look up the proper - // header to verify the merkle proof. - message IBCPacket { - // chain id it is coming from - string src_chain = 1; - // height for the header the proof belongs to - uint64 src_height = 2; - // the message type, which determines what key/value mean - enum MsgType { - RECEIVE = 0; - RECEIPT = 1; - TIMEOUT = 2; - CLEANUP = 3; - } - MsgType msgType = 3; - bytes key = 4; - bytes value = 5; - // the proof of the message - MerkleProof proof = 6; - } -``` - diff --git a/docs/spec/ibc/appendix-e.md b/docs/spec/ibc/appendix-e.md deleted file mode 100644 index f07360850d..0000000000 --- a/docs/spec/ibc/appendix-e.md +++ /dev/null @@ -1,49 +0,0 @@ -## Appendix E: Tendermint Header Proofs - -TODO: clean this all up - -This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. - -In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. - -**Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet** - -**-> needs input/support from Tendermint Core team (and go-crypto)** - -**Registering Chain** - -**Updating Header** - -**Validator Changes** - -ROOT of trust - -As mentioned in the definitions, all proofs are based on an original assumption. The root of trust here is either the genesis block (if it is newer than the unbonding period) or any signed header of the other chain. - -When governance on a pair of chain, the respective chains must agree to a root of trust on the counterparty chain. This can be the genesis block on a chain that launches with an IBC channel or a later block header. - -From this signed header, one can check the validator set against the validator hash stored in the header, and then verify the signatures match. This provides internal consistency and accountability, but if 5 nodes provide you different headers (eg. of forks), you must make a subjective decision which one to trust. This should be performed by on-chain governance to avoid an exploitable position of trust. - -VERIFYING HEADERS - -Once we have a trusted header with a known validator set, we can quickly validate any new header with the same validator set. To validate a new header, simply verifying that the validator hash has not changed, and that over 2/3 of the voting power in that set has properly signed a commit for that header. We can skip all intervening headers, as we have complete finality (no forks) and accountability (to punish a double-sign). - -This is safe as long as we have a valid signed header by the trusted validator set that is within the unbonding period for staking. In that case, if we were given a false (forked) header, we could use this as proof to slash the stake of all the double-signing validators. This demonstrates the importance of attribution and is the same security guarantee of any non-validating full node. Even in the presence of some ultra-powerful malicious actors, this makes the cost of creating a fake proof for a header equal to at least one third of all staked tokens, which should be significantly higher than any gain of a false message. - -UPDATING VALIDATORS SET - -If the validator hash is different than the trusted one, we must simultaneously both verify that if the change is valid while, as well as use using the new set to validate the header. Since the entire validator set is not provided by default when we give a header and commit votes, this must be provided as extra data to the certifier. - -A validator change in Tendermint can be securely verified with the following checks: - - - -* First, that the new header, validators, and signatures are internally consistent - * We have a new set of validators that matches the hash on the new header - * At least 2/3 of the voting power of the new set validates the new header -* Second, that the new header is also valid in the eyes of our trust set - * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header - -In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). - - diff --git a/docs/spec/ibc/appendix.md b/docs/spec/ibc/appendix.md new file mode 100644 index 0000000000..e818ad9ef5 --- /dev/null +++ b/docs/spec/ibc/appendix.md @@ -0,0 +1,97 @@ +# Appendices + +([Back to table of contents](specification.md#contents)) + +## Appendix A: Encoding Libraries + +The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. + +In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./footnotes.md#6)] and google's protobuf[[7](./footnotes.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. + +The tendermint-specific data structures are encoded with go-wire[[8](./footnotes.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. + +For the following appendixes, the data structure specifications will be in proto3[[9](./footnotes.md#9)] format. + +## Appendix B: IBC Queue Format + +The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: + +_key = _(_remote id, [send|receipt], [head|tail|index])_ + +_Vsend = (maxHeight, maxTime, type, data)_ + +_Vreceipt = (result, [success|error code])_ + +Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. + +See [binary format as protobuf specification](./protobuf/queue.proto) + +## Appendix C: Merkle Proof Formats + +A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./footnotes.md#10)]. + +There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./footnotes.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. + +We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. + +The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. + +A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. + +For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./footnotes.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. + +See [binary format as protobuf specification](./protobuf/merkle.proto) + +## Appendix D: Universal IBC Packets + +The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. + +See [binary format as protobuf specification](./protobuf/messages.proto) + +## Appendix E: Tendermint Header Proofs + +**TODO: clean this all up** + +This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. + +In order to prove a merkle root, we must fully define the headers, signatures, and validator information returned from the Tendermint consensus engine, as well as the rules by which to verify a header. We also define here the messages used for creating and removing connections to other blockchains as well as how to handle forks. + +Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet + +-> needs input/support from Tendermint Core team (and go-crypto) + +Registering Chain + +Updating Header + +Validator Changes + +**ROOT of trust** + +As mentioned in the definitions, all proofs are based on an original assumption. The root of trust here is either the genesis block (if it is newer than the unbonding period) or any signed header of the other chain. + +When governance on a pair of chain, the respective chains must agree to a root of trust on the counterparty chain. This can be the genesis block on a chain that launches with an IBC channel or a later block header. + +From this signed header, one can check the validator set against the validator hash stored in the header, and then verify the signatures match. This provides internal consistency and accountability, but if 5 nodes provide you different headers (eg. of forks), you must make a subjective decision which one to trust. This should be performed by on-chain governance to avoid an exploitable position of trust. + +**VERIFYING HEADERS** + +Once we have a trusted header with a known validator set, we can quickly validate any new header with the same validator set. To validate a new header, simply verifying that the validator hash has not changed, and that over 2/3 of the voting power in that set has properly signed a commit for that header. We can skip all intervening headers, as we have complete finality (no forks) and accountability (to punish a double-sign). + +This is safe as long as we have a valid signed header by the trusted validator set that is within the unbonding period for staking. In that case, if we were given a false (forked) header, we could use this as proof to slash the stake of all the double-signing validators. This demonstrates the importance of attribution and is the same security guarantee of any non-validating full node. Even in the presence of some ultra-powerful malicious actors, this makes the cost of creating a fake proof for a header equal to at least one third of all staked tokens, which should be significantly higher than any gain of a false message. + +**UPDATING VALIDATORS SET** + +If the validator hash is different than the trusted one, we must simultaneously both verify that if the change is valid while, as well as use using the new set to validate the header. Since the entire validator set is not provided by default when we give a header and commit votes, this must be provided as extra data to the certifier. + +A validator change in Tendermint can be securely verified with the following checks: + +* First, that the new header, validators, and signatures are internally consistent + * We have a new set of validators that matches the hash on the new header + * At least 2/3 of the voting power of the new set validates the new header +* Second, that the new header is also valid in the eyes of our trust set + * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header + +In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). + + diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md index 9674abbb32..5864337df0 100644 --- a/docs/spec/ibc/specification.md +++ b/docs/spec/ibc/specification.md @@ -36,13 +36,13 @@ The protocol makes no assumptions of block times or network delays in the transm 1. Handling Byzantine Failures 1. **[Conclusion](conclusion.md)** -**[Appendix A: Encoding Libraries](appendix-a.md)** +**[Appendix A: Encoding Libraries](appendix.md#appendix-a-encoding-libraries)** -**[Appendix B: IBC Queue Format](appendix-b.md)** +**[Appendix B: IBC Queue Format](appendix.md#appendix-b-ibc-queue-format)** -**[Appendix C: Merkle Proof Format](appendix-c.md)** +**[Appendix C: Merkle Proof Format](appendix.md#appendix-c-merkle-proof-formats)** -**[Appendix D: Universal IBC Packets](appendix-d.md)** +**[Appendix D: Universal IBC Packets](appendix.md#appendix-d-universal-ibc-packets)** -**[Appendix E: Tendermint Header Proofs](appendix-e.md)** +**[Appendix E: Tendermint Header Proofs](appendix.md#appendix-e-tendermint-header-proofs)** From 32ffd2d316e74ca49c9d733030dba439c5568f2c Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Mon, 26 Feb 2018 10:48:12 +0100 Subject: [PATCH 10/32] Review the IBC specification --- docs/spec/ibc/conclusion.md | 20 ++- docs/spec/ibc/optimizations.md | 188 ++++++++++++++++++++++------ docs/spec/ibc/overview.md | 101 ++++++++++++--- docs/spec/ibc/proofs.md | 102 ++++++++++++--- docs/spec/ibc/queues.md | 219 ++++++++++++++++++++++++++------- docs/spec/ibc/specification.md | 28 ++++- 6 files changed, 530 insertions(+), 128 deletions(-) diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md index 37d555e6c4..db5833ee1c 100644 --- a/docs/spec/ibc/conclusion.md +++ b/docs/spec/ibc/conclusion.md @@ -1,7 +1,21 @@ ## 5 Conclusion -We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. +We have demonstrated a secure, performant, and flexible protocol for connecting +two blockchains with complete finality using a secure, reliable messaging +queue. The algorithm and semantics of all data types have been defined above, +which provides a solid basis for reasoning about correctness and efficiency of +the algorithm. -The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. +The observant reader may note that while we have defined a message queue +protocol, we have not yet defined how to use that to transfer value within the +Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that +defines the application logic used for direct value transfer as well as routing +over the Cosmos hub. That paper builds upon the IBC protocol defined here and +provides a first example of how to reason about application logic and global +invariants in the context of IBC. -There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. +There is a reference implementation of the Cosmos IBC protocol as part of the +Cosmos SDK, written in go and freely usable under the Apache license. For those +wish to write an implementation of IBC in another language, or who want to +analyze the specification further, the following appendixes define the exact +message formats and binary encoding. diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index e6c2f8cbcf..ef6259a0e1 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -2,104 +2,214 @@ ([Back to table of contents](specification.md#contents)) -The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. +The above sections describe a secure messaging protocol that can handle all +normal situations between two blockchains. It guarantees that all messages are +processed exactly once and in order, and provides a mechanism for non-blocking +atomic transactions spanning two blockchains. However, to increase efficiency +over millions of messages with many possible failure modes on both sides of the +connection, we can extend the protocol. These extensions allow us to clean up +the receipt queue to avoid state bloat, as well as more gracefully recover from +cases where large numbers of messages are not being relayed, or other failure +modes in the remote chain. ### 4.1 Timeouts -Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. +Sometimes it is desirable to have some timeout, an upper limit to how long you +will wait for a transaction to be processed before considering it an error. At +the same time, this is an obvious attack vector for a double spend, just +delaying the relay of the receipt or waiting to send the message in the first +place and then relaying it right after the cutoff to take advantage of +different local clocks on the two chains. -One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. +One solution to this is to include a timeout in the IBC message itself. When +sending it, one can specify a block height or timestamp on the **receiving** +chain after which it is no longer valid. If the message is posted before the +cutoff, it will be processed normally. If it is posted after that cutoff, it +will be a guaranteed error. Note that to make this secure, the timeout must be +relative to a condition on the **receiving** chain, and the sending chain must +have proof of the state of the receiving chain after the cutoff. -For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: +For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for +_A:qB.send_ or _B:qA.receipt_ we currently have the +following guarantees: _A:Mk,v,h =_ ∅ _if message i was not sent before height h_ -_A:Mk,v,h =_ ∅ _if message i was sent and receipt received before height h (and the receipts for all messages j < i were also handled)_ +_A:Mk,v,h =_ ∅ _if message i was sent and receipt received +before height h (and the receipts for all messages j < i were also handled)_ -_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet processed)_ +_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet +processed)_ _B:Mk,v,h =_ ∅ _if message i was not received before height h_ -_B:Mk,v,h _ ≠ ∅ _if message i was received before height h (and all messages j < i were received)_ +_B:Mk,v,h _ ≠ ∅ _if message i was received before height +h (and all messages j < i were received)_ -Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime_. +Based on these guarantees, we can make a few modifications of the above +protocol to allow us to prove timeouts, by adding some fields to the messages +in the send queue, and defining an expired function that returns true iff +_h > maxHeight_ or _timestamp(Hh ) > maxTime_. _Vsend = (maxHeight, maxTime, type, data)_ _expired(Hh ,Vsend )_ ⇒ _[true|false]_ -We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: +We then update message handling in _IBCreceive_, so it doesn't even call the +handler function if the timeout was reached, but rather directly writes and +error in the receipt queue: _IBCreceive:_ - * …. * _expired(latestHeader, v)_ ⇒ _push(qS.receipt , (None, TimeoutError)),_ * _v = (\_, \_, type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err));_ -and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. - +and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that +the message was not processed at some given header on the recipient chain. This +allows the sender chain to assert timeouts locally. _S:IBCtimeout(A, Mk,v,h)_ ⇒ _match_ * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ * _k = (\_, send, \_)_ ⇒ _Error("must be a receipt"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ - * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different + chain"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header + for height h"),_ + * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid + merkle proof"),_ * _k = (S, receipt, tail)_ ⇒ _match_ - * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt exists, no timeout proof")_ - * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout not yet reached")_ - * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); rollbacktype(data); Success_ + * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt + exists, no timeout proof")_ + * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout + not yet reached")_ + * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); + rollbacktype(data); Success_ * _default_ ⇒ _Error("must be a tail proof")_ which processes timeouts in order, and adds one more condition to the queues: -_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven before height h (and the receipts for all messages j < i were also handled)_ +_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven +before height h (and the receipts for all messages j < i were also handled)_ -Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. +Now chain A can rollback all transactions that were blocked by this flood of +unrelayed messages, without waiting for chain B to process them and return a +receipt. Adding reasonable timeouts to all packets allows us to gracefully +handle any errors with the IBC relay processes, or a flood of unrelayed "spam" +IBC packets. If a blockchain requires a timeout on all messages, and imposes +some reasonable upper limit (or just assigns it automatically), we can +guarantee that if message _i_ is not processed by the upper limit of the +timeout period, then all previous messages must also have either been processed +or reached the timeout period. -Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. +Note that in order to avoid any possible "double-spend" attacks, the timeout +algorithm requires that the destination chain is running and reachable. One can +prove nothing in a complete network partition, and must wait to connect; the +timeout must be proven on the recipient chain, not simply the absence of a +response on the sending chain. ### 4.2 Clean up -While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. +While we clean up the _send queue_ upon getting a receipt, if left to run +indefinitely, the _receipt queues_ could grow without limit and create a major +storage requirement for the chains. However, we must not delete receipts until +they have been proven to be processed by the sending chain, or we lose +important information and sacrifice reliability. -The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: +The observant reader may also notice, that when we perform the timeout on the +sending chain, we do not update the _receipt queue_ on the receiving chain, and +now it is blocked waiting for a message _i_, which **no longer exists** on the +sending chain. We can update the guarantees of the receipt queue as follows to +allow us to handle both: _B:Mk,v,h =_ ∅ _if message i was not received before height h_ -_B:Mk,v,h =_ ∅ _if message i was provably resolved on the sending chain before height h_ +_B:Mk,v,h =_ ∅ _if message i was provably resolved on the +sending chain before height h_ -_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed before height h, and no ack of receipt from the sending chain)_ +_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed +before height h, and no ack of receipt from the sending chain)_ -Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. +Consider a connection where many messages have been sent, and their receipts +processed on the sending chain, either explicitly or through a timeout. We wish +to quickly advance over all the processed messages, either for a normal +cleanup, or to prepare the queue for normal use again after timeouts. -Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. +Through the definition of the send queue above, we see that all messages +_i < head_ have been fully processed, and all messages _head <= i < tail_ are +awaiting processing. By proving a much advanced _head_ of the _send queue_, we +can demonstrate that the sending chain already handled all messages. Thus, we +can safely advance our local _receipt queue_ to the new head of the remote +_send queue_. _S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ * _qA.receipt =_ ∅ ⇒ _Error("unknown sender"),_ * _k = (\_, send, \_)_ ⇒ _Error("must be for the send queue"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ - * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of the queue"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ - * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different + chain"),_ + * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of + the queue"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header + for height h"),_ + * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid + merkle proof"),_ * _head := v_ ⇒ _match_ - * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go forward"),_ + * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go + forward"),_ * _default_ ⇒ _advance(qA.receipt , head); Success_ -This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. +This allows us to invoke the _IBCcleanup_ function to resolve all outstanding +messages up to and including _head_ with one merkle proof. Note that this +handles both recovering from a blocked queue after timeouts, as well as a +routine cleanup method to recover space. In the cleanup scenario, we assume +that there may also be a number of messages that have been processed by the +receiving chain, but not yet posted to the sending chain, +_tail(B:qA.reciept ) > head(A:qB.send )_. As such, the +_advance_ function must not modify any messages between the head and the tail. ![Cleaning up Packets](images/CleanUp.png) ### 4.3 Handling Byzantine Failures -While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). +While every message is guaranteed reliable in the face of malicious nodes or +relays, all guarantees break down when the entire blockchain on the other end +of the connection exhibits byzantine faults. These can be in two forms: -The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. +* failures of the consensus mechanism (reversing "final" blocks) +* failure at the application level (not performing the action defined by the + message). -If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). +The IBC protocol can only detect byzantine faults at the consensus level, and +is designed to halt with an error upon detecting any such fault. That is, if it +ever sees two different headers for the same height (or any evidence that +headers belong to different forks), then it must freeze the connection +immediately. The resolution of the fault must be handled by the blockchain +governance, as this is a serious incident and cannot be predefined. -The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). +If there is a big divide in the remote chain and they split eg. 60-40 as to the +direction of the chain, then the light-client protocol will refuses to follow +either fork. If both sides declare a hard fork and continue with new validator +sets that are not compatible with the consensus engine (they don't have ⅔ +support from the previous block), then users will have to manually tell their +local client which chain to follow (or fork and follow both with different IDs). -The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? +The IBC protocol doesn't have the option to follow both chains as the queue and +associated state must map to exactly one remote chain. In a fork, the chain can +continue the connection with one fork, and optionally make a fresh connection +with the other fork (which will also have to adjust internally to wipe its view +of the connection clean). -Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. +The other major byzantine action is at the application level. Let us assume +messages represent transfer of value. If chain A sends a message with X tokens +to chain B, then it promises to remove X tokens from the local supply. And if +chain B handles this message with a success code, it promises to credit X +tokens to the account mentioned in the message. What if A isn't actually +removing tokens from the supply, or if B is not actually crediting accounts? + +Such application level issues cannot be proven in a generic sense, but must be +handled individually by each application. The activity should be provable in +some manner (as it is all in an auditable blockchain), but there are too many +failure modes to attempt to enumerate, so we rely on the vigilance of the +participants in the extremely rare case of a rogue blockchain. Of course, this +misbehavior is provable and can negatively impact the value of the offending +chain, providing economic incentives for any normal chain not to run malicious +applications over IBC. diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index 9c07d83691..266fc92024 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -2,38 +2,109 @@ ([Back to table of contents](specification.md#contents)) -The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. +The IBC protocol creates a mechanism by which multiple sovereign replicated +fault tolerant state machines may pass messages to each other. These messages +provide a base layer for the creation of communicating the blockchain +architecture that overcomes challenges in the scalability and extensibility of +computing blockchain environments. -The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by StormMQ, RabbitMQ, etc. +The IBC protocol assumes that multiple applications are running on their own +blockchain with their own state and logic. Communication is achieved over a +secure message queue protocol, allowing the creation of complex inter-chain +processes without trusted parties. This architecture can be seen as a parallel +to microservices in the blockchain space, and the IBC protocol can be seen as +an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by +StormMQ, RabbitMQ, etc. -The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. +The message packets are not signed by one pseudonymous account, or even +multiple. Rather, IBC effectively assigns authorization of the packets to the +blockchain's consensus algorithm itself. Not only are blockchains highly +secure, they are auditable and have an extremely high creation cost in +comparison to cryptographic key pairs. This prevents sybil attacks and allows +out-of-protocol accountability, since any byzantine behavior is provable and +can be published to damage the reputation/value of the other blockchain. By +using registered blockchains as "actors" in the system, we can achieve +extremely high security through a combination of cryptography and incentives. -In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. +In this paper, we define a process of posting block headers and merkle proofs +to enable secure verification of individual packets. We then describe how to +combine these packets into a messaging queue to guarantee reliable, in-order +delivery of messages. We then explain how to securely handle receipts +(response/error), which enables the creation of asynchronous RPC-like +protocols. Finally, we detail some optimizations and how to handle byzantine +blockchains. ### 1.1 Definitions -_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. +_Blockchain_ - an immutable ledger created through distributed consensus, +coupled with a deterministic state machine to process the transactions on the +ledger. The smallest unit produced through consensus is a block, which may +contain many transactions. -_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. +_Module_ - we assume that the state machine of the blockchain is comprised of +multiple components (modules or smart contracts) that have limited rights, and +that they can only interact over pre-defined interfaces rather than directly +mutating internal state. -_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. +_Finality_ - a guarantee that a given block will not be reverted within some +predefined conditions. All proof of work systems offer probabilistic finality, +which means the probability of that a block will be reverted approaches 0. A +"better", alternative chain could exist, but the cost of creation increases +rapidly over time. Many proof of stake systems offer much weaker guarantees, +based only on the honesty of the validators. However, BFT algorithms such as +Tendermint guarantee complete finality upon production of a block, unless over +two thirds of the validators collude to break consensus. This collusion is +provable and can be punished. _Knowledge_ - what is certain to be true. -_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. +_Provable_ - the existence of irrefutable mathematical (often cryptographic) +proof of the truth of a given statement. These can be expressed as: +given knowledge **A** and a statement **s**, then **B** must be true. +This is a form of deductive proof and they can be chained together without +losing validity. -_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. +_Attributable_ - provable knowledge of who made a statement. If a statement is +provably false, then it is known which actor lied. Attributable statements +allow us to build incentives against lying. This is also referred to as +accountability. -_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. +_Root of Trust_ - any proof depends on some prior assumptions, however simple +they are. We refer to the first assumption we make as the root of trust, and +all our knowledge of the system is derived from this root through a provable +chain of information. We seek to make this root of trust as simple and +verifiable as possible, since if the original assignment of trust is false, all +conclusions drawn will also be false. -_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. +_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for +some time to provide a lower bound for the length of a long-range +attack [[3](./footnotes.md#3)]. Since complete finality is associated with a +subset of the proof of stake class of consensus algorithms, I will assume all +implementations that support IBC have some unbonding period P, such that if my +last knowledge of the blockchain is older than P, I can no longer trust any +message without a new root of trust. -The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. +The IBC protocol requires each actor to be a blockchain with deterministic +finality. All transitions must be provable and attributable to (at least) one +actor. That implies the smallest unit of trust is the consensus algorithm of a +blockchain. ### 1.2 Threat Models -_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. +_False statements_ - any information we receive may be false, all actors must +have enough knowledge be able to prove its correctness without external +dependencies. All statements should be attributable. -_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. +_Network partitions and delays_ - we assume an asynchronous, adversarial +network. Any message may or may not reach the destination. They may be modified +or selectively dropped. Messages may reach the destination out of order and may +arrive multiple times. There is no upper limit to the time it takes for a +message to be received. Actors may be arbitrarily partitioned by an adversary. +The protocol favors correctness over liveness. That is, it only acts upon +information that is provably correct. -_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. +_Byzantine actors_ - it is possible that an entire blockchain is not acting +according to protocol. This must be detectable and provable, allowing the +communicating blockchain to revoke trust and take necessary action. +Furthermore, we should design application-level protocols on top of IBC to +minimize the risk exposure in the face of Byzantine actors. diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md index c402eae393..e13f2bb458 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/proofs.md @@ -2,47 +2,98 @@ ([Back to table of contents](specification.md#contents)) -The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh_ is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. +The basis of IBC is the ability to perform efficient proofs of a message packet +on-chain and deterministically. All transactions must be attributable and +provable without depending on any information outside of the blockchain. We +define the following variables: _Hh_ is the signed header at height +_h_, _Ch_ are the consensus rules at height _h_, and _P_ is the +unbonding period of this blockchain. _Vk,h_ is the value stored +under key _k_ at height _h_. Note that out of all of these, only +_Hh_ defines a signature and is thus attributable. -To support an IBC connection, two actors must be able to make the following proofs to each other: +To support an IBC connection, two actors must be able to make the following +proofs to each other: -* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ -* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ -* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ +* given a trusted _Hh_ and _Ch_ and an attributable + update message _Uh'_ it is possible to prove _Hh'_ + where _Ch' = Ch_ and Δ_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable + change message _Xh'_ it is possible to prove _Hh'_ + where _Ch'_ ≠ _Ch_ and + Δ _(now, Hh) < P_ +* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is + possible to prove _Vk,h_ -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. +It is possible to make use of the structure of BFT consensus to construct +extremely lightweight and provable messages _Uh'_ and +_Xh'_. The implementation of these requirements with Tendermint is +defined in Appendix E. Another engine that is able to provide equally strong +guarantees (such as Casper) is compatible with IBC, and must define its own set +of update/change messages. -The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. +The merkle proof _Mk,v,h_ is a well-defined concept in the +blockchain space, and provides a compact proof that the key value pair (_k, v)_ +is consistent with a merkle root stored in _Hh_. Handling the case +where _k_ is not in the store requires a separate proof of non-existence, which +is not supported by all merkle stores. Thus, we define the proof only as a +proof of existence. There is no valid proof for missing keys, and we design the +algorithm to work without it. _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ ### 2.1 Establishing a Root of Trust -As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. +As mentioned in the definitions, all proofs are based on an original +assumption. In this case it is _Hh_ and _Ch_ for some +_h_, where Δ_(now, Hh) < P_. -Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. +Any header may be from a malicious chain (eg. shadowing a real chain id with a +fake validator set), so a subjective decision is required before establishing a +connection. This should be performed by on-chain governance to avoid an +exploitable position of trust. Establishing a bidirectional root of trust +between two blockchains (A trusts B and B trusts A) is a necessary and +sufficient prerequisite for all other IBC activity. -Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. +Development of a fully open and decentralized PKI for tracking blockchains is +an open research question for future iterations of the IBC protocol. ### 2.2 Following Block Headers -We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation +We define two messages _Uh_ and _Xh_, which together +allow us to securely advance our trust from some known _Hn_ to a +future _Hh_ where _h > n_. Some implementations may provide the +additional limitation that _h = n + 1_, which requires us to process every +header. Tendermint allows us to exploit knowledge of the BFT algorithm to only +require the additional limitation +Δ_vals(Cn, Ch ) < ⅓_, that each step must +have a change of less than one-third of the validator +set[[4](./footnotes.md#4)]. -Δ_vals(Cn, Ch ) < ⅓_, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. +Any of these requirements allows us to support IBC for the given block chain. +However, by supporting proofs where _h_-_n > 1_, we can follow the block +headers much more efficiently in situations where the majority of blocks do not +include an IBC message between chains A and B, and enable low-bandwidth +connections to be implemented at very low cost. If there are messages to relay +every block, then these collapse to the same case, relaying every header. -Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. - -Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. - -More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: +Since these messages _Uh_ and _Xh_ provide all knowledge +of the remote blockchain, we require that they not just be provable, but also +attributable. As such any attempt to violate the finality guarantees or provide +fake proof can be submitted to the remote blockchain for punishment, in the +same manner that any violation of the internal consensus algorithm is punished. +This incentive enhances the security guarantees and avoids the nothing-at-stake +issue in IBC as well. +More formally, given existing set of trust +_T_ = _{(Hi , Ci ), (Hj , Cj ), …}_ +we must provide: _valid(T, Xh | Uh )_ ⇒ _[true | false | unknown]_ _if Hh-1_ ∈ _T then_: * _valid(T, Xh | Uh )_ ⇒ _[true | false]_ * _there must exist some Uh or Xh that evaluates to true_ -_if Ch_ ∉ _T then_ +_if Ch_ ∉ _T then_: * _valid(T, Uh )_ ⇒ _false_ and can process update transactions as follows: @@ -53,6 +104,17 @@ _ match valid(T, Xh | Uh )_ * _unknown_ ⇒ _return Error("need a proof between current and h")_ * _true_ ⇒ _T_ ∪ _(Hh ,Ch )_ -We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. +We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ +with _max(T) = h-1_. And from above, there must exist some +_Xh | Uh_ so that +_max(update(T, Xh | Uh )) = h_. By induction, +we can see there must exist a set of proofs, such that +_max(update…(T,...)) = h+n_ for any n. -We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. +We also can see the validity of using bisection as an optimization to discover +this set of proofs. That is, given _max(T) = n_ and +_valid(T, Xh | Uh ) = unknown_, we then try +_update(T, Xb | Ub )_, where _b = (h+n)/2_. +The base case is where +_valid(T, Xh | Uh ) = true_ and is guaranteed +to exist if _h=max(T)+1_. diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index b0f459c40a..4f46fb6697 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -2,41 +2,72 @@ ([Back to table of contents](specification.md#contents)) -Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. +Messaging in distributed systems is a deeply researched field and a primitive +upon which many other systems are built. We can model asynchronous message +passing, and make no timing assumptions on the communication channels. By doing +this, we allow each zone to move at its own speed, unblocked by any other zone, +but able to communicate as fast as the network allows at that moment. -Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. +Another benefit of using message passing as our primitive, is that the receiver +decides how to act upon the incoming message. Just because one zone sends a +message and we have an IBC connection with this zone, doesn't mean we have to +execute the requested action. Each zone can run its own business logic upon +receiving the message to decide whether to accept or reject the message. To +maintain consistency, both sides must only agree on the proper state +transitions associated with accepting or rejecting. -This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. +This encapsulation is very difficult to impossible to achieve in a shared-state +scenario. Message passing allows each zone to ensure its security and autonomy, +while simultaneously allowing the different systems to work as one whole. This +can be seen as an analogue to a micro-services architecture, but across +organizational boundaries. -To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[[5](./footnotes.md#5)], and avoid blocking. +To build useful algorithms upon a provable asynchronous messaging primitive, we +introduce a reliable messaging queue (hereafter just referred to as a queue), +typical in asynchronous message passing, to allow us to guarantee a causal +ordering[[5](./footnotes.md#5)], and avoid blocking. -Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). +Causal ordering means that if _x_ is causally before _y_ on chain A, it must +also be on chain B. Many events may happen concurrently (unrelated tx on two +different blockchains) with no causality relation, but every transaction on the +same chain has a clear causality relation (same as the order in the +blockchain). -Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: - -_A:send(msgi )_ → _B:receive(msgi )_ - -_B:receive(msgi )_ → _A:receipt(msgi )_ - -_A:send(msgi )_ → _A:send(msgi+1 )_ - -_x_ → _A:send(msgi )_ ⇒ -_x_ → _B:receive(msgi )_ - -_y_ → _B:receive(msgi )_ ⇒ -_y_ → _A:receipt(msgi )_ +Message passing implies a causal ordering over multiple chains and these can be +important for reasoning on the system. Given _x_ → _y_ means _x_ is +causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies +_b_: +* _A:send(msgi )_ → _B:receive(msgi )_ +* _B:receive(msgi )_ → _A:receipt(msgi )_ +* _A:send(msgi )_ → _A:send(msgi+1 )_ +* _x_ → _A:send(msgi )_ ⇒ +* _x_ → _B:receive(msgi )_ +* _y_ → _B:receive(msgi )_ ⇒ +* _y_ → _A:receipt(msgi )_ ![Vector Clock image](https://upload.wikimedia.org/wikipedia/commons/5/55/Vector_Clock.svg) -([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) +![](https://en.wikipedia.org/wiki/Vector_clock) + +In this section, we define an efficient implementation of a secure, reliable +messaging queue. -In this section, we define an efficient implementation of a secure, reliable messaging queue. ### 3.1 Merkle Proofs for Queues -Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. +Given the three proofs we have available, we make use of the most flexible one, +_Mk,v,h_, to provide proofs for a message queue. To do so, we must +define a unique, deterministic, and predictable key in the merkle store for +each message in the queue. We also define a clearly defined format for the +content of each message in the queue, which can be parsed by all chains +participating in IBC. The key format and queue ordering are conceptually +explained here. The binary encoding format can be found in Appendix C. -We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: +We can visualize a queue as a slice pointing into an infinite sized array. It +maintains a head and a tail pointing to two indexes, such that there is data +for every index where _head <= index < tail_. Data is pushed to the tail and +popped from the head. Another method, _advance_, is introduced to pop all +messages until _i_, and is useful for cleanup: **init**: _qhead = qtail = 0_ @@ -52,32 +83,68 @@ We can visualize a queue as a slice pointing into an infinite sized array. It ma **tail** ⇒ **i**: _qtail_ -Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. +Based upon this needed functionality, we define a set of keys to be stored in +the merkle tree, which allows us to efficiently implement and prove any of the +above queries. **Key:** _(queue name, [head|tail|index])_ -The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. +The index is stored as a fixed-length unsigned integer in big endian format, so +that the lexicographical order of the byte representation of the key is +consistent with their sequence number. This allows us to quickly iterate over +the queue, as well as prove the content of a packet (or lack of packet) at a +given sequence. _head_ and _tail_ are two special constants that store an +integer index, and are chosen such that their serialization cannot collide with +any possible index. -A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. This property is essential to safely process asynchronous messages. +A message queue is simply a set of serialized packets stored at predefined keys +in a merkle store, which can produce proofs for any key. Once a packet is +written it must be immutable (except for deleting when popped from the queue). +That is, if a value _v_ is written to a queue, then every valid proof +_Mk,v,h _ must refer to the same _v_. This property is essential to +safely process asynchronous messages. -Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. +Every IBC implementation must provide a protected subspace of the merkle store +for use by each queue that cannot be affected by other modules. ### 3.2 Naming Queues -As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. +As mentioned above, in order for the receiver to unambiguously interpret the +merkle proofs, we need a unique, deterministic, and predictable key in the +merkle store for each message in the queue. We explained how the indexes are +generated to provide each message in a queue a unique key, and mentioned the +need for a unique name for each queue. -The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). +The queue name must be unambiguously associated with a given connection to +another chain, so an observer can prove if a message was intended for chain A +or chain B. In order to do so, upon registration of a connection with a remote +chain, we create two queues with different names (prefixes). * _ibc::send_ - all outgoing packets destined to chain A * _ibc::receipt_ - the results of executing the packets received from chain A -These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. +These two queues have different purposes and store messages of different types. +By parsing the key of a merkle proof, a recipient can uniquely identify which +queue, if any, this message belongs to. We now define +_k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and +verify every message, before the contents of the packet are processed by the +appropriate application logic. ### 3.3 Message Contents -Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. +Up to this point, we have focused on the semantics of the message key, and how +we can produce a unique identifier for every possible message in every possible +connection. The actual data written at the location has been left as an opaque +blob, but by providing some structure to the messages, we can enable more +functionality. -We define every message in a _send queue_ to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) +We define every message in a _send queue_ to consist of a well-known type and +opaque data. The IBC protocol relies on the type for routing, and lets the +appropriate module process the data as it sees fit. The _receipt queue_ stores +if it was an error, an optional error code, and an optional return value. We +use the same index as the received message, so that the results of +_A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: +the message at index _i_ in the _send_ queue for chain B as stored on chain A) _Vsend = (type, data)_ @@ -85,16 +152,32 @@ _Vreceipt = (result, [success|error code])_ ### 3.4 Sending a Message -A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. +A proper implementation of IBC requires all relevant state to be encapsulated, +so that other modules can only interact with it via a fixed API (to be defined +in the next sections) rather than directly mutating internal state. This allows +the IBC module to provide security guarantees. -Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send queue_, which enables all the proofs described above. +Sending an IBC packet involves an application module calling the send method of +the IBC module with a packet and a destination chain id. The IBC module must +ensure that the destination chain was already properly registered, and that the +calling module has permission to write this packet. If so, the IBC module +simply pushes the packet to the tail of the _send queue_, which enables all the +proofs described above. -The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. +The permissioning of which module can write which packet can be defined per +type, so this module can maintain any application-level invariants related to +this area. Thus, the "coin" module can maintain the constant supply of tokens, +while another module can maintain its own invariants, without IBC messages +providing a means to escape their encapsulations. The IBC module must associate +every supported message type with a particular handler (_ftype_) and +return an error for unsupported types. _(IBCsend(D, type, data)_ ⇒ _Success)_ ⇒ _push(qD.send ,Vsend{type, data})_ -We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: +We also consider how a given blockchain _A_ is expected to receive the packet +from a source chain _S_ with a merkle proof, given the current set of trusted +headers for that chain, _TS_: _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ * _qS.receipt =_ ∅ ⇒ _Error("unregistered sender"),_ @@ -105,13 +188,30 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ * _valid(Hh ,Mk,v,h ) = false_ ⇒ _Error("invalid merkle proof"),_ * _v = (type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err)); Success_ -Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). +Note that this requires not only a valid proof, but also that the proper header +as well as all prior messages were previously submitted. This returns success +upon accepting a proper message, even if the message execution returned an +error (which must then be relayed to the sender). ### 3.5 Receipts -When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. +When we wish to create a transaction that atomically commits or rolls back +across two chains, we must look at the receipts from sending the original +message. For example, if I want to send tokens from Alice on chain A to Bob on +chain B, chain A must decrement Alice's account _if and only if_ Bob's account +was incremented on chain B. We can achieve that by storing a protected +intermediate state on chain A, which is then committed or rolled back based on +the result of executing the transaction on chain B. -To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: +To do this requires that we not only provable send a message from chain A to +chain B, but provably return the result of that message (the receipt) from +chain B to chain A. As one noticed above in the implementation of _IBCreceive_, +if the valid IBC message was sent from A to B, then the result of executing it, +even if it was an error, is stored in _B:qA.receipt_. Since the +receipts are stored in a queue with the same key construction as the sending +queue, we can generate the same set of proofs for them, and perform a similar +sequence of steps to handle a receipt coming back to _S_ for a message +previously sent to _A_: _S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ @@ -124,7 +224,10 @@ _S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ * _v = (\_, error)_ ⇒ _(type, data) := pop(qS.send ); rollbacktype(data); Success_ * _v = (res, success)_ ⇒ _(type, data) := pop(qS.send ); committype(data, res); Success_ -This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. +This enforces that the receipts are processed in order, to allow some the +applications to make use of some basic assumptions about ordering. It also +removes the message from the send queue, as there is now proof it was processed +on the receiving chain and there is no more need to store this information. ![Successful Transaction](images/Receipts.png) @@ -133,13 +236,26 @@ This enforces that the receipts are processed in order, to allow some the applic ### 3.6 Relay Process -The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. +The blockchain itself only records the _intention_ to send the given message to +the recipient chain, it doesn't make any network connections as that would add +unbounded delays and non-determinism into the state machine. We define the +concept of a _relay_ process that connects two chain by querying one for all +proofs needed to prove outgoing messages and submit these proofs to the +recipient chain. -The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. +The relay process must have access to accounts on both chains with sufficient +balance to pay for transaction fees but needs no other permissions. Many +_relay_ processes may run in parallel without violating any safety +consideration. However, they will consume unnecessary fees if they submit the +same proof multiple times, so some minimal coordination is ideal. -As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. +As an example, here is a naive algorithm for relaying send messages from A to +B, without error handling. We must also concurrently run the relay of receipts +from B back to A, in order to complete the cycle. Note that all reads of +variables belonging to a chain imply queries and all function calls imply +submitting a transaction to the blockchain. -``` +```go while true pending := tail(A:qB.send) received := tail(B:qA.receive) @@ -153,8 +269,19 @@ while true sleep(desiredLatency) ``` -Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. +Note that updating a header is a costly transaction compared to posting a +merkle proof for a known header. Thus, a process could wait until many messages +are pending, then submit one header along with multiple merkle proofs, rather +than a separate header for each message. This decreases total computation cost +(and fees) at the price of additional latency and is a trade-off each relay can +dynamically adjust. -In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. +In the presence of multiple concurrent relays, any given relay can perform +local optimizations to minimize the number of headers it submits, but remember +the frequency of header submissions defines the latency of the packet transfer. -Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). +Indeed, it is ideal if each user that initiates the creation of an IBC packet +also relays it to the recipient chain. The only constraint is that the relay +must be able to pay the appropriate fees on the destination chain. However, in +order to avoid bottlenecks, a group may sponsor an account to pay fees for a +public relayer that moves all unrelayed packets (perhaps with a high latency). diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md index 5864337df0..9d7b302a8b 100644 --- a/docs/spec/ibc/specification.md +++ b/docs/spec/ibc/specification.md @@ -6,14 +6,32 @@ _v0.4.0 / Feb. 13, 2018_ ## Abstract -This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. +This paper specifies the IBC (inter blockchain communication) protocol, which +was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June +2016. The IBC protocol uses authenticated message passing to simultaneously +solve two problems: +* transferring value (and state) between two distinct chains +* sharding one chain securely. +IBC follows the message-passing paradigm and assumes that the participating +chains are independent. -Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. +Each chain maintains a local partial order, while inter-chain messages track +any cross-chain causality relations. Once two chains have registered a trust +relationship, cryptographically provable packets can be securely sent between +the chains, due to Tendermint's instant finality property. -We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value ar -e provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. +We currently use this protocol for secure value transfer in the Cosmos Hub, but +the protocol can support arbitrary application logic. Designing secure +communication logic for other types of applications is still an area of active +research. + +The protocol makes no assumptions about block times or network delays in the +transmission of the packets between chains and requires cryptographic proofs +for every message, and thus is highly robust in a heterogeneous environment +with Byzantine actors. This paper explains the requirements and structure of +the Cosmos IBC protocol. It aims to provide enough detail to fully understand +and analyze the security of the protocol. -The protocol makes no assumptions of block times or network delays in the transmission of the packets between chains and requires cryptographic proofs for every message, and thus is highly robust in a heterogeneous environment with Byzantine actors. This paper explains the requirements and structure of the Cosmos IBC protocol. It aims to provide enough detail to fully understand and analyze the security of the protocol. ## Contents From a8d3b3ef19e38db63f41c185f72f684c8271f77d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 26 Feb 2018 13:17:42 +0100 Subject: [PATCH 11/32] Correct spelling This reverts commit 3c7d194a7f1bc53b60cf09473f0ac50d2192e2af. --- docs/spec/ibc/conclusion.md | 20 +-- docs/spec/ibc/optimizations.md | 188 ++++++---------------------- docs/spec/ibc/overview.md | 101 +++------------ docs/spec/ibc/proofs.md | 102 +++------------ docs/spec/ibc/queues.md | 219 +++++++-------------------------- docs/spec/ibc/specification.md | 28 +---- 6 files changed, 128 insertions(+), 530 deletions(-) diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md index db5833ee1c..37d555e6c4 100644 --- a/docs/spec/ibc/conclusion.md +++ b/docs/spec/ibc/conclusion.md @@ -1,21 +1,7 @@ ## 5 Conclusion -We have demonstrated a secure, performant, and flexible protocol for connecting -two blockchains with complete finality using a secure, reliable messaging -queue. The algorithm and semantics of all data types have been defined above, -which provides a solid basis for reasoning about correctness and efficiency of -the algorithm. +We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. -The observant reader may note that while we have defined a message queue -protocol, we have not yet defined how to use that to transfer value within the -Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that -defines the application logic used for direct value transfer as well as routing -over the Cosmos hub. That paper builds upon the IBC protocol defined here and -provides a first example of how to reason about application logic and global -invariants in the context of IBC. +The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. -There is a reference implementation of the Cosmos IBC protocol as part of the -Cosmos SDK, written in go and freely usable under the Apache license. For those -wish to write an implementation of IBC in another language, or who want to -analyze the specification further, the following appendixes define the exact -message formats and binary encoding. +There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index ef6259a0e1..e6c2f8cbcf 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -2,214 +2,104 @@ ([Back to table of contents](specification.md#contents)) -The above sections describe a secure messaging protocol that can handle all -normal situations between two blockchains. It guarantees that all messages are -processed exactly once and in order, and provides a mechanism for non-blocking -atomic transactions spanning two blockchains. However, to increase efficiency -over millions of messages with many possible failure modes on both sides of the -connection, we can extend the protocol. These extensions allow us to clean up -the receipt queue to avoid state bloat, as well as more gracefully recover from -cases where large numbers of messages are not being relayed, or other failure -modes in the remote chain. +The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. ### 4.1 Timeouts -Sometimes it is desirable to have some timeout, an upper limit to how long you -will wait for a transaction to be processed before considering it an error. At -the same time, this is an obvious attack vector for a double spend, just -delaying the relay of the receipt or waiting to send the message in the first -place and then relaying it right after the cutoff to take advantage of -different local clocks on the two chains. +Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. -One solution to this is to include a timeout in the IBC message itself. When -sending it, one can specify a block height or timestamp on the **receiving** -chain after which it is no longer valid. If the message is posted before the -cutoff, it will be processed normally. If it is posted after that cutoff, it -will be a guaranteed error. Note that to make this secure, the timeout must be -relative to a condition on the **receiving** chain, and the sending chain must -have proof of the state of the receiving chain after the cutoff. +One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. -For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for -_A:qB.send_ or _B:qA.receipt_ we currently have the -following guarantees: +For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: _A:Mk,v,h =_ ∅ _if message i was not sent before height h_ -_A:Mk,v,h =_ ∅ _if message i was sent and receipt received -before height h (and the receipts for all messages j < i were also handled)_ +_A:Mk,v,h =_ ∅ _if message i was sent and receipt received before height h (and the receipts for all messages j < i were also handled)_ -_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet -processed)_ +_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet processed)_ _B:Mk,v,h =_ ∅ _if message i was not received before height h_ -_B:Mk,v,h _ ≠ ∅ _if message i was received before height -h (and all messages j < i were received)_ +_B:Mk,v,h _ ≠ ∅ _if message i was received before height h (and all messages j < i were received)_ -Based on these guarantees, we can make a few modifications of the above -protocol to allow us to prove timeouts, by adding some fields to the messages -in the send queue, and defining an expired function that returns true iff -_h > maxHeight_ or _timestamp(Hh ) > maxTime_. +Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime_. _Vsend = (maxHeight, maxTime, type, data)_ _expired(Hh ,Vsend )_ ⇒ _[true|false]_ -We then update message handling in _IBCreceive_, so it doesn't even call the -handler function if the timeout was reached, but rather directly writes and -error in the receipt queue: +We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: _IBCreceive:_ + * …. * _expired(latestHeader, v)_ ⇒ _push(qS.receipt , (None, TimeoutError)),_ * _v = (\_, \_, type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err));_ -and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that -the message was not processed at some given header on the recipient chain. This -allows the sender chain to assert timeouts locally. +and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. + _S:IBCtimeout(A, Mk,v,h)_ ⇒ _match_ * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ * _k = (\_, send, \_)_ ⇒ _Error("must be a receipt"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different - chain"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header - for height h"),_ - * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid - merkle proof"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ + * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ * _k = (S, receipt, tail)_ ⇒ _match_ - * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt - exists, no timeout proof")_ - * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout - not yet reached")_ - * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); - rollbacktype(data); Success_ + * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt exists, no timeout proof")_ + * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout not yet reached")_ + * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); rollbacktype(data); Success_ * _default_ ⇒ _Error("must be a tail proof")_ which processes timeouts in order, and adds one more condition to the queues: -_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven -before height h (and the receipts for all messages j < i were also handled)_ +_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven before height h (and the receipts for all messages j < i were also handled)_ -Now chain A can rollback all transactions that were blocked by this flood of -unrelayed messages, without waiting for chain B to process them and return a -receipt. Adding reasonable timeouts to all packets allows us to gracefully -handle any errors with the IBC relay processes, or a flood of unrelayed "spam" -IBC packets. If a blockchain requires a timeout on all messages, and imposes -some reasonable upper limit (or just assigns it automatically), we can -guarantee that if message _i_ is not processed by the upper limit of the -timeout period, then all previous messages must also have either been processed -or reached the timeout period. +Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. -Note that in order to avoid any possible "double-spend" attacks, the timeout -algorithm requires that the destination chain is running and reachable. One can -prove nothing in a complete network partition, and must wait to connect; the -timeout must be proven on the recipient chain, not simply the absence of a -response on the sending chain. +Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. ### 4.2 Clean up -While we clean up the _send queue_ upon getting a receipt, if left to run -indefinitely, the _receipt queues_ could grow without limit and create a major -storage requirement for the chains. However, we must not delete receipts until -they have been proven to be processed by the sending chain, or we lose -important information and sacrifice reliability. +While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. -The observant reader may also notice, that when we perform the timeout on the -sending chain, we do not update the _receipt queue_ on the receiving chain, and -now it is blocked waiting for a message _i_, which **no longer exists** on the -sending chain. We can update the guarantees of the receipt queue as follows to -allow us to handle both: +The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: _B:Mk,v,h =_ ∅ _if message i was not received before height h_ -_B:Mk,v,h =_ ∅ _if message i was provably resolved on the -sending chain before height h_ +_B:Mk,v,h =_ ∅ _if message i was provably resolved on the sending chain before height h_ -_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed -before height h, and no ack of receipt from the sending chain)_ +_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed before height h, and no ack of receipt from the sending chain)_ -Consider a connection where many messages have been sent, and their receipts -processed on the sending chain, either explicitly or through a timeout. We wish -to quickly advance over all the processed messages, either for a normal -cleanup, or to prepare the queue for normal use again after timeouts. +Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. -Through the definition of the send queue above, we see that all messages -_i < head_ have been fully processed, and all messages _head <= i < tail_ are -awaiting processing. By proving a much advanced _head_ of the _send queue_, we -can demonstrate that the sending chain already handled all messages. Thus, we -can safely advance our local _receipt queue_ to the new head of the remote -_send queue_. +Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. _S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ * _qA.receipt =_ ∅ ⇒ _Error("unknown sender"),_ * _k = (\_, send, \_)_ ⇒ _Error("must be for the send queue"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different - chain"),_ - * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of - the queue"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header - for height h"),_ - * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid - merkle proof"),_ + * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ + * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of the queue"),_ + * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ + * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ * _head := v_ ⇒ _match_ - * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go - forward"),_ + * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go forward"),_ * _default_ ⇒ _advance(qA.receipt , head); Success_ -This allows us to invoke the _IBCcleanup_ function to resolve all outstanding -messages up to and including _head_ with one merkle proof. Note that this -handles both recovering from a blocked queue after timeouts, as well as a -routine cleanup method to recover space. In the cleanup scenario, we assume -that there may also be a number of messages that have been processed by the -receiving chain, but not yet posted to the sending chain, -_tail(B:qA.reciept ) > head(A:qB.send )_. As such, the -_advance_ function must not modify any messages between the head and the tail. +This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. ![Cleaning up Packets](images/CleanUp.png) ### 4.3 Handling Byzantine Failures -While every message is guaranteed reliable in the face of malicious nodes or -relays, all guarantees break down when the entire blockchain on the other end -of the connection exhibits byzantine faults. These can be in two forms: +While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). -* failures of the consensus mechanism (reversing "final" blocks) -* failure at the application level (not performing the action defined by the - message). +The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. -The IBC protocol can only detect byzantine faults at the consensus level, and -is designed to halt with an error upon detecting any such fault. That is, if it -ever sees two different headers for the same height (or any evidence that -headers belong to different forks), then it must freeze the connection -immediately. The resolution of the fault must be handled by the blockchain -governance, as this is a serious incident and cannot be predefined. +If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). -If there is a big divide in the remote chain and they split eg. 60-40 as to the -direction of the chain, then the light-client protocol will refuses to follow -either fork. If both sides declare a hard fork and continue with new validator -sets that are not compatible with the consensus engine (they don't have ⅔ -support from the previous block), then users will have to manually tell their -local client which chain to follow (or fork and follow both with different IDs). +The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). -The IBC protocol doesn't have the option to follow both chains as the queue and -associated state must map to exactly one remote chain. In a fork, the chain can -continue the connection with one fork, and optionally make a fresh connection -with the other fork (which will also have to adjust internally to wipe its view -of the connection clean). +The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? -The other major byzantine action is at the application level. Let us assume -messages represent transfer of value. If chain A sends a message with X tokens -to chain B, then it promises to remove X tokens from the local supply. And if -chain B handles this message with a success code, it promises to credit X -tokens to the account mentioned in the message. What if A isn't actually -removing tokens from the supply, or if B is not actually crediting accounts? - -Such application level issues cannot be proven in a generic sense, but must be -handled individually by each application. The activity should be provable in -some manner (as it is all in an auditable blockchain), but there are too many -failure modes to attempt to enumerate, so we rely on the vigilance of the -participants in the extremely rare case of a rogue blockchain. Of course, this -misbehavior is provable and can negatively impact the value of the offending -chain, providing economic incentives for any normal chain not to run malicious -applications over IBC. +Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index 266fc92024..9c07d83691 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -2,109 +2,38 @@ ([Back to table of contents](specification.md#contents)) -The IBC protocol creates a mechanism by which multiple sovereign replicated -fault tolerant state machines may pass messages to each other. These messages -provide a base layer for the creation of communicating the blockchain -architecture that overcomes challenges in the scalability and extensibility of -computing blockchain environments. +The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. -The IBC protocol assumes that multiple applications are running on their own -blockchain with their own state and logic. Communication is achieved over a -secure message queue protocol, allowing the creation of complex inter-chain -processes without trusted parties. This architecture can be seen as a parallel -to microservices in the blockchain space, and the IBC protocol can be seen as -an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by -StormMQ, RabbitMQ, etc. +The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by StormMQ, RabbitMQ, etc. -The message packets are not signed by one pseudonymous account, or even -multiple. Rather, IBC effectively assigns authorization of the packets to the -blockchain's consensus algorithm itself. Not only are blockchains highly -secure, they are auditable and have an extremely high creation cost in -comparison to cryptographic key pairs. This prevents sybil attacks and allows -out-of-protocol accountability, since any byzantine behavior is provable and -can be published to damage the reputation/value of the other blockchain. By -using registered blockchains as "actors" in the system, we can achieve -extremely high security through a combination of cryptography and incentives. +The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. -In this paper, we define a process of posting block headers and merkle proofs -to enable secure verification of individual packets. We then describe how to -combine these packets into a messaging queue to guarantee reliable, in-order -delivery of messages. We then explain how to securely handle receipts -(response/error), which enables the creation of asynchronous RPC-like -protocols. Finally, we detail some optimizations and how to handle byzantine -blockchains. +In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. ### 1.1 Definitions -_Blockchain_ - an immutable ledger created through distributed consensus, -coupled with a deterministic state machine to process the transactions on the -ledger. The smallest unit produced through consensus is a block, which may -contain many transactions. +_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. -_Module_ - we assume that the state machine of the blockchain is comprised of -multiple components (modules or smart contracts) that have limited rights, and -that they can only interact over pre-defined interfaces rather than directly -mutating internal state. +_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. -_Finality_ - a guarantee that a given block will not be reverted within some -predefined conditions. All proof of work systems offer probabilistic finality, -which means the probability of that a block will be reverted approaches 0. A -"better", alternative chain could exist, but the cost of creation increases -rapidly over time. Many proof of stake systems offer much weaker guarantees, -based only on the honesty of the validators. However, BFT algorithms such as -Tendermint guarantee complete finality upon production of a block, unless over -two thirds of the validators collude to break consensus. This collusion is -provable and can be punished. +_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. _Knowledge_ - what is certain to be true. -_Provable_ - the existence of irrefutable mathematical (often cryptographic) -proof of the truth of a given statement. These can be expressed as: -given knowledge **A** and a statement **s**, then **B** must be true. -This is a form of deductive proof and they can be chained together without -losing validity. +_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. -_Attributable_ - provable knowledge of who made a statement. If a statement is -provably false, then it is known which actor lied. Attributable statements -allow us to build incentives against lying. This is also referred to as -accountability. +_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. -_Root of Trust_ - any proof depends on some prior assumptions, however simple -they are. We refer to the first assumption we make as the root of trust, and -all our knowledge of the system is derived from this root through a provable -chain of information. We seek to make this root of trust as simple and -verifiable as possible, since if the original assignment of trust is false, all -conclusions drawn will also be false. +_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. -_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for -some time to provide a lower bound for the length of a long-range -attack [[3](./footnotes.md#3)]. Since complete finality is associated with a -subset of the proof of stake class of consensus algorithms, I will assume all -implementations that support IBC have some unbonding period P, such that if my -last knowledge of the blockchain is older than P, I can no longer trust any -message without a new root of trust. +_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. -The IBC protocol requires each actor to be a blockchain with deterministic -finality. All transitions must be provable and attributable to (at least) one -actor. That implies the smallest unit of trust is the consensus algorithm of a -blockchain. +The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. ### 1.2 Threat Models -_False statements_ - any information we receive may be false, all actors must -have enough knowledge be able to prove its correctness without external -dependencies. All statements should be attributable. +_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. -_Network partitions and delays_ - we assume an asynchronous, adversarial -network. Any message may or may not reach the destination. They may be modified -or selectively dropped. Messages may reach the destination out of order and may -arrive multiple times. There is no upper limit to the time it takes for a -message to be received. Actors may be arbitrarily partitioned by an adversary. -The protocol favors correctness over liveness. That is, it only acts upon -information that is provably correct. +_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. -_Byzantine actors_ - it is possible that an entire blockchain is not acting -according to protocol. This must be detectable and provable, allowing the -communicating blockchain to revoke trust and take necessary action. -Furthermore, we should design application-level protocols on top of IBC to -minimize the risk exposure in the face of Byzantine actors. +_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md index e13f2bb458..c402eae393 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/proofs.md @@ -2,98 +2,47 @@ ([Back to table of contents](specification.md#contents)) -The basis of IBC is the ability to perform efficient proofs of a message packet -on-chain and deterministically. All transactions must be attributable and -provable without depending on any information outside of the blockchain. We -define the following variables: _Hh_ is the signed header at height -_h_, _Ch_ are the consensus rules at height _h_, and _P_ is the -unbonding period of this blockchain. _Vk,h_ is the value stored -under key _k_ at height _h_. Note that out of all of these, only -_Hh_ defines a signature and is thus attributable. +The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh_ is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. -To support an IBC connection, two actors must be able to make the following -proofs to each other: +To support an IBC connection, two actors must be able to make the following proofs to each other: -* given a trusted _Hh_ and _Ch_ and an attributable - update message _Uh'_ it is possible to prove _Hh'_ - where _Ch' = Ch_ and Δ_(now, Hh) < P_ -* given a trusted _Hh_ and _Ch_ and an attributable - change message _Xh'_ it is possible to prove _Hh'_ - where _Ch'_ ≠ _Ch_ and - Δ _(now, Hh) < P_ -* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is - possible to prove _Vk,h_ +* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ +* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ +* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ -It is possible to make use of the structure of BFT consensus to construct -extremely lightweight and provable messages _Uh'_ and -_Xh'_. The implementation of these requirements with Tendermint is -defined in Appendix E. Another engine that is able to provide equally strong -guarantees (such as Casper) is compatible with IBC, and must define its own set -of update/change messages. +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. -The merkle proof _Mk,v,h_ is a well-defined concept in the -blockchain space, and provides a compact proof that the key value pair (_k, v)_ -is consistent with a merkle root stored in _Hh_. Handling the case -where _k_ is not in the store requires a separate proof of non-existence, which -is not supported by all merkle stores. Thus, we define the proof only as a -proof of existence. There is no valid proof for missing keys, and we design the -algorithm to work without it. +The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ ### 2.1 Establishing a Root of Trust -As mentioned in the definitions, all proofs are based on an original -assumption. In this case it is _Hh_ and _Ch_ for some -_h_, where Δ_(now, Hh) < P_. +As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. -Any header may be from a malicious chain (eg. shadowing a real chain id with a -fake validator set), so a subjective decision is required before establishing a -connection. This should be performed by on-chain governance to avoid an -exploitable position of trust. Establishing a bidirectional root of trust -between two blockchains (A trusts B and B trusts A) is a necessary and -sufficient prerequisite for all other IBC activity. +Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. -Development of a fully open and decentralized PKI for tracking blockchains is -an open research question for future iterations of the IBC protocol. +Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. ### 2.2 Following Block Headers -We define two messages _Uh_ and _Xh_, which together -allow us to securely advance our trust from some known _Hn_ to a -future _Hh_ where _h > n_. Some implementations may provide the -additional limitation that _h = n + 1_, which requires us to process every -header. Tendermint allows us to exploit knowledge of the BFT algorithm to only -require the additional limitation -Δ_vals(Cn, Ch ) < ⅓_, that each step must -have a change of less than one-third of the validator -set[[4](./footnotes.md#4)]. +We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation -Any of these requirements allows us to support IBC for the given block chain. -However, by supporting proofs where _h_-_n > 1_, we can follow the block -headers much more efficiently in situations where the majority of blocks do not -include an IBC message between chains A and B, and enable low-bandwidth -connections to be implemented at very low cost. If there are messages to relay -every block, then these collapse to the same case, relaying every header. +Δ_vals(Cn, Ch ) < ⅓_, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. -Since these messages _Uh_ and _Xh_ provide all knowledge -of the remote blockchain, we require that they not just be provable, but also -attributable. As such any attempt to violate the finality guarantees or provide -fake proof can be submitted to the remote blockchain for punishment, in the -same manner that any violation of the internal consensus algorithm is punished. -This incentive enhances the security guarantees and avoids the nothing-at-stake -issue in IBC as well. +Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. + +Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. + +More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: -More formally, given existing set of trust -_T_ = _{(Hi , Ci ), (Hj , Cj ), …}_ -we must provide: _valid(T, Xh | Uh )_ ⇒ _[true | false | unknown]_ _if Hh-1_ ∈ _T then_: * _valid(T, Xh | Uh )_ ⇒ _[true | false]_ * _there must exist some Uh or Xh that evaluates to true_ -_if Ch_ ∉ _T then_: +_if Ch_ ∉ _T then_ * _valid(T, Uh )_ ⇒ _false_ and can process update transactions as follows: @@ -104,17 +53,6 @@ _ match valid(T, Xh | Uh )_ * _unknown_ ⇒ _return Error("need a proof between current and h")_ * _true_ ⇒ _T_ ∪ _(Hh ,Ch )_ -We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ -with _max(T) = h-1_. And from above, there must exist some -_Xh | Uh_ so that -_max(update(T, Xh | Uh )) = h_. By induction, -we can see there must exist a set of proofs, such that -_max(update…(T,...)) = h+n_ for any n. +We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. -We also can see the validity of using bisection as an optimization to discover -this set of proofs. That is, given _max(T) = n_ and -_valid(T, Xh | Uh ) = unknown_, we then try -_update(T, Xb | Ub )_, where _b = (h+n)/2_. -The base case is where -_valid(T, Xh | Uh ) = true_ and is guaranteed -to exist if _h=max(T)+1_. +We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index 4f46fb6697..b0f459c40a 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -2,72 +2,41 @@ ([Back to table of contents](specification.md#contents)) -Messaging in distributed systems is a deeply researched field and a primitive -upon which many other systems are built. We can model asynchronous message -passing, and make no timing assumptions on the communication channels. By doing -this, we allow each zone to move at its own speed, unblocked by any other zone, -but able to communicate as fast as the network allows at that moment. +Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. -Another benefit of using message passing as our primitive, is that the receiver -decides how to act upon the incoming message. Just because one zone sends a -message and we have an IBC connection with this zone, doesn't mean we have to -execute the requested action. Each zone can run its own business logic upon -receiving the message to decide whether to accept or reject the message. To -maintain consistency, both sides must only agree on the proper state -transitions associated with accepting or rejecting. +Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. -This encapsulation is very difficult to impossible to achieve in a shared-state -scenario. Message passing allows each zone to ensure its security and autonomy, -while simultaneously allowing the different systems to work as one whole. This -can be seen as an analogue to a micro-services architecture, but across -organizational boundaries. +This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. -To build useful algorithms upon a provable asynchronous messaging primitive, we -introduce a reliable messaging queue (hereafter just referred to as a queue), -typical in asynchronous message passing, to allow us to guarantee a causal -ordering[[5](./footnotes.md#5)], and avoid blocking. +To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[[5](./footnotes.md#5)], and avoid blocking. -Causal ordering means that if _x_ is causally before _y_ on chain A, it must -also be on chain B. Many events may happen concurrently (unrelated tx on two -different blockchains) with no causality relation, but every transaction on the -same chain has a clear causality relation (same as the order in the -blockchain). +Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). -Message passing implies a causal ordering over multiple chains and these can be -important for reasoning on the system. Given _x_ → _y_ means _x_ is -causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies -_b_: +Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: + +_A:send(msgi )_ → _B:receive(msgi )_ + +_B:receive(msgi )_ → _A:receipt(msgi )_ + +_A:send(msgi )_ → _A:send(msgi+1 )_ + +_x_ → _A:send(msgi )_ ⇒ +_x_ → _B:receive(msgi )_ + +_y_ → _B:receive(msgi )_ ⇒ +_y_ → _A:receipt(msgi )_ -* _A:send(msgi )_ → _B:receive(msgi )_ -* _B:receive(msgi )_ → _A:receipt(msgi )_ -* _A:send(msgi )_ → _A:send(msgi+1 )_ -* _x_ → _A:send(msgi )_ ⇒ -* _x_ → _B:receive(msgi )_ -* _y_ → _B:receive(msgi )_ ⇒ -* _y_ → _A:receipt(msgi )_ ![Vector Clock image](https://upload.wikimedia.org/wikipedia/commons/5/55/Vector_Clock.svg) -![](https://en.wikipedia.org/wiki/Vector_clock) - -In this section, we define an efficient implementation of a secure, reliable -messaging queue. +([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) +In this section, we define an efficient implementation of a secure, reliable messaging queue. ### 3.1 Merkle Proofs for Queues -Given the three proofs we have available, we make use of the most flexible one, -_Mk,v,h_, to provide proofs for a message queue. To do so, we must -define a unique, deterministic, and predictable key in the merkle store for -each message in the queue. We also define a clearly defined format for the -content of each message in the queue, which can be parsed by all chains -participating in IBC. The key format and queue ordering are conceptually -explained here. The binary encoding format can be found in Appendix C. +Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. -We can visualize a queue as a slice pointing into an infinite sized array. It -maintains a head and a tail pointing to two indexes, such that there is data -for every index where _head <= index < tail_. Data is pushed to the tail and -popped from the head. Another method, _advance_, is introduced to pop all -messages until _i_, and is useful for cleanup: +We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: **init**: _qhead = qtail = 0_ @@ -83,68 +52,32 @@ messages until _i_, and is useful for cleanup: **tail** ⇒ **i**: _qtail_ -Based upon this needed functionality, we define a set of keys to be stored in -the merkle tree, which allows us to efficiently implement and prove any of the -above queries. +Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. **Key:** _(queue name, [head|tail|index])_ -The index is stored as a fixed-length unsigned integer in big endian format, so -that the lexicographical order of the byte representation of the key is -consistent with their sequence number. This allows us to quickly iterate over -the queue, as well as prove the content of a packet (or lack of packet) at a -given sequence. _head_ and _tail_ are two special constants that store an -integer index, and are chosen such that their serialization cannot collide with -any possible index. +The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. -A message queue is simply a set of serialized packets stored at predefined keys -in a merkle store, which can produce proofs for any key. Once a packet is -written it must be immutable (except for deleting when popped from the queue). -That is, if a value _v_ is written to a queue, then every valid proof -_Mk,v,h _ must refer to the same _v_. This property is essential to -safely process asynchronous messages. +A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. This property is essential to safely process asynchronous messages. -Every IBC implementation must provide a protected subspace of the merkle store -for use by each queue that cannot be affected by other modules. +Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. ### 3.2 Naming Queues -As mentioned above, in order for the receiver to unambiguously interpret the -merkle proofs, we need a unique, deterministic, and predictable key in the -merkle store for each message in the queue. We explained how the indexes are -generated to provide each message in a queue a unique key, and mentioned the -need for a unique name for each queue. +As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. -The queue name must be unambiguously associated with a given connection to -another chain, so an observer can prove if a message was intended for chain A -or chain B. In order to do so, upon registration of a connection with a remote -chain, we create two queues with different names (prefixes). +The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). * _ibc::send_ - all outgoing packets destined to chain A * _ibc::receipt_ - the results of executing the packets received from chain A -These two queues have different purposes and store messages of different types. -By parsing the key of a merkle proof, a recipient can uniquely identify which -queue, if any, this message belongs to. We now define -_k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and -verify every message, before the contents of the packet are processed by the -appropriate application logic. +These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. ### 3.3 Message Contents -Up to this point, we have focused on the semantics of the message key, and how -we can produce a unique identifier for every possible message in every possible -connection. The actual data written at the location has been left as an opaque -blob, but by providing some structure to the messages, we can enable more -functionality. +Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. -We define every message in a _send queue_ to consist of a well-known type and -opaque data. The IBC protocol relies on the type for routing, and lets the -appropriate module process the data as it sees fit. The _receipt queue_ stores -if it was an error, an optional error code, and an optional return value. We -use the same index as the received message, so that the results of -_A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: -the message at index _i_ in the _send_ queue for chain B as stored on chain A) +We define every message in a _send queue_ to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) _Vsend = (type, data)_ @@ -152,32 +85,16 @@ _Vreceipt = (result, [success|error code])_ ### 3.4 Sending a Message -A proper implementation of IBC requires all relevant state to be encapsulated, -so that other modules can only interact with it via a fixed API (to be defined -in the next sections) rather than directly mutating internal state. This allows -the IBC module to provide security guarantees. +A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. -Sending an IBC packet involves an application module calling the send method of -the IBC module with a packet and a destination chain id. The IBC module must -ensure that the destination chain was already properly registered, and that the -calling module has permission to write this packet. If so, the IBC module -simply pushes the packet to the tail of the _send queue_, which enables all the -proofs described above. +Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send queue_, which enables all the proofs described above. -The permissioning of which module can write which packet can be defined per -type, so this module can maintain any application-level invariants related to -this area. Thus, the "coin" module can maintain the constant supply of tokens, -while another module can maintain its own invariants, without IBC messages -providing a means to escape their encapsulations. The IBC module must associate -every supported message type with a particular handler (_ftype_) and -return an error for unsupported types. +The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. _(IBCsend(D, type, data)_ ⇒ _Success)_ ⇒ _push(qD.send ,Vsend{type, data})_ -We also consider how a given blockchain _A_ is expected to receive the packet -from a source chain _S_ with a merkle proof, given the current set of trusted -headers for that chain, _TS_: +We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ * _qS.receipt =_ ∅ ⇒ _Error("unregistered sender"),_ @@ -188,30 +105,13 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ * _valid(Hh ,Mk,v,h ) = false_ ⇒ _Error("invalid merkle proof"),_ * _v = (type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err)); Success_ -Note that this requires not only a valid proof, but also that the proper header -as well as all prior messages were previously submitted. This returns success -upon accepting a proper message, even if the message execution returned an -error (which must then be relayed to the sender). +Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). ### 3.5 Receipts -When we wish to create a transaction that atomically commits or rolls back -across two chains, we must look at the receipts from sending the original -message. For example, if I want to send tokens from Alice on chain A to Bob on -chain B, chain A must decrement Alice's account _if and only if_ Bob's account -was incremented on chain B. We can achieve that by storing a protected -intermediate state on chain A, which is then committed or rolled back based on -the result of executing the transaction on chain B. +When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. -To do this requires that we not only provable send a message from chain A to -chain B, but provably return the result of that message (the receipt) from -chain B to chain A. As one noticed above in the implementation of _IBCreceive_, -if the valid IBC message was sent from A to B, then the result of executing it, -even if it was an error, is stored in _B:qA.receipt_. Since the -receipts are stored in a queue with the same key construction as the sending -queue, we can generate the same set of proofs for them, and perform a similar -sequence of steps to handle a receipt coming back to _S_ for a message -previously sent to _A_: +To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: _S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ @@ -224,10 +124,7 @@ _S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ * _v = (\_, error)_ ⇒ _(type, data) := pop(qS.send ); rollbacktype(data); Success_ * _v = (res, success)_ ⇒ _(type, data) := pop(qS.send ); committype(data, res); Success_ -This enforces that the receipts are processed in order, to allow some the -applications to make use of some basic assumptions about ordering. It also -removes the message from the send queue, as there is now proof it was processed -on the receiving chain and there is no more need to store this information. +This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. ![Successful Transaction](images/Receipts.png) @@ -236,26 +133,13 @@ on the receiving chain and there is no more need to store this information. ### 3.6 Relay Process -The blockchain itself only records the _intention_ to send the given message to -the recipient chain, it doesn't make any network connections as that would add -unbounded delays and non-determinism into the state machine. We define the -concept of a _relay_ process that connects two chain by querying one for all -proofs needed to prove outgoing messages and submit these proofs to the -recipient chain. +The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. -The relay process must have access to accounts on both chains with sufficient -balance to pay for transaction fees but needs no other permissions. Many -_relay_ processes may run in parallel without violating any safety -consideration. However, they will consume unnecessary fees if they submit the -same proof multiple times, so some minimal coordination is ideal. +The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. -As an example, here is a naive algorithm for relaying send messages from A to -B, without error handling. We must also concurrently run the relay of receipts -from B back to A, in order to complete the cycle. Note that all reads of -variables belonging to a chain imply queries and all function calls imply -submitting a transaction to the blockchain. +As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. -```go +``` while true pending := tail(A:qB.send) received := tail(B:qA.receive) @@ -269,19 +153,8 @@ while true sleep(desiredLatency) ``` -Note that updating a header is a costly transaction compared to posting a -merkle proof for a known header. Thus, a process could wait until many messages -are pending, then submit one header along with multiple merkle proofs, rather -than a separate header for each message. This decreases total computation cost -(and fees) at the price of additional latency and is a trade-off each relay can -dynamically adjust. +Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. -In the presence of multiple concurrent relays, any given relay can perform -local optimizations to minimize the number of headers it submits, but remember -the frequency of header submissions defines the latency of the packet transfer. +In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. -Indeed, it is ideal if each user that initiates the creation of an IBC packet -also relays it to the recipient chain. The only constraint is that the relay -must be able to pay the appropriate fees on the destination chain. However, in -order to avoid bottlenecks, a group may sponsor an account to pay fees for a -public relayer that moves all unrelayed packets (perhaps with a high latency). +Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md index 9d7b302a8b..5864337df0 100644 --- a/docs/spec/ibc/specification.md +++ b/docs/spec/ibc/specification.md @@ -6,32 +6,14 @@ _v0.4.0 / Feb. 13, 2018_ ## Abstract -This paper specifies the IBC (inter blockchain communication) protocol, which -was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June -2016. The IBC protocol uses authenticated message passing to simultaneously -solve two problems: -* transferring value (and state) between two distinct chains -* sharding one chain securely. -IBC follows the message-passing paradigm and assumes that the participating -chains are independent. +This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. -Each chain maintains a local partial order, while inter-chain messages track -any cross-chain causality relations. Once two chains have registered a trust -relationship, cryptographically provable packets can be securely sent between -the chains, due to Tendermint's instant finality property. +Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. -We currently use this protocol for secure value transfer in the Cosmos Hub, but -the protocol can support arbitrary application logic. Designing secure -communication logic for other types of applications is still an area of active -research. - -The protocol makes no assumptions about block times or network delays in the -transmission of the packets between chains and requires cryptographic proofs -for every message, and thus is highly robust in a heterogeneous environment -with Byzantine actors. This paper explains the requirements and structure of -the Cosmos IBC protocol. It aims to provide enough detail to fully understand -and analyze the security of the protocol. +We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value ar +e provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. +The protocol makes no assumptions of block times or network delays in the transmission of the packets between chains and requires cryptographic proofs for every message, and thus is highly robust in a heterogeneous environment with Byzantine actors. This paper explains the requirements and structure of the Cosmos IBC protocol. It aims to provide enough detail to fully understand and analyze the security of the protocol. ## Contents From 6500728dd283f8ff79376b83cf2611cfe1880d0a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 12 Apr 2018 13:32:49 +0200 Subject: [PATCH 12/32] Rebase onto develop --- docs/spec/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/spec/README.md b/docs/spec/README.md index 6492dc9392..e7507bf95e 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -5,7 +5,6 @@ the Cosmos Hub. NOTE: the specifications are not yet complete and very much a work in progress. -<<<<<<< HEAD - [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for sending tokens. - [Staking](staking) - Proof of Stake related specifications including bonding From dc2c638f7f86dfd4fa4f42f7c19c27e349f0a05e Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 12 Apr 2018 18:46:16 +0200 Subject: [PATCH 13/32] Move specification.md to README.md, cleanup overview, separate MVP directory --- docs/spec/ibc/README.md | 45 ++++++++++++++++++++++++++++--- docs/spec/ibc/appendix.md | 2 +- docs/spec/ibc/{ => mvp}/ibc.md | 0 docs/spec/ibc/{ => mvp}/mvp1.md | 0 docs/spec/ibc/{ => mvp}/mvp2.md | 0 docs/spec/ibc/{ => mvp}/mvp3.md | 0 docs/spec/ibc/optimizations.md | 2 +- docs/spec/ibc/overview.md | 2 +- docs/spec/ibc/proofs.md | 2 +- docs/spec/ibc/queues.md | 2 +- docs/spec/ibc/specification.md | 48 --------------------------------- 11 files changed, 46 insertions(+), 57 deletions(-) rename docs/spec/ibc/{ => mvp}/ibc.md (100%) rename docs/spec/ibc/{ => mvp}/mvp1.md (100%) rename docs/spec/ibc/{ => mvp}/mvp2.md (100%) rename docs/spec/ibc/{ => mvp}/mvp3.md (100%) delete mode 100644 docs/spec/ibc/specification.md diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 43183251e9..b9251a5189 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -1,7 +1,44 @@ -# Inter Blockchain Communication protocol +# Cosmos Inter-Blockchain Communication (IBC) Protocol -IBC was defined in the [cosmos whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), -and then in detail in a [specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). +## Abstract -This package builds on that and includes detailed specifications, pseudocode and protocol specification. Please read the [table of contents](specification.md) of the IBC specification. +This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. The IBC protocol defines a set of semantics for authenticated, strictly-ordered message passing between two blockchains with independent consensus algorithms. +The protocol requires two blockchains with cheaply verifiable instant finality. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. + +Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. + +IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), +and then later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). +This documentation replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. + +## Contents + +1. **[Overview](overview.md)** + 1. Definitions + 1. Threat Models +1. **[Proofs](proofs.md)** + 1. Establishing a Root of Trust + 1. Following Block Headers +1. **[Messaging Queue](queues.md)** + 1. Merkle Proofs for Queues + 1. Naming Queues + 1. Message Contents + 1. Sending a Packet + 1. Receipts + 1. Relay Process +1. **[Optimizations](optimizations.md)** + 1. Cleanup + 1. Timeout + 1. Handling Byzantine Failures +1. **[Conclusion](conclusion.md)** + +**[Appendix A: Encoding Libraries](appendix.md#appendix-a-encoding-libraries)** + +**[Appendix B: IBC Queue Format](appendix.md#appendix-b-ibc-queue-format)** + +**[Appendix C: Merkle Proof Format](appendix.md#appendix-c-merkle-proof-formats)** + +**[Appendix D: Universal IBC Packets](appendix.md#appendix-d-universal-ibc-packets)** + +**[Appendix E: Tendermint Header Proofs](appendix.md#appendix-e-tendermint-header-proofs)** diff --git a/docs/spec/ibc/appendix.md b/docs/spec/ibc/appendix.md index e818ad9ef5..bd277ff704 100644 --- a/docs/spec/ibc/appendix.md +++ b/docs/spec/ibc/appendix.md @@ -1,6 +1,6 @@ # Appendices -([Back to table of contents](specification.md#contents)) +([Back to table of contents](README.md#contents)) ## Appendix A: Encoding Libraries diff --git a/docs/spec/ibc/ibc.md b/docs/spec/ibc/mvp/ibc.md similarity index 100% rename from docs/spec/ibc/ibc.md rename to docs/spec/ibc/mvp/ibc.md diff --git a/docs/spec/ibc/mvp1.md b/docs/spec/ibc/mvp/mvp1.md similarity index 100% rename from docs/spec/ibc/mvp1.md rename to docs/spec/ibc/mvp/mvp1.md diff --git a/docs/spec/ibc/mvp2.md b/docs/spec/ibc/mvp/mvp2.md similarity index 100% rename from docs/spec/ibc/mvp2.md rename to docs/spec/ibc/mvp/mvp2.md diff --git a/docs/spec/ibc/mvp3.md b/docs/spec/ibc/mvp/mvp3.md similarity index 100% rename from docs/spec/ibc/mvp3.md rename to docs/spec/ibc/mvp/mvp3.md diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index e6c2f8cbcf..17967c0111 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -1,6 +1,6 @@ ## 4 Optimizations -([Back to table of contents](specification.md#contents)) +([Back to table of contents](README.md#contents)) The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index 9c07d83691..48d739796d 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -1,6 +1,6 @@ ## 1 Overview -([Back to table of contents](specification.md#contents)) +([Back to table of contents](README.md#contents)) The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md index c402eae393..d8fa8f7e94 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/proofs.md @@ -1,6 +1,6 @@ ## 2 Proofs -([Back to table of contents](specification.md#contents)) +([Back to table of contents](README.md#contents)) The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh_ is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index b0f459c40a..780d382859 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -1,6 +1,6 @@ ## 3 Messaging Queue -([Back to table of contents](specification.md#contents)) +([Back to table of contents](README.md#contents)) Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. diff --git a/docs/spec/ibc/specification.md b/docs/spec/ibc/specification.md deleted file mode 100644 index 5864337df0..0000000000 --- a/docs/spec/ibc/specification.md +++ /dev/null @@ -1,48 +0,0 @@ -# IBC Protocol Specification - -_v0.4.0 / Feb. 13, 2018_ - -**Ethan Frey** - -## Abstract - -This paper specifies the IBC (inter blockchain communication) protocol, which was first described in the Cosmos white paper [[1](./footnotes.md#1)] in June 2016. The IBC protocol uses authenticated message passing to simultaneously solve two problems: transferring value (and state) between two distinct chains, as well as sharding one chain securely. IBC follows the message-passing paradigm and assumes the participating chains are independent. - -Each chain maintains a local partial order, while inter-chain messages track any cross-chain causality relations. Once two chains have registered a trust relationship, cryptographically provable packets can be securely sent between the chains, using Tendermint's instant finality for quick and efficient transmission. - -We currently use this protocol for secure value transfer in the Cosmos Hub, but the protocol can support arbitrary application logic. Details of how Cosmos Hub uses IBC to securely route and transfer value ar -e provided in a separate paper, along with a framework for expressing global invariants. Designing secure communication logic for other types of applications is still an area of research. - -The protocol makes no assumptions of block times or network delays in the transmission of the packets between chains and requires cryptographic proofs for every message, and thus is highly robust in a heterogeneous environment with Byzantine actors. This paper explains the requirements and structure of the Cosmos IBC protocol. It aims to provide enough detail to fully understand and analyze the security of the protocol. - -## Contents - -1. **[Overview](overview.md)** - 1. Definitions - 1. Threat Models -1. **[Proofs](proofs.md)** - 1. Establishing a Root of Trust - 1. Following Block Headers -1. **[Messaging Queue](queues.md)** - 1. Merkle Proofs for Queues - 1. Naming Queues - 1. Message Contents - 1. Sending a Packet - 1. Receipts - 1. Relay Process -1. **[Optimizations](optimizations.md)** - 1. Cleanup - 1. Timeout - 1. Handling Byzantine Failures -1. **[Conclusion](conclusion.md)** - -**[Appendix A: Encoding Libraries](appendix.md#appendix-a-encoding-libraries)** - -**[Appendix B: IBC Queue Format](appendix.md#appendix-b-ibc-queue-format)** - -**[Appendix C: Merkle Proof Format](appendix.md#appendix-c-merkle-proof-formats)** - -**[Appendix D: Universal IBC Packets](appendix.md#appendix-d-universal-ibc-packets)** - -**[Appendix E: Tendermint Header Proofs](appendix.md#appendix-e-tendermint-header-proofs)** - From f1c7d1cceaee423336da1b53fdba0fdb0250093f Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Apr 2018 16:27:32 +0200 Subject: [PATCH 14/32] Cleanup & clarification in progress --- docs/spec/ibc/README.md | 8 ++--- docs/spec/ibc/conclusion.md | 6 ++-- docs/spec/ibc/footnotes.md | 3 -- docs/spec/ibc/overview.md | 36 ++++++++------------- docs/spec/ibc/proofs.md | 64 ++++++++++++++++++++++--------------- 5 files changed, 58 insertions(+), 59 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index b9251a5189..9378ba93cd 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -4,13 +4,11 @@ This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. The IBC protocol defines a set of semantics for authenticated, strictly-ordered message passing between two blockchains with independent consensus algorithms. -The protocol requires two blockchains with cheaply verifiable instant finality. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. +IBC requires two blockchains with cheaply verifiable rapid finality. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. -Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. +The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. -IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), -and then later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). -This documentation replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. +IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This documentation replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. ## Contents diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md index 37d555e6c4..9e58c248ea 100644 --- a/docs/spec/ibc/conclusion.md +++ b/docs/spec/ibc/conclusion.md @@ -1,7 +1,7 @@ ## 5 Conclusion -We have demonstrated a secure, performant, and flexible protocol for connecting two blockchains with complete finality using a secure, reliable messaging queue. The algorithm and semantics of all data types have been defined above, which provides a solid basis for reasoning about correctness and efficiency of the algorithm. +We have demonstrated a secure, performant, and flexible protocol for cross-blockchain messaging, and provided sufficient detail to reason about the correctness and efficiency of the protocol. -The observant reader may note that while we have defined a message queue protocol, we have not yet defined how to use that to transfer value within the Cosmos ecosystem. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. +This document defines solely a message queue protocol - not the application-level semantics which must sit on top of it to enable asset transfer between two chains. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. -There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in go and freely usable under the Apache license. For those wish to write an implementation of IBC in another language, or who want to analyze the specification further, the following appendixes define the exact message formats and binary encoding. +There is a reference implementation of the Cosmos IBC protocol as part of the Cosmos SDK, written in Golang and released under the Apache license. To facilitate implementations in other langauages which are wire-compatible with the Cosmos implementation, the following appendices define exact message and binary encoding formats. diff --git a/docs/spec/ibc/footnotes.md b/docs/spec/ibc/footnotes.md index eecd528412..bb263fb635 100644 --- a/docs/spec/ibc/footnotes.md +++ b/docs/spec/ibc/footnotes.md @@ -3,9 +3,6 @@ ##### 1: [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) -##### 2: -[http://www.amqp.org/sites/amqp.org/files/amqp.pdf](http://www.amqp.org/sites/amqp.org/files/amqp.pdf) - ##### 3: [https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index 48d739796d..e909bc39e4 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -2,38 +2,30 @@ ([Back to table of contents](README.md#contents)) -The IBC protocol creates a mechanism by which multiple sovereign replicated fault tolerant state machines my pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. +The IBC protocol creates a mechanism by which two replicated fault-tolerant state machines may pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. -The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an extremely secure message queue protocol, allowing the creation of complex inter-chain processes without trusted parties. This architecture can be seen as a parallel to microservices in the blockchain space, and the IBC protocol can be seen as an analog to the AMQP messaging protocol[[2](./footnotes.md#2)], used by StormMQ, RabbitMQ, etc. +The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an ordered message queue primitive, allowing the creation of complex inter-chain processes without trusted third parties. -The message packets are not signed by one psuedonymous account, or even multiple. Rather, IBC effectively assigns authorization of the packets to the blockchain's consensus algorithm itself. Not only are blockchains highly secure, they are auditable and have an extremely high creation cost in comparison to cryptographic key pairs. This prevents Sybil attacks and allows out-of-protocol accountability, since any byzantine behavior is provable and can be published to damage the reputation/value of the other blockchain. By using registered blockchains as "actors" in the system, we can achieve extremely high security through a combination of cryptography and incentives. +The message packets are not signed by one psuedonymous account, or even multiple, as in multi-signature sidechain implementations. Rather, IBC assigns authorization of the packets to the source blockchain's consensus algorithm, performing light-client style verification on the destination chain. The Byzantine-fault-tolerant properties of the underlying blockchains are preserved: a user transferring assets between two chains using IBC must trust only the consensus algorithms of both chains. -In this paper, we define a process of posting block headers and merkle proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee reliable, in-order delivery of message. We then explain how to securely handle receipts (response/error), which enables the creation of asynchronous RPC-like protocols. Finally, we detail some optimizations and how to handle byzantine blockchains. +In this paper, we define a process of posting block headers and Merkle tree proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee ordered delivery. We then explain how to handle packet receipts (response/error) on the source chain, which enables the creation of asynchronous RPC-like protocols on top of IBC. Finally, we detail some optimizations and how to handle Byzantine blockchains. -### 1.1 Definitions +### 1.1 Definitions -_Blockchain_ - an immutable ledger created through distributed consensus, coupled with a deterministic state machine to process the transactions on the ledger. The smallest unit produced through consensus is a block, which may contain many transactions. +_Blockchain_ - A replicated fault-tolerant state machine with a distributed consensus algorithm. The smallest unit produced through consensus is a block, which may contain many transactions, each applying some arbitrary mutation to the state. -_Module_ - we assume that the state machine of the blockchain is comprised of multiple components (modules or smart contracts) that have limited rights, and they can only interact over pre-defined interfaces rather than directly mutating internal state. +_Module_ - We assume that the state machine of each blockchain is comprised of multiple components that have limited rights to execute some particular set of state transfers (these are modules in the Cosmos SDK or smart contracts in Ethereum). -_Finality_ - a guarantee that a given block will not be reverted within some predefined conditions. All proof of work systems offer probabilistic finality, which means the probability of that a block will be reverted approaches 0. A "better", alternative chain could exist, but the cost of creation increases rapidly over time. Many "proof of stake" systems offer much weaker guarantees, based only on the honesty of the miners. However, BFT algorithms such as Tendermint guarantee complete finality upon production of a block, unless over two thirds of the validators collude to break consensus. This collusion is provable and can be punished. +_Finality_ - The guarantee that a given block will not be reverted within some predefined conditions of a consensus algorithm. All proof-of-work systems offer probabilistic finality, which means that the difficulty of reverting a block increases as the block is embedded more deeply in the chain. Many proof-of-stake systems offer much weaker guarantees, based only on the honesty of the block producers. BFT algorithms such as Tendermint guarantee complete finality upon production of a block (unless over two thirds of the validators collude to break consensus, in which case the offenders can be identified and punished - further discussion of that scenario is outside the scope of this document). -_Knowledge_ - what is certain to be true. +_Attributable_ - Knowledge of the pseudonymous identity which made a statement, whom we can punish with some deduction of value (slashing) if the statement is false. Synonymous with accountability. -_Provable_ - the existence of irrefutable mathematical (often cryptographic) proof of the truth of a given statement. These can be expressed as: given knowledge **A** and a statement **s**, then **B** must be true. This is a form of deductive proof and they can be chained together without losing validity. +_Unbonding Period_ - Proof-of-stake algorithms need to lock the stake (prevent transfers) for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Complete finality is associated with a subset of the proof-of-stake class of consensus algorithms. We assume the proof-of-stake algorithms utilized by the two blockchains have some unbonding period P. -_Attributable_ - provable knowledge of who made a statement. If a statement is provably false, then it is known which actor lied. Attributable statements allow us to build incentives against lying, which help enforce finality. This is also referred to as accountability. +### 1.2 Threat Models -_Root of Trust_ - any proof depends on some prior assumptions, however simple they are. We refer to the first assumption we make as the root of trust, and all our knowledge of the system is derived from this root through a provable chain of information. We seek to make this root of trust as simple and a verifiable as possible, since if the original assignment of trust is false, all conclusions drawn will also be false. +_False statements_ - Any information we receive may be false. -_Unbonding Period_ - Proof of Stake algorithms need to freeze the stake for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Since complete finality is associated with a subset of the Proof of Stake class of consensus algorithms, I will assume all implementations that support IBC have some unbonding period P, such that if my last knowledge of the blockchain is older than P, I can no longer trust any message without a new root of trust. +_Network partitions and delays_ - We assume an asynchronous, adversarial network with unbounded latency. Network messages may be modified, reordered, duplicated, or selectively dropped. Actors may be arbitrarily partitioned by a powerful adversary. The IBC protocol favors correctness over liveness (and provides no particular guarantees of the latter). -The IBC protocol requires each actor to be a blockchain with complete finality. All transitions must be provable and attributable to (at least) one actor. That implies the smallest unit of trust is the consensus algorithm of a blockchain. - -### 1.2 Threat Models - -_False statements_ - any information we receive may be false, all actors must have enough knowledge be able to prove its correctness without external dependencies. All statements should be attributable. - -_Network partitions and delays_ - we assume an asynchronous, adversarial network. Any message may or may not reach the destination. They may be modified or selectively dropped. Messages may reach the destination out of order and may arrive multiple times. There is no upper limit to the time it takes for a message to be received. Actors may be arbitrarily partitioned by a powerful adversary. The protocol favors correctness over liveness. That is, it only acts upon information that is provably correct. - -_Byzantine actors_ - it is possible that an entire blockchain is not acting according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Furthermore, we should design application-level protocols on top of IBC to minimize risk exposure in the face of Byzantine actors. +_Byzantine actors_ - An entire blockchain may not act according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Application-level protocols designed on top of IBC should consider this risk and mitigate it as possible in a manner suitable to their application. diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/proofs.md index d8fa8f7e94..3065f51fc9 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/proofs.md @@ -2,37 +2,49 @@ ([Back to table of contents](README.md#contents)) -The basis of IBC is the ability to perform efficient proofs of a message packet on-chain and deterministically. All transactions must be attributable and provable without depending on any information outside of the blockchain. We define the following variables: _Hh_ is the signed header at height _h_, _Ch_ are the consensus rules at height _h_, and _P_ is the unbonding period of this blockchain. _Vk,h_ is the value stored under key _k_ at height _h_. Note that of all these, only _Hh_ defines a signature and is thus attributable. +The basis of IBC is the ability to verify in the on-chain consensus ruleset of chain _B_ that a message packet received on chain _B_ was correctly generated on chain _A_. This establishes a cross-chain linearity guarantee: upon validation of that packet on chain _B_ we know that the packet has been executed on chain _A_ and any associated logic resolved (such as assets being escrowed), and we can safely perform application logic on chain _B_ (such as generating vouchers on chain _B_ for the chain _A_ assets which can later be redeemed with a packet in the opposite direction). -To support an IBC connection, two actors must be able to make the following proofs to each other: +### 2.1 Definitions -* given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_ it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ -* given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_ it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ -* given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ +- Chain _A_ is the source blockchain from which the IBC packet is sent +- Chain _B_ is the destination blockchain on which the IBC packet is received +- _Hh_ is the signed header of chain _A_ at height _h_ +- _Ch_ is the consensus ruleset of chain _A_ at height _h_ +- _Vk,h_ is the value stored on chain _A_ under key _k_ at height _h_ +- _P_ is the unbonding period of chain _A_, in units of time +- Δ_(a, b)_ is the time difference between events _a_ and _b_ -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint is defined in Appendix E. Another engine able to provide equally strong guarantees (such as Casper) should be theoretically compatible with IBC, and must define its own set of update/change messages. +Note that of all these, only _Hh_ defines a signature and is thus attributable. + +### 2.2 Basics + +To facilitate an IBC connection, the two blockchains must provide the following proofs: + +1. Given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_, + it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ +2. Given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_, + it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ +3. Given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ + +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint consensus is defined in Appendix E. Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ -### 2.1 Establishing a Root of Trust +### 2.3 Establishing a Root of Trust -As mentioned in the definitions, all proofs are based on an original assumption. In this case it is _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. +All proofs require an initial _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. -Any header may be from a malicious chain (eg. shadowing a real chain id with a fake validator set), so a subjective decision is required before establishing a connection. This should be performed by on-chain governance to avoid an exploitable position of trust. Establishing a bidirectional root of trust between two blockchains (A trusts B and B trusts A) is a necessary and sufficient prerequisite for all other IBC activity. +Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed by on-chain governance or a similar decentralized mechanism if desired. Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) is necessary before any IBC packets can be sent. -Development of a fully open and decentralized PKI for tracking blockchains is an open research question for future iterations of the IBC protocol. +### 2.4 Following Block Headers -### 2.2 Following Block Headers +We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to some future _Hh_ where _h > n_. Some implementations may require that _h = n + 1_ (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that Δ_vals(Cn, Ch ) < ⅓_ (each step must have a change of less than one-third of the validator set)[[4](./footnotes.md#4)]. -We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to a future _Hh_ where _h > n_. Some implementations may provide the additional limitation that _h = n + 1_, which requires us to process every header. Tendermint allows us to exploit knowledge of the BFT algorithm to only require the additional limitation +Either requirement is compatible with IBC. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains _A_ and _B_, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). -Δ_vals(Cn, Ch ) < ⅓_, that each step must have a change of less than one-third of the validator set[[4](./footnotes.md#4)]. - -Any of these requirements allows us to support IBC for the given block chain. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC message between chains A and B, and enable low-bandwidth connections to be implemented at very low cost. If there are messages to relay every block, then these collapse to the same case, relaying every header. - -Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such any attempt to violate the finality guarantees or provide fake proof can be submitted to the remote blockchain for punishment, in the same manner that any violation of the internal consensus algorithm is punished. This incentive enhances the security guarantees and avoids the nothing-at-stake issue in IBC as well. +Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such, any attempt to violate the finality guarantees in headers posted to chain _B_ can be submitted back to chain _A_ for punishment, in the same manner that chain _A_ would independently punish (slash) identified Byzantine actors. More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: @@ -40,19 +52,19 @@ _valid(T, Xh | Uh )_ ⇒ _[true | false | u _if Hh-1_ ∈ _T then_: * _valid(T, Xh | Uh )_ ⇒ _[true | false]_ -* _there must exist some Uh or Xh that evaluates to true_ +* ∃ (Uh | Xh) ⇒ valid(T, Xh | Uh) {aren't there infinite? why is this necessary} _if Ch_ ∉ _T then_ * _valid(T, Uh )_ ⇒ _false_ -and can process update transactions as follows: +We can then process update transactions as follows: -_update(T, Xh | Uh )_ ⇒ -_ match valid(T, Xh | Uh )_ -* _false_ ⇒ _return Error("invalid proof")_ -* _unknown_ ⇒ _return Error("need a proof between current and h")_ -* _true_ ⇒ _T_ ∪ _(Hh ,Ch )_ +_update(T, Xh | Uh )_ ⇒ match _valid(T, Xh | Uh )_ with +* _false_ ⇒ fail with `invalid proof` +* _unknown_ ⇒ fail with `need a proof between current and h` +* _true_ ⇒ set _T_ = _T_ ∪ _(Hh ,Ch )_ -We define _max(T)_ as _max(h, where Hh_ ∈ _T)_ for any _T_ with _max(T) = h-1_. And from above, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. By induction, we can see there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. +Define _max(T)_ as _max(h, where Hh_ ∈ _T)_. For any _T_ with _max(T) = h-1_, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. +By induction, there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. -We also can see the validity of using bisection as an optimization to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. +Bisection can be used to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. From f01ab5e4c930e59ef1dc5652bbc3d263f6c40ac3 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 13 Apr 2018 19:01:40 +0200 Subject: [PATCH 15/32] Start editing of Queues section --- docs/spec/ibc/queues.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index 780d382859..c86b9fb2a5 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -1,18 +1,14 @@ -## 3 Messaging Queue +## 3 Packet Queue ([Back to table of contents](README.md#contents)) -Messaging in distributed systems is a deeply researched field and a primitive upon which many other systems are built. We can model asynchronous message passing, and make no timing assumptions on the communication channels. By doing this, we allow each zone to move at its own speed, unblocked by any other zone, but able to communicate as fast as the network allows at that moment. +IBC uses an asynchronous message passing model that makes no assumptions about network synchrony. Chain _A_ and chain _B_ confirm new blocks independently, and IBC packets from one chain to the other may be delayed or censored arbitrarily. The speed of the IBC packet queue is limited only by the speed of the underlying chains. -Another benefit of using message passing as our primitive, is that the receiver decides how to act upon the incoming message. Just because one zone sends a message and we have an IBC connection with this zone, doesn't mean we have to execute the requested action. Each zone can add its own business logic upon receiving the message to decide whether to accept or reject the message. To maintain consistency, both sides must only agree on the proper state transitions associated with accepting or rejecting. +The IBC packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply (or not). Both chains must only agree that the packet has been received and either accepted or rejected, which is determined independently of any application logic. -This encapsulation is very difficult to impossible to achieve in a shared-state scenario. Message passing allows each zone to ensure its security and autonomy, while simultaneously allowing the different systems to work as one whole. This can be seen as an analogue to a microservices architecture, but across organizational boundaries. +To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./footnotes.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. Every transaction on the same chain already has a well-defined causality relation (order in history). The IBC protocol provides an ordering guarantee across two chains. -To build useful algorithms upon a provable asynchronous messaging primitive, we introduce a reliable messaging queue (hereafter just referred to as a queue), typical in asynchronous message passing, to allow us to guarantee a causal ordering[[5](./footnotes.md#5)], and avoid blocking. - -Causal ordering means that if _x_ is causally before _y_ on chain A, it must also be on chain B. Many events may happen concurrently (unrelated tx on two different blockchains) with no causality relation, but every transaction on the same chain has a clear causality relation (same as the order in the blockchain). - -Message passing implies a causal ordering over multiple chains and these can be important for reasoning on the system. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: +A causal ordering over multiple chains can be used to reason about the combined state of both chains as a whole. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: _A:send(msgi )_ → _B:receive(msgi )_ @@ -30,7 +26,7 @@ _y_ → _A:receipt(msgi )_ ![Vector Clock image](https://upload.wikimedia.org/wikipedia/commons/5/55/Vector_Clock.svg) ([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) -In this section, we define an efficient implementation of a secure, reliable messaging queue. +In this section, we define an efficient implementation of a reliable ordered messaging queue. ### 3.1 Merkle Proofs for Queues From 8e9615a40313e78dcf320c3b07e15954982778bd Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 12:59:09 +0200 Subject: [PATCH 16/32] Pluralize --- docs/spec/ibc/README.md | 16 ++++++---------- docs/spec/ibc/{appendix.md => appendices.md} | 10 ++++++++++ 2 files changed, 16 insertions(+), 10 deletions(-) rename docs/spec/ibc/{appendix.md => appendices.md} (98%) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 9378ba93cd..e542b8b1a8 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -30,13 +30,9 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. Timeout 1. Handling Byzantine Failures 1. **[Conclusion](conclusion.md)** - -**[Appendix A: Encoding Libraries](appendix.md#appendix-a-encoding-libraries)** - -**[Appendix B: IBC Queue Format](appendix.md#appendix-b-ibc-queue-format)** - -**[Appendix C: Merkle Proof Format](appendix.md#appendix-c-merkle-proof-formats)** - -**[Appendix D: Universal IBC Packets](appendix.md#appendix-d-universal-ibc-packets)** - -**[Appendix E: Tendermint Header Proofs](appendix.md#appendix-e-tendermint-header-proofs)** +1. **[Appendices](appendices.md)** + 1. [Appendix A: Encoding Libraries](appendices.md#appendix-a-encoding-libraries) + 1. [Appendix B: IBC Queue Format](appendices.md#appendix-b-ibc-queue-format) + 1. [Appendix C: Merkle Proof Format](appendices.md#appendix-c-merkle-proof-formats) + 1. [Appendix D: Universal IBC Packets](appendices.md#appendix-d-universal-ibc-packets) + 1. [Appendix E: Tendermint Header Proofs](appendices.md#appendix-e-tendermint-header-proofs) diff --git a/docs/spec/ibc/appendix.md b/docs/spec/ibc/appendices.md similarity index 98% rename from docs/spec/ibc/appendix.md rename to docs/spec/ibc/appendices.md index bd277ff704..2a19e43682 100644 --- a/docs/spec/ibc/appendix.md +++ b/docs/spec/ibc/appendices.md @@ -4,6 +4,8 @@ ## Appendix A: Encoding Libraries +{ figure out what encoding IBC actually uses } + The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./footnotes.md#6)] and google's protobuf[[7](./footnotes.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. @@ -14,6 +16,8 @@ For the following appendixes, the data structure specifications will be in proto ## Appendix B: IBC Queue Format +{ include queue details here instead of in the other section } + The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: _key = _(_remote id, [send|receipt], [head|tail|index])_ @@ -28,6 +32,8 @@ See [binary format as protobuf specification](./protobuf/queue.proto) ## Appendix C: Merkle Proof Formats +{ link to the implementation } + A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./footnotes.md#10)]. There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./footnotes.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. @@ -44,12 +50,16 @@ See [binary format as protobuf specification](./protobuf/merkle.proto) ## Appendix D: Universal IBC Packets +{ what is this } + The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. See [binary format as protobuf specification](./protobuf/messages.proto) ## Appendix E: Tendermint Header Proofs +{ is this finalized? } + **TODO: clean this all up** This is a mess now, we need to figure out what formats we use, define go-wire, etc. or just point to the source???? Will do more later, need help here from the tendermint core team. From 0b00dbfdcd704ba7cc842d2d740b0c14b631f7e2 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 14:37:11 +0200 Subject: [PATCH 17/32] Clarify queue interface --- docs/spec/ibc/queues.md | 69 +++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index c86b9fb2a5..e90d7593f1 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -4,11 +4,11 @@ IBC uses an asynchronous message passing model that makes no assumptions about network synchrony. Chain _A_ and chain _B_ confirm new blocks independently, and IBC packets from one chain to the other may be delayed or censored arbitrarily. The speed of the IBC packet queue is limited only by the speed of the underlying chains. -The IBC packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply (or not). Both chains must only agree that the packet has been received and either accepted or rejected, which is determined independently of any application logic. +The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply (or not). Both chains must only agree that the packet has been received and either accepted or rejected, which is determined independently of any application logic. -To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./footnotes.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. Every transaction on the same chain already has a well-defined causality relation (order in history). The IBC protocol provides an ordering guarantee across two chains. +To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./footnotes.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. IBC implements a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). -A causal ordering over multiple chains can be used to reason about the combined state of both chains as a whole. Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: +Formally, given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: _A:send(msgi )_ → _B:receive(msgi )_ @@ -22,34 +22,60 @@ _x_ → _B:receive(msgi )_ _y_ → _B:receive(msgi )_ ⇒ _y_ → _A:receipt(msgi )_ +Every transaction on the same chain already has a well-defined causality relation (order in history). IBC provides an ordering guarantee across two chains which can be used to reason about the combined state of both chains as a whole. -![Vector Clock image](https://upload.wikimedia.org/wikipedia/commons/5/55/Vector_Clock.svg) -([https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock)) +For example, an application may wish to allow a single fungible asset to be transferred between and held on multiple blockchains while preserving conservation of supply. The application can mint asset vouchers on chain _B_ when a particular IBC packet is committed to chain _B_, and require outgoing sends of that packet on chain _A_ to escrow an equal amount of the asset on chain _A_ until the vouchers are later redeemed back to chain _A_ with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain _B_ can later be redeemed back to chain _A_. -In this section, we define an efficient implementation of a reliable ordered messaging queue. -### 3.1 Merkle Proofs for Queues +### 3.1 Queue Specification -Given the three proofs we have available, we make use of the most flexible one, _Mk,v,h_, to provide proofs for a message queue. To do so, we must define a unique, deterministic, and predictable key in the merkle store for each message in the queue. We also define a clearly defined format for the content of each message in the queue, which can be parsed by all chains participating in IBC. The key format and queue ordering are conceptually explained here. The binary encoding format can be found in Appendix C. +A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. -We can visualize a queue as a slice pointing into an infinite sized array. It maintains a head and a tail pointing to two indexes, such that there is data for every index where _head <= index < tail_. Data is pushed to the tail and popped from the head. Another method, _advance_, is introduced to pop all messages until _i_, and is useful for cleanup: +Each IBC-supporting blockchain must implement a reliable ordered packet queue with the following interface specification: -**init**: _qhead = qtail = 0_ +**init** +> set _qhead_ = _0_ +> set _qtail_ = _0_ -**peek** ⇒ **m**: _if qhead = qtail { return None } else { return q[qhead] }_ +**peek** ⇒ **e** +> match _qhead == qtail_ with +> _true_ ⇒ return _nil_ +> _false_ ⇒ return _q[qhead]_ -**pop** ⇒ **m**: _if qhead = qtail { return None } else { qhead++; return q[qhead-1] }_ +**pop** ⇒ **e** +> match _qhead == qtail_ with +> _true_ ⇒ return _nil_ +> _false_ ⇒ set _qhead_ = _qhead + 1_; return _q[qhead-1]_ -**push(m)**: _q[qtail] = m; qtail++_ +**retrieve(i)** ⇒ **e** +> match _qhead <= i < qtail_ with +> _true_ ⇒ return _qi_ +> _false_ ⇒ return _nil_ -**advance(i)**: _qhead = i; qtail = max(qtail , i)_ +**push(e)** +> set _q[qtail]_ = _e_; set _qtail_ = _qtail + 1_ -**head** ⇒ **i**: _qhead_ +**advance(i)** +> set _qhead_ = _i_; set _qtail_ = _max(qtail, i)_ -**tail** ⇒ **i**: _qtail_ +**head** ⇒ **i** +> return _qhead_ + +**tail** ⇒ **i** +> return _qtail_ + +{ two queues, one send, one receive } + +### 3.2 Merkle Proofs for Queues + +In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that IBC packets have been stored at particular indices in the incoming and outgoing packet queues. + +We make use of the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. To do so, we must define a unique, deterministic key in the Merkle store for each message in the queue. Packet types and proofs are conceptually explained here. An example binary encoding format can be found in Appendix C. Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. +{ todo: rewrite the rest of this section } + **Key:** _(queue name, [head|tail|index])_ The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. @@ -58,8 +84,6 @@ A message queue is simply a set of serialized packets stored at predefined keys Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. -### 3.2 Naming Queues - As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). @@ -71,6 +95,8 @@ These two queues have different purposes and store messages of different types. ### 3.3 Message Contents +{ todo: clarify about payload-agnostic } + Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. We define every message in a _send queue_ to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) @@ -81,6 +107,8 @@ _Vreceipt = (result, [success|error code])_ ### 3.4 Sending a Message +{ todo: cleanup wording } + A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send queue_, which enables all the proofs described above. @@ -105,6 +133,8 @@ Note that this requires not only an valid proof, but also that the proper header ### 3.5 Receipts +{ todo: cleanup logic } + When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: @@ -126,9 +156,10 @@ This enforces that the receipts are processed in order, to allow some the applic ![Rejected Transaction](images/ReceiptError.png) - ### 3.6 Relay Process +{ todo: cleanup wording } + The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. From 1cdfef81cdd1b6e9591edace3ab64df3d8f23519 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 15:08:53 +0200 Subject: [PATCH 18/32] Reorder Merkle proof section --- docs/spec/ibc/queues.md | 47 +++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/queues.md index e90d7593f1..a7a7c7b98f 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/queues.md @@ -26,6 +26,7 @@ Every transaction on the same chain already has a well-defined causality relatio For example, an application may wish to allow a single fungible asset to be transferred between and held on multiple blockchains while preserving conservation of supply. The application can mint asset vouchers on chain _B_ when a particular IBC packet is committed to chain _B_, and require outgoing sends of that packet on chain _A_ to escrow an equal amount of the asset on chain _A_ until the vouchers are later redeemed back to chain _A_ with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain _B_ can later be redeemed back to chain _A_. +This section provides a high-level specification of the queue interface and a list of the necessary proofs. To implement wire-compatible IBC, chain _A_ and chain _B_ must also use a common encoding format. An example binary encoding format can be found in Appendix C. ### 3.1 Queue Specification @@ -64,48 +65,48 @@ Each IBC-supporting blockchain must implement a reliable ordered packet queue wi **tail** ⇒ **i** > return _qtail_ -{ two queues, one send, one receive } +### 3.2 Connection Abstraction -### 3.2 Merkle Proofs for Queues +We introduce the abstraction of an IBC **connection**: a set of the required components to facilitate bidirectional communication between two blockchains _A_ and _B_. -In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that IBC packets have been stored at particular indices in the incoming and outgoing packet queues. +An IBC connection consists of four distinct queues, two on each chain: -We make use of the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. To do so, we must define a unique, deterministic key in the Merkle store for each message in the queue. Packet types and proofs are conceptually explained here. An example binary encoding format can be found in Appendix C. +_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ -Based upon this needed functionality, we define a set of keys to be stored in the merkle tree, which allows us to efficiently implement and prove any of the above queries. +_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ -{ todo: rewrite the rest of this section } +_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ -**Key:** _(queue name, [head|tail|index])_ +_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ -The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serialization cannot collide with any possible index. +### 3.3 Merkle Proofs for Queues -A message queue is simply a set of serialized packets stored at predefined keys in a merkle store, which can produce proofs for any key. Once a packet is written it must be immutable (except for deleting when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. This property is essential to safely process asynchronous messages. +In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that particular IBC packets have been stored at particular indices in the outgoing packet queue, and particular IBC packet execution results have been stored at particular indices in the incoming packet queue. -Every IBC implementation must provide a protected subspace of the merkle store for use by each queue that cannot be affected by other modules. +We use the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. In order to do so, we must define a unique, deterministic key in the Merkle store for each message in the queue: -As mentioned above, in order for the receiver to unambiguously interpret the merkle proofs, we need a unique, deterministic, and predictable key in the merkle store for each message in the queue. We explained how the indexes are generated to provide each message in a queue a unique key, and mentioned the need for a unique name for each queue. +**key**: _(queue name, [head|tail|index])_ + +The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serializated representation cannot collide with that of any possible index. + +Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). -* _ibc::send_ - all outgoing packets destined to chain A -* _ibc::receipt_ - the results of executing the packets received from chain A +* _ibc::send_ - all outgoing packets destined to chain _A_ +* _ibc::receipt_ - the results of executing the packets received from chain _A_ -These two queues have different purposes and store messages of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. +These two queues have different purposes and store elements of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. -### 3.3 Message Contents +### 3.4 Message Contents -{ todo: clarify about payload-agnostic } - -Up to this point, we have focused on the semantics of the message key, and how we can produce a unique identifier for every possible message in every possible connection. The actual data written at the location has been left as an opaque blob, put by providing some structure to the messages, we can enable more functionality. - -We define every message in a _send queue_ to consist of a well-known type and opaque data. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) +We define every message in a _send queue_ to consist of two fields: an enumerable _type_, and an opaque _payload_. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) _Vsend = (type, data)_ _Vreceipt = (result, [success|error code])_ -### 3.4 Sending a Message +### 3.5 Sending a Message { todo: cleanup wording } @@ -131,7 +132,7 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). -### 3.5 Receipts +### 3.6 Receipts { todo: cleanup logic } @@ -156,7 +157,7 @@ This enforces that the receipts are processed in order, to allow some the applic ![Rejected Transaction](images/ReceiptError.png) -### 3.6 Relay Process +### 3.7 Relay Process { todo: cleanup wording } From be3fa5672e351364d664133f25a5e9f035892bfd Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 16:27:55 +0200 Subject: [PATCH 19/32] Update layout & fix anchor links --- docs/spec/ibc/README.md | 37 ++++++++------ docs/spec/ibc/appendices.md | 12 ++--- docs/spec/ibc/{proofs.md => connections.md} | 16 ++++-- docs/spec/ibc/optimizations.md | 7 ++- docs/spec/ibc/overview.md | 8 +-- docs/spec/ibc/{queues.md => packets.md} | 51 ++++++++----------- docs/spec/ibc/{footnotes.md => references.md} | 0 7 files changed, 68 insertions(+), 63 deletions(-) rename docs/spec/ibc/{proofs.md => connections.md} (96%) rename docs/spec/ibc/{queues.md => packets.md} (91%) rename docs/spec/ibc/{footnotes.md => references.md} (100%) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index e542b8b1a8..0969346c6a 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -4,7 +4,7 @@ This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. The IBC protocol defines a set of semantics for authenticated, strictly-ordered message passing between two blockchains with independent consensus algorithms. -IBC requires two blockchains with cheaply verifiable rapid finality. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. +IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. @@ -13,23 +13,28 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm ## Contents 1. **[Overview](overview.md)** - 1. Definitions - 1. Threat Models -1. **[Proofs](proofs.md)** - 1. Establishing a Root of Trust - 1. Following Block Headers -1. **[Messaging Queue](queues.md)** - 1. Merkle Proofs for Queues - 1. Naming Queues - 1. Message Contents - 1. Sending a Packet - 1. Receipts - 1. Relay Process + 1. [Summary](overview.md#11-summary) + 1. [Requirements](overview.md#12-requirements) + 1. [Threat Models](overview.md#13-threat-models) +1. **[Connections](connections.md)** + 1. [Definitions](connections.md#21-definitions) + 1. [Requirements](connections.md#22-requirements) + 1. [Connection lifecycle](connections.md#23-connection-lifecycle) + 1. [Opening a connection](connections.md#231-opening-a-connection) + 1. [Following block headers](connections.md#232-following-block-headers) + 1. [Closing a connection](connections.md#233-closing-a-connection) +1. **[Packets](packets.md)** + 1. [Definitions](packets.md#31-definitions) + 1. [Requirements](packets.md#32-requirements) + 1. [Sending a packet](packets.md#33-sending-a-packet) + 1. [Receiving a packet](packets.md#34-receiving-a-packet) + 1. [Packet relayer](packets.md#35-packet-relayer) 1. **[Optimizations](optimizations.md)** - 1. Cleanup - 1. Timeout - 1. Handling Byzantine Failures + 1. [Timeouts](optimizations.md#41-timeouts) + 1. [Cleanup](optimizations.md#42-cleanup) + 1. [Handling Byzantine failures](optimizations.md#43-handling-byzantine-failures) 1. **[Conclusion](conclusion.md)** +1. **[References](references.md)** 1. **[Appendices](appendices.md)** 1. [Appendix A: Encoding Libraries](appendices.md#appendix-a-encoding-libraries) 1. [Appendix B: IBC Queue Format](appendices.md#appendix-b-ibc-queue-format) diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 2a19e43682..96feed7091 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -8,11 +8,11 @@ The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. -In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./footnotes.md#6)] and google's protobuf[[7](./footnotes.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. +In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./references.md#6)] and google's protobuf[[7](./references.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. -The tendermint-specific data structures are encoded with go-wire[[8](./footnotes.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. +The tendermint-specific data structures are encoded with go-wire[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. -For the following appendixes, the data structure specifications will be in proto3[[9](./footnotes.md#9)] format. +For the following appendixes, the data structure specifications will be in proto3[[9](./references.md#9)] format. ## Appendix B: IBC Queue Format @@ -34,9 +34,9 @@ See [binary format as protobuf specification](./protobuf/queue.proto) { link to the implementation } -A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./footnotes.md#10)]. +A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./references.md#10)]. -There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./footnotes.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. +There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./references.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. @@ -44,7 +44,7 @@ The proof format also allows for chaining of trees, combining multiple merkle st A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. -For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./footnotes.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. +For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./references.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. See [binary format as protobuf specification](./protobuf/merkle.proto) diff --git a/docs/spec/ibc/proofs.md b/docs/spec/ibc/connections.md similarity index 96% rename from docs/spec/ibc/proofs.md rename to docs/spec/ibc/connections.md index 3065f51fc9..248a9e771a 100644 --- a/docs/spec/ibc/proofs.md +++ b/docs/spec/ibc/connections.md @@ -1,4 +1,4 @@ -## 2 Proofs +## 2 Connections ([Back to table of contents](README.md#contents)) @@ -16,7 +16,7 @@ The basis of IBC is the ability to verify in the on-chain consensus ruleset of c Note that of all these, only _Hh_ defines a signature and is thus attributable. -### 2.2 Basics +### 2.2 Requirements To facilitate an IBC connection, the two blockchains must provide the following proofs: @@ -32,15 +32,17 @@ The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ -### 2.3 Establishing a Root of Trust +### 2.3 Connection Lifecycle + +#### 2.3.1 Opening a Connection All proofs require an initial _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed by on-chain governance or a similar decentralized mechanism if desired. Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) is necessary before any IBC packets can be sent. -### 2.4 Following Block Headers +#### 2.3.2 Following Block Headers -We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to some future _Hh_ where _h > n_. Some implementations may require that _h = n + 1_ (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that Δ_vals(Cn, Ch ) < ⅓_ (each step must have a change of less than one-third of the validator set)[[4](./footnotes.md#4)]. +We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to some future _Hh_ where _h > n_. Some implementations may require that _h = n + 1_ (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that Δ_vals(Cn, Ch ) < ⅓_ (each step must have a change of less than one-third of the validator set)[[4](./references.md#4)]. Either requirement is compatible with IBC. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains _A_ and _B_, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). @@ -68,3 +70,7 @@ Define _max(T)_ as _max(h, where Hh_ ∈ _T)_. For any _T_ with _ By induction, there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. Bisection can be used to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. + +#### 2.3.3 Closing a Connection + +{ todo } diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index 17967c0111..0c4568e0eb 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -4,7 +4,7 @@ The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. -### 4.1 Timeouts +### 4.1 Timeouts Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. @@ -58,7 +58,7 @@ Now chain A can rollback all transactions that were blocked by this flood of unr Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. -### 4.2 Clean up +### 4.2 Cleanup While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. @@ -89,8 +89,7 @@ This allows us to invoke the _IBCcleanup _function to resolve all outstanding me ![Cleaning up Packets](images/CleanUp.png) - -### 4.3 Handling Byzantine Failures +### 4.3 Handling Byzantine failures While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index e909bc39e4..ab8c929188 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -2,6 +2,8 @@ ([Back to table of contents](README.md#contents)) +### 1.1 Summary + The IBC protocol creates a mechanism by which two replicated fault-tolerant state machines may pass messages to each other. These messages provide a base layer for the creation of communicating blockchain architecture that overcomes challenges in the scalability and extensibility of computing blockchain environments. The IBC protocol assumes that multiple applications are running on their own blockchain with their own state and own logic. Communication is achieved over an ordered message queue primitive, allowing the creation of complex inter-chain processes without trusted third parties. @@ -10,7 +12,7 @@ The message packets are not signed by one psuedonymous account, or even multiple In this paper, we define a process of posting block headers and Merkle tree proofs to enable secure verification of individual packets. We then describe how to combine these packets into a messaging queue to guarantee ordered delivery. We then explain how to handle packet receipts (response/error) on the source chain, which enables the creation of asynchronous RPC-like protocols on top of IBC. Finally, we detail some optimizations and how to handle Byzantine blockchains. -### 1.1 Definitions +### 1.2 Definitions _Blockchain_ - A replicated fault-tolerant state machine with a distributed consensus algorithm. The smallest unit produced through consensus is a block, which may contain many transactions, each applying some arbitrary mutation to the state. @@ -20,9 +22,9 @@ _Finality_ - The guarantee that a given block will not be reverted within some p _Attributable_ - Knowledge of the pseudonymous identity which made a statement, whom we can punish with some deduction of value (slashing) if the statement is false. Synonymous with accountability. -_Unbonding Period_ - Proof-of-stake algorithms need to lock the stake (prevent transfers) for some time to provide a lower bound for the length of a long-range attack [[3](./footnotes.md#3)]. Complete finality is associated with a subset of the proof-of-stake class of consensus algorithms. We assume the proof-of-stake algorithms utilized by the two blockchains have some unbonding period P. +_Unbonding Period_ - Proof-of-stake algorithms need to lock the stake (prevent transfers) for some time to provide a lower bound for the length of a long-range attack [[3](./references.md#3)]. Complete finality is associated with a subset of the proof-of-stake class of consensus algorithms. We assume the proof-of-stake algorithms utilized by the two blockchains have some unbonding period P. -### 1.2 Threat Models +### 1.3 Threat Models _False statements_ - Any information we receive may be false. diff --git a/docs/spec/ibc/queues.md b/docs/spec/ibc/packets.md similarity index 91% rename from docs/spec/ibc/queues.md rename to docs/spec/ibc/packets.md index a7a7c7b98f..40dbe2e58b 100644 --- a/docs/spec/ibc/queues.md +++ b/docs/spec/ibc/packets.md @@ -1,4 +1,4 @@ -## 3 Packet Queue +## 3 Packets ([Back to table of contents](README.md#contents)) @@ -6,7 +6,7 @@ IBC uses an asynchronous message passing model that makes no assumptions about n The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply (or not). Both chains must only agree that the packet has been received and either accepted or rejected, which is determined independently of any application logic. -To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./footnotes.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. IBC implements a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). +To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. IBC implements a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). Formally, given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: @@ -28,7 +28,21 @@ For example, an application may wish to allow a single fungible asset to be tran This section provides a high-level specification of the queue interface and a list of the necessary proofs. To implement wire-compatible IBC, chain _A_ and chain _B_ must also use a common encoding format. An example binary encoding format can be found in Appendix C. -### 3.1 Queue Specification +### 3.1 Definitions + +We introduce the abstraction of an IBC _connection_: a set of the required components to facilitate bidirectional communication between two blockchains _A_ and _B_. + +An IBC connection consists of four distinct queues, two on each chain: + +_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ + +_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ + +_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ + +_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ + +### 3.2 Requirements A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. @@ -65,22 +79,6 @@ Each IBC-supporting blockchain must implement a reliable ordered packet queue wi **tail** ⇒ **i** > return _qtail_ -### 3.2 Connection Abstraction - -We introduce the abstraction of an IBC **connection**: a set of the required components to facilitate bidirectional communication between two blockchains _A_ and _B_. - -An IBC connection consists of four distinct queues, two on each chain: - -_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ - -_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ - -_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ - -_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ - -### 3.3 Merkle Proofs for Queues - In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that particular IBC packets have been stored at particular indices in the outgoing packet queue, and particular IBC packet execution results have been stored at particular indices in the incoming packet queue. We use the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. In order to do so, we must define a unique, deterministic key in the Merkle store for each message in the queue: @@ -91,14 +89,9 @@ The index is stored as a fixed-length unsigned integer in big endian format, so Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. -The queue name must be unambiguously associated with a given connection to another chain, so an observer can prove if a message was intended for chain A or chain B. In order to do so, upon registration of a connection with a remote chain, we create two queues with different names (prefixes). +Each incoming & outgoing queue must be provably associated with another uniquely identified chain, so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with a string unique to the other chain, such as the chain identifier or the hash of the genesis block. -* _ibc::send_ - all outgoing packets destined to chain _A_ -* _ibc::receipt_ - the results of executing the packets received from chain _A_ - -These two queues have different purposes and store elements of different types. By parsing the key of a merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. - -### 3.4 Message Contents +These two queues have different purposes and store elements of different types. By parsing the key of a Merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. We define every message in a _send queue_ to consist of two fields: an enumerable _type_, and an opaque _payload_. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) @@ -106,7 +99,7 @@ _Vsend = (type, data)_ _Vreceipt = (result, [success|error code])_ -### 3.5 Sending a Message +### 3.3 Sending a packet { todo: cleanup wording } @@ -132,7 +125,7 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). -### 3.6 Receipts +### 3.4 Receiving a packet { todo: cleanup logic } @@ -157,7 +150,7 @@ This enforces that the receipts are processed in order, to allow some the applic ![Rejected Transaction](images/ReceiptError.png) -### 3.7 Relay Process +### 3.5 Packet relayer { todo: cleanup wording } diff --git a/docs/spec/ibc/footnotes.md b/docs/spec/ibc/references.md similarity index 100% rename from docs/spec/ibc/footnotes.md rename to docs/spec/ibc/references.md From eafa484184616d20a4b952a0d6f212fadb534b71 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 16:39:33 +0200 Subject: [PATCH 20/32] Fix a few links --- docs/spec/ibc/README.md | 8 ++++---- docs/spec/ibc/conclusion.md | 2 ++ docs/spec/ibc/references.md | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 0969346c6a..6835915516 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -4,17 +4,17 @@ This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. The IBC protocol defines a set of semantics for authenticated, strictly-ordered message passing between two blockchains with independent consensus algorithms. -IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically provable packets can be sent between the chains. - The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. -IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This documentation replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. +IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically verifiable packets can be sent between the chains. + +IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This document replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. ## Contents 1. **[Overview](overview.md)** 1. [Summary](overview.md#11-summary) - 1. [Requirements](overview.md#12-requirements) + 1. [Definitions](overview.md#12-definitions) 1. [Threat Models](overview.md#13-threat-models) 1. **[Connections](connections.md)** 1. [Definitions](connections.md#21-definitions) diff --git a/docs/spec/ibc/conclusion.md b/docs/spec/ibc/conclusion.md index 9e58c248ea..f85ae85994 100644 --- a/docs/spec/ibc/conclusion.md +++ b/docs/spec/ibc/conclusion.md @@ -1,5 +1,7 @@ ## 5 Conclusion +([Back to table of contents](README.md#contents)) + We have demonstrated a secure, performant, and flexible protocol for cross-blockchain messaging, and provided sufficient detail to reason about the correctness and efficiency of the protocol. This document defines solely a message queue protocol - not the application-level semantics which must sit on top of it to enable asset transfer between two chains. We will shortly release a separate paper on Cosmos IBC that defines the application logic used for direct value transfer as well as routing over the Cosmos hub. That paper builds upon the IBC protocol defined here and provides a first example of how to reason about application logic and global invariants in the context of IBC. diff --git a/docs/spec/ibc/references.md b/docs/spec/ibc/references.md index bb263fb635..f3677494cf 100644 --- a/docs/spec/ibc/references.md +++ b/docs/spec/ibc/references.md @@ -1,4 +1,6 @@ -## Footnotes +## References + +([Back to table of contents](README.md#contents)) ##### 1: [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) From edd5368669a9c46b3a2ad4845bdf3e217e518862 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 17 Apr 2018 18:23:26 +0200 Subject: [PATCH 21/32] Add the concept of a 'channel' --- docs/spec/ibc/README.md | 13 +++++++------ .../ibc/{packets.md => channels-and-packets.md} | 0 2 files changed, 7 insertions(+), 6 deletions(-) rename docs/spec/ibc/{packets.md => channels-and-packets.md} (100%) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 6835915516..6a15619d38 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -23,12 +23,13 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. [Opening a connection](connections.md#231-opening-a-connection) 1. [Following block headers](connections.md#232-following-block-headers) 1. [Closing a connection](connections.md#233-closing-a-connection) -1. **[Packets](packets.md)** - 1. [Definitions](packets.md#31-definitions) - 1. [Requirements](packets.md#32-requirements) - 1. [Sending a packet](packets.md#33-sending-a-packet) - 1. [Receiving a packet](packets.md#34-receiving-a-packet) - 1. [Packet relayer](packets.md#35-packet-relayer) +1. **[Channels & Packets](channels-and-packets.md)** + 1. [Definitions](channels-and-packets.md#31-definitions) + 1. [Requirements](channels-and-packets.md#32-requirements) + 1. [Channels](channels-and-packets.md#33-channels) + 1. [Sending a packet](channels-and-packets.md#34-sending-a-packet) + 1. [Receiving a packet](channels-and-packets.md#35-receiving-a-packet) + 1. [Packet relayer](channels-and-packets.md#36-packet-relayer) 1. **[Optimizations](optimizations.md)** 1. [Timeouts](optimizations.md#41-timeouts) 1. [Cleanup](optimizations.md#42-cleanup) diff --git a/docs/spec/ibc/packets.md b/docs/spec/ibc/channels-and-packets.md similarity index 100% rename from docs/spec/ibc/packets.md rename to docs/spec/ibc/channels-and-packets.md From 2493332509707f673930746bab6d43b5e017d1f5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 18 Apr 2018 13:22:14 +0200 Subject: [PATCH 22/32] Define IBC packet, IBC channel --- docs/spec/ibc/README.md | 6 +-- docs/spec/ibc/channels-and-packets.md | 60 ++++++++++++++++++--------- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 6a15619d38..e84c055e69 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -24,9 +24,9 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. [Following block headers](connections.md#232-following-block-headers) 1. [Closing a connection](connections.md#233-closing-a-connection) 1. **[Channels & Packets](channels-and-packets.md)** - 1. [Definitions](channels-and-packets.md#31-definitions) - 1. [Requirements](channels-and-packets.md#32-requirements) - 1. [Channels](channels-and-packets.md#33-channels) + 1. [Background](channels-and-packets.md#31-background) + 1. [Definitions](channels-and-packets.md#32-definitions) + 1. [Requirements](channels-and-packets.md#33-requirements) 1. [Sending a packet](channels-and-packets.md#34-sending-a-packet) 1. [Receiving a packet](channels-and-packets.md#35-receiving-a-packet) 1. [Packet relayer](channels-and-packets.md#36-packet-relayer) diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 40dbe2e58b..f503372195 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -1,14 +1,16 @@ -## 3 Packets +## 3 Channels & Packets ([Back to table of contents](README.md#contents)) -IBC uses an asynchronous message passing model that makes no assumptions about network synchrony. Chain _A_ and chain _B_ confirm new blocks independently, and IBC packets from one chain to the other may be delayed or censored arbitrarily. The speed of the IBC packet queue is limited only by the speed of the underlying chains. +### 3.1 Background -The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply (or not). Both chains must only agree that the packet has been received and either accepted or rejected, which is determined independently of any application logic. +IBC uses an asynchronous message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain _A_ and chain _B_ confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. -To facilitate building useful application logic, we introduce a reliable messaging queue (hereafter just referred to as a queue) to allow us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. IBC implements a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). +The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received and either accepted or rejected. -Formally, given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: +To facilitate useful application logic, we introduce an IBC *channel*: a set of reliable messaging queues that allows us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. + +IBC channels implement a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: _A:send(msgi )_ → _B:receive(msgi )_ @@ -24,29 +26,31 @@ _y_ → _A:receipt(msgi )_ Every transaction on the same chain already has a well-defined causality relation (order in history). IBC provides an ordering guarantee across two chains which can be used to reason about the combined state of both chains as a whole. -For example, an application may wish to allow a single fungible asset to be transferred between and held on multiple blockchains while preserving conservation of supply. The application can mint asset vouchers on chain _B_ when a particular IBC packet is committed to chain _B_, and require outgoing sends of that packet on chain _A_ to escrow an equal amount of the asset on chain _A_ until the vouchers are later redeemed back to chain _A_ with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain _B_ can later be redeemed back to chain _A_. +For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain _B_ when a particular IBC packet is committed to chain _B_, and require outgoing sends of that packet on chain _A_ to escrow an equal amount of the asset on chain _A_ until the vouchers are later redeemed back to chain _A_ with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain _B_ can later be redeemed back to chain _A_. -This section provides a high-level specification of the queue interface and a list of the necessary proofs. To implement wire-compatible IBC, chain _A_ and chain _B_ must also use a common encoding format. An example binary encoding format can be found in Appendix C. +This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain _A_ and chain _B_ must also use a common encoding format. An example binary encoding format can be found in Appendix C. -### 3.1 Definitions +### 3.2 Definitions -We introduce the abstraction of an IBC _connection_: a set of the required components to facilitate bidirectional communication between two blockchains _A_ and _B_. +#### 3.1.1 Packet -An IBC connection consists of four distinct queues, two on each chain: +We define an IBC *packet* as the five-tuple *(type, sequence, source, destination, data|receipt)*, where: -_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ +**type** is one of *data* or *receipt* -_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ +**sequence** is an unsigned integer -_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ +**source** is a string uniquely identifying the chain from which this packet was sent -_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ +**destination** is a string uniquely identifying the chain which should receive this packet -### 3.2 Requirements +**data|receipt**, if *type* is *data*, is an opaque application payload, or if *type* is *receipt* is a code of either *success* or *failure* -A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. +#### 3.1.2 Queue -Each IBC-supporting blockchain must implement a reliable ordered packet queue with the following interface specification: +To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. + +Each IBC-supporting blockchain must provide a queue abstraction with the following functionality: **init** > set _qhead_ = _0_ @@ -79,6 +83,22 @@ Each IBC-supporting blockchain must implement a reliable ordered packet queue wi **tail** ⇒ **i** > return _qtail_ +#### 3.1.3 Channel + +We introduce the abstraction of an IBC _channel_: a set of the required packet queues to facilitate ordered bidirectional communication between two blockchains _A_ and _B_. An IBC connection, as defined earlier, can have any number of associated channels. IBC connections handle header initialization & updates. All IBC channels use the same connection, but implement independent queues, so have independent ordering guarantees. + +An IBC channel consists of four distinct queues, two on each chain: + +_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ + +_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ + +_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ + +_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ + +### 3.3 Requirements + In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that particular IBC packets have been stored at particular indices in the outgoing packet queue, and particular IBC packet execution results have been stored at particular indices in the incoming packet queue. We use the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. In order to do so, we must define a unique, deterministic key in the Merkle store for each message in the queue: @@ -99,7 +119,7 @@ _Vsend = (type, data)_ _Vreceipt = (result, [success|error code])_ -### 3.3 Sending a packet +### 3.4 Sending a packet { todo: cleanup wording } @@ -125,7 +145,7 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). -### 3.4 Receiving a packet +### 3.5 Receiving a packet { todo: cleanup logic } @@ -150,7 +170,7 @@ This enforces that the receipts are processed in order, to allow some the applic ![Rejected Transaction](images/ReceiptError.png) -### 3.5 Packet relayer +### 3.6 Packet relayer { todo: cleanup wording } From f9e8018d434884467b86a6c7844644f2ddba520b Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 18 Apr 2018 14:35:20 +0200 Subject: [PATCH 23/32] Add receipt definition --- docs/spec/ibc/README.md | 4 ++ docs/spec/ibc/channels-and-packets.md | 58 ++++++++++++++------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index e84c055e69..9427646833 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -26,6 +26,10 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. **[Channels & Packets](channels-and-packets.md)** 1. [Background](channels-and-packets.md#31-background) 1. [Definitions](channels-and-packets.md#32-definitions) + 1. [Packet](channels-and-packets.md#321-packet) + 1. [Receipt](channels-and-packets.md#322-receipt) + 1. [Queue](channels-and-packets.md#323-queue) + 1. [Channel](channels-and-packets.md#324-channel) 1. [Requirements](channels-and-packets.md#33-requirements) 1. [Sending a packet](channels-and-packets.md#34-sending-a-packet) 1. [Receiving a packet](channels-and-packets.md#35-receiving-a-packet) diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index f503372195..aa4dde58aa 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -4,7 +4,7 @@ ### 3.1 Background -IBC uses an asynchronous message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain _A_ and chain _B_ confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. +IBC uses a cross-chain message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain _A_ and chain _B_ confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received and either accepted or rejected. @@ -32,21 +32,33 @@ This section provides definitions for packets and channels, a high-level specifi ### 3.2 Definitions -#### 3.1.1 Packet +#### 3.2.1 Packet -We define an IBC *packet* as the five-tuple *(type, sequence, source, destination, data|receipt)*, where: +We define an IBC *packet* _P_ as the five-tuple *(type, sequence, source, destination, data)*, where: -**type** is one of *data* or *receipt* +**type** is an opaque routing field (an integer or string) -**sequence** is an unsigned integer +**sequence** is an unsigned, arbitrary-precision integer -**source** is a string uniquely identifying the chain from which this packet was sent +**source** is a string uniquely identifying the chain, connection, and channel from which this packet was sent -**destination** is a string uniquely identifying the chain which should receive this packet +**destination** is a string uniquely identifying the chain, connection, and channel which should receive this packet -**data|receipt**, if *type* is *data*, is an opaque application payload, or if *type* is *receipt* is a code of either *success* or *failure* +**data** is an opaque application payload -#### 3.1.2 Queue +#### 3.2.2 Receipt + +We define an IBC *receipt* _R_ as the four-tuple *(sequence, source, destination, result)*, where + +**sequence** is an unsigned, arbitrary-precision integer + +**source** is a string uniquely identifying the chain, connection, and channel from which this packet was sent + +**destination** is a string uniquely identifying the chain, connection, and channel which should receive this packet + +**result** is a code of either *success* or *failure* + +#### 3.2.3 Queue To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. @@ -83,9 +95,9 @@ Each IBC-supporting blockchain must provide a queue abstraction with the followi **tail** ⇒ **i** > return _qtail_ -#### 3.1.3 Channel +#### 3.2.4 Channel -We introduce the abstraction of an IBC _channel_: a set of the required packet queues to facilitate ordered bidirectional communication between two blockchains _A_ and _B_. An IBC connection, as defined earlier, can have any number of associated channels. IBC connections handle header initialization & updates. All IBC channels use the same connection, but implement independent queues, so have independent ordering guarantees. +We introduce the abstraction of an IBC _channel_: a set of the required packet queues to facilitate ordered bidirectional communication between two blockchains _A_ and _B_. An IBC connection, as defined earlier, can have any number of associated channels. IBC connections handle header initialization & updates. All IBC channels use the same connection, but implement independent queues and thus independent ordering guarantees. An IBC channel consists of four distinct queues, two on each chain: @@ -109,29 +121,19 @@ The index is stored as a fixed-length unsigned integer in big endian format, so Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. -Each incoming & outgoing queue must be provably associated with another uniquely identified chain, so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with a string unique to the other chain, such as the chain identifier or the hash of the genesis block. - -These two queues have different purposes and store elements of different types. By parsing the key of a Merkle proof, a recipient can uniquely identify which queue, if any, this message belongs to. We now define _k =_ _(remote id, [send|receipt], index)_. This tuple is used to route and verify every message, before the contents of the packet are processed by the appropriate application logic. - -We define every message in a _send queue_ to consist of two fields: an enumerable _type_, and an opaque _payload_. The IBC protocol relies on the type for routing, and lets the appropriate module process the data as it sees fit. The _receipt queue_ stores if it was an error, an optional error code, and an optional return value. We use the same index as the received message, so that the results of _A:qB.send[i]_ are stored at _B:qA.receipt[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) - -_Vsend = (type, data)_ - -_Vreceipt = (result, [success|error code])_ +Each incoming & outgoing queue for each connection must be provably associated with another uniquely identified chain, so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with a string unique to the other chain, such as the chain identifier or the hash of the genesis block. ### 3.4 Sending a packet -{ todo: cleanup wording } +To send an IBC packet, an application module on the source chain must call the send method of the IBC module, providing a packet as defined above. The IBC module must ensure that the destination chain was already properly registered and that the calling module has permission to write this packet. If all is in order, the IBC module simply pushes the packet to the tail of _OutgoingA_, which enables all the proofs described above. -A proper implementation of IBC requires all relevant state to be encapsulated, so that other modules can only interact with it via a fixed API (to be defined in the next sections) rather than directly mutating internal state. This allows the IBC module to provide security guarantees. - -Sending an IBC packet involves an application module calling the send method of the IBC module with a packet and a destination chain id. The IBC module must ensure that the destination chain was already properly registered, and that the calling module has permission to write this packet. If so, the IBC module simply pushes the packet to the tail of the _send queue_, which enables all the proofs described above. - -The permissioning of which module can write which packet can be defined per type, so this module can maintain any application-level invariants related to this area. Thus, the "coin" module can maintain the constant supply of tokens, while another module can maintain its own invariants, without IBC messages providing a means to escape their encapsulations. The IBC module must associate every supported message type with a particular handler (_ftype_) and return an error for unsupported types. +If desired, the packet payload can contain additional module routing information in the form of a _kind_, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module must associate every supported message with a particular handler (_fkind_) and return an error for unsupported types. _(IBCsend(D, type, data)_ ⇒ _Success)_ ⇒ _push(qD.send ,Vsend{type, data})_ +### 3.5 Receiving a packet + We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ @@ -145,7 +147,7 @@ _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). -### 3.5 Receiving a packet +### 3.6 Handling a receipt { todo: cleanup logic } @@ -170,7 +172,7 @@ This enforces that the receipts are processed in order, to allow some the applic ![Rejected Transaction](images/ReceiptError.png) -### 3.6 Packet relayer +### 3.7 Packet relayer { todo: cleanup wording } From 773c87e8c0c20c610d55c8b51b4560b0286e2cff Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 19 Apr 2018 16:51:27 +0200 Subject: [PATCH 24/32] Add section on closing IBC connections --- docs/spec/ibc/connections.md | 10 ++++++++-- docs/spec/ibc/references.md | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/spec/ibc/connections.md b/docs/spec/ibc/connections.md index 248a9e771a..3a1c2efc05 100644 --- a/docs/spec/ibc/connections.md +++ b/docs/spec/ibc/connections.md @@ -38,7 +38,9 @@ _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ All proofs require an initial _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. -Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed by on-chain governance or a similar decentralized mechanism if desired. Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) is necessary before any IBC packets can be sent. +Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) — _HAh_ and _CAh_ stored on chain _B_, and _HBh_ and _CBhh | Uh )_ as defined above to always return _false_. + +Closing a connection may break application invariants (such as fungiblity - token vouchers on chain _B_ will no longer be redeemable for tokens on chain _A_) and should only be undertaken in extreme circumstances such as Byzantine behavior of the connected chain. + +Closure may be permissioned to an on-chain governance system, an identifiable party on the other chain (such as a signer quorum, although this will not work in some Byzantine cases), or any user who submits a connection-specific fraud proof of Byzantine behavior. When a connection is closed, application-specific measures may be undertaken to recover assets held on a Byzantine chain. We defer further discussion to { an appendix }. diff --git a/docs/spec/ibc/references.md b/docs/spec/ibc/references.md index f3677494cf..9262ff99cf 100644 --- a/docs/spec/ibc/references.md +++ b/docs/spec/ibc/references.md @@ -5,6 +5,9 @@ ##### 1: [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) +##### 2: +{ vector clock } + ##### 3: [https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) From 00489162ac4bf7c0ed5c5229bec30ef3895d9f7a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 20 Apr 2018 13:28:17 +0200 Subject: [PATCH 25/32] Clarify connections intro, Byzantine recovery strategies --- docs/spec/ibc/README.md | 9 +++++---- docs/spec/ibc/appendices.md | 10 ++++++++-- docs/spec/ibc/channels-and-packets.md | 8 ++++++-- docs/spec/ibc/connections.md | 10 ++++++---- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 9427646833..eca34c80d3 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -6,9 +6,9 @@ This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. T The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. -IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically verifiable packets can be sent between the chains. +IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically verifiable packets can be sent between them. -IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This document replaces and supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation, including example pseudocode. +IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc), and later described in more detail by the [IBC specification paper](https://github.com/cosmos/ibc/raw/master/CosmosIBCSpecification.pdf). This document supersedes both. It explains the requirements and structure of the protocol and provides sufficient detail for both analysis and implementation. ## Contents @@ -44,5 +44,6 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. [Appendix A: Encoding Libraries](appendices.md#appendix-a-encoding-libraries) 1. [Appendix B: IBC Queue Format](appendices.md#appendix-b-ibc-queue-format) 1. [Appendix C: Merkle Proof Format](appendices.md#appendix-c-merkle-proof-formats) - 1. [Appendix D: Universal IBC Packets](appendices.md#appendix-d-universal-ibc-packets) - 1. [Appendix E: Tendermint Header Proofs](appendices.md#appendix-e-tendermint-header-proofs) + 1. [Appendix D: Byzantine Recovery Strategies](appendices.md#appendix-d-byzantine-recovery-strategies) + 1. [Appendix E: Universal IBC Packets](appendices.md#appendix-e-universal-ibc-packets) + 1. [Appendix F: Tendermint Header Proofs](appendices.md#appendix-f-tendermint-header-proofs) diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 96feed7091..736167d2ae 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -48,7 +48,13 @@ For those who wish to minimize the size of their merkle proofs, we recommend usi See [binary format as protobuf specification](./protobuf/merkle.proto) -## Appendix D: Universal IBC Packets +## Appendix D: Byzantine Recovery Strategies + +- Goal: keep application invariants +- Plasma-like fraud proofs +- Trusted entity - governance + +## Appendix E: Universal IBC Packets { what is this } @@ -56,7 +62,7 @@ The structures above can be used to define standard encodings for the basic IBC See [binary format as protobuf specification](./protobuf/messages.proto) -## Appendix E: Tendermint Header Proofs +## Appendix F: Tendermint Header Proofs { is this finalized? } diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index aa4dde58aa..112f220afa 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -10,7 +10,7 @@ The IBC protocol as defined here is payload-agnostic. The packet receiver on cha To facilitate useful application logic, we introduce an IBC *channel*: a set of reliable messaging queues that allows us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. -IBC channels implement a [vector clock](https://en.wikipedia.org/wiki/Vector_clock) for the restricted case of two processes (in our case, blockchains). Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: +IBC channels implement a vector clock [2](references.md#2) for the restricted case of two processes (in our case, blockchains). Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: _A:send(msgi )_ → _B:receive(msgi )_ @@ -125,6 +125,8 @@ Each incoming & outgoing queue for each connection must be provably associated w ### 3.4 Sending a packet +{ todo: unify terms, clarify } + To send an IBC packet, an application module on the source chain must call the send method of the IBC module, providing a packet as defined above. The IBC module must ensure that the destination chain was already properly registered and that the calling module has permission to write this packet. If all is in order, the IBC module simply pushes the packet to the tail of _OutgoingA_, which enables all the proofs described above. If desired, the packet payload can contain additional module routing information in the form of a _kind_, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module must associate every supported message with a particular handler (_fkind_) and return an error for unsupported types. @@ -134,6 +136,8 @@ _(IBCsend(D, type, data)_ ⇒ _Success)_ ### 3.5 Receiving a packet +{ todo: unify terms } + We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: _A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ @@ -174,7 +178,7 @@ This enforces that the receipts are processed in order, to allow some the applic ### 3.7 Packet relayer -{ todo: cleanup wording } +{ todo: cleanup wording & terms } The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. diff --git a/docs/spec/ibc/connections.md b/docs/spec/ibc/connections.md index 3a1c2efc05..94993b59d8 100644 --- a/docs/spec/ibc/connections.md +++ b/docs/spec/ibc/connections.md @@ -2,7 +2,9 @@ ([Back to table of contents](README.md#contents)) -The basis of IBC is the ability to verify in the on-chain consensus ruleset of chain _B_ that a message packet received on chain _B_ was correctly generated on chain _A_. This establishes a cross-chain linearity guarantee: upon validation of that packet on chain _B_ we know that the packet has been executed on chain _A_ and any associated logic resolved (such as assets being escrowed), and we can safely perform application logic on chain _B_ (such as generating vouchers on chain _B_ for the chain _A_ assets which can later be redeemed with a packet in the opposite direction). +The basis of IBC is the ability to verify in the on-chain consensus ruleset of chain _B_ that a data packet received on chain _B_ was correctly generated on chain _A_. This establishes a cross-chain linearity guarantee: upon validation of that packet on chain _B_ we know that the packet has been executed on chain _A_ and any associated logic resolved (such as assets being escrowed), and we can safely perform application logic on chain _B_ (such as generating vouchers on chain _B_ for the chain _A_ assets which can later be redeemed with a packet in the opposite direction). + +This section outlines the abstraction of an IBC _connection_: the state and consensus ruleset necessary to perform IBC packet verification. ### 2.1 Definitions @@ -38,9 +40,9 @@ _valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ All proofs require an initial _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. -Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) — _HAh_ and _CAh_ stored on chain _B_, and _HBh_ and _CBhh_ and _CAh_ stored on chain _B_, and _HBh_ and _CBh_ stored on chain _A_ — is necessary before any IBC packets can be sent. -Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed permissionlessly, in which case users later utilizing the IBC channel must check the root-of-trust themselves, or authorized by on-chain governance. +Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed permissionlessly, in which case users later utilizing the IBC channel must check the root-of-trust themselves, or authorized by on-chain governance for additional assurance. #### 2.3.2 Following Block Headers @@ -79,4 +81,4 @@ IBC implementations may optionally include the ability to close an IBC connectio Closing a connection may break application invariants (such as fungiblity - token vouchers on chain _B_ will no longer be redeemable for tokens on chain _A_) and should only be undertaken in extreme circumstances such as Byzantine behavior of the connected chain. -Closure may be permissioned to an on-chain governance system, an identifiable party on the other chain (such as a signer quorum, although this will not work in some Byzantine cases), or any user who submits a connection-specific fraud proof of Byzantine behavior. When a connection is closed, application-specific measures may be undertaken to recover assets held on a Byzantine chain. We defer further discussion to { an appendix }. +Closure may be permissioned to an on-chain governance system, an identifiable party on the other chain (such as a signer quorum, although this will not work in some Byzantine cases), or any user who submits an application-specific fraud proof. When a connection is closed, application-specific measures may be undertaken to recover assets held on a Byzantine chain. We defer further discussion to [Appendix D](appendices.md#appendix-d-byzantine-recovery-strategies). From 37fd166829bf65ff17bb428b4f4198b108c73c39 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 20 Apr 2018 19:22:00 +0200 Subject: [PATCH 26/32] Formatting updates --- docs/spec/ibc/channels-and-packets.md | 219 ++++++++++++++++---------- docs/spec/ibc/connections.md | 93 ++++++----- docs/spec/ibc/overview.md | 16 +- 3 files changed, 192 insertions(+), 136 deletions(-) diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 112f220afa..128a0c01ac 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -4,173 +4,216 @@ ### 3.1 Background -IBC uses a cross-chain message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain _A_ and chain _B_ confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. +IBC uses a cross-chain message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain `A` and chain `B` confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. -The IBC protocol as defined here is payload-agnostic. The packet receiver on chain _B_ decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received and either accepted or rejected. +The IBC protocol as defined here is payload-agnostic. The packet receiver on chain `B` decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received and either accepted or rejected. -To facilitate useful application logic, we introduce an IBC *channel*: a set of reliable messaging queues that allows us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet _x_ is processed before packet _y_ on chain _A_, packet _x_ must also be processed before packet _y_ on chain _B_. +To facilitate useful application logic, we introduce an IBC *channel*: a set of reliable messaging queues that allows us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet *x* is processed before packet *y* on chain `A`, packet *x* must also be processed before packet *y* on chain `B`. -IBC channels implement a vector clock [2](references.md#2) for the restricted case of two processes (in our case, blockchains). Given _x_ → _y_ means _x_ is causally before _y_, and chains A and B, and _a_ ⇒ _b_ means _a_ implies _b_: +IBC channels implement a vector clock[[2](references.md#2)] for the restricted case of two processes (in our case, blockchains). Given *x* -> *y* means *x* is causally before *y*, chains `A` and `B`, and *a* => *b* means *a* implies *b*: -_A:send(msgi )_ → _B:receive(msgi )_ +*A:send(msgi )* -> *B:receive(msgi )* -_B:receive(msgi )_ → _A:receipt(msgi )_ +*B:receive(msgi )* -> *A:receipt(msgi )* -_A:send(msgi )_ → _A:send(msgi+1 )_ +*A:send(msgi )* -> *A:send(msgi+1 )* -_x_ → _A:send(msgi )_ ⇒ -_x_ → _B:receive(msgi )_ +*x* -> *A:send(msgi )* => +*x* -> *B:receive(msgi )* -_y_ → _B:receive(msgi )_ ⇒ -_y_ → _A:receipt(msgi )_ +*y* -> *B:receive(msgi )* => +*y* -> *A:receipt(msgi )* Every transaction on the same chain already has a well-defined causality relation (order in history). IBC provides an ordering guarantee across two chains which can be used to reason about the combined state of both chains as a whole. -For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain _B_ when a particular IBC packet is committed to chain _B_, and require outgoing sends of that packet on chain _A_ to escrow an equal amount of the asset on chain _A_ until the vouchers are later redeemed back to chain _A_ with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain _B_ can later be redeemed back to chain _A_. +For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain `B` when a particular IBC packet is committed to chain `B`, and require outgoing sends of that packet on chain `A` to escrow an equal amount of the asset on chain `A` until the vouchers are later redeemed back to chain `A` with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain `B` can later be redeemed back to chain `A`. -This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain _A_ and chain _B_ must also use a common encoding format. An example binary encoding format can be found in Appendix C. +This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-format). ### 3.2 Definitions #### 3.2.1 Packet -We define an IBC *packet* _P_ as the five-tuple *(type, sequence, source, destination, data)*, where: +We define an IBC *packet* `P` as the five-tuple `(type, sequence, source, destination, data)`, where: -**type** is an opaque routing field (an integer or string) +`type` is an opaque routing field -**sequence** is an unsigned, arbitrary-precision integer +`sequence` is an unsigned, arbitrary-precision integer -**source** is a string uniquely identifying the chain, connection, and channel from which this packet was sent +`source` is a string uniquely identifying the chain, connection, and channel from which this packet was sent -**destination** is a string uniquely identifying the chain, connection, and channel which should receive this packet +`destination` is a string uniquely identifying the chain, connection, and channel which should receive this packet -**data** is an opaque application payload +`data` is an opaque application payload #### 3.2.2 Receipt -We define an IBC *receipt* _R_ as the four-tuple *(sequence, source, destination, result)*, where +We define an IBC *receipt* `R` as the four-tuple `(sequence, source, destination, result)`, where -**sequence** is an unsigned, arbitrary-precision integer +`sequence` is an unsigned, arbitrary-precision integer -**source** is a string uniquely identifying the chain, connection, and channel from which this packet was sent +`source` is a string uniquely identifying the chain, connection, and channel from which this packet was sent -**destination** is a string uniquely identifying the chain, connection, and channel which should receive this packet +`destination` is a string uniquely identifying the chain, connection, and channel which should receive this packet -**result** is a code of either *success* or *failure* +`result` is a code of either `success` or `failure` #### 3.2.3 Queue -To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - _qhead_ and _qtail_ - bound the slice, such that for every _index_ where _head <= index < tail_, there is a queue element _q[qindex]_. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, _advance_, to facilitate efficient queue cleanup. +To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - `q_head` and `q_tail` - bound the slice, such that for every `index` where `q_head <= index < q_tail`, there is a queue element `q[q_index]`. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, `advance`, to facilitate efficient queue cleanup. Each IBC-supporting blockchain must provide a queue abstraction with the following functionality: -**init** -> set _qhead_ = _0_ -> set _qtail_ = _0_ +`init` -**peek** ⇒ **e** -> match _qhead == qtail_ with -> _true_ ⇒ return _nil_ -> _false_ ⇒ return _q[qhead]_ +``` +set q_head = 0 +set q_tail = 0 +``` -**pop** ⇒ **e** -> match _qhead == qtail_ with -> _true_ ⇒ return _nil_ -> _false_ ⇒ set _qhead_ = _qhead + 1_; return _q[qhead-1]_ +`peek => e` -**retrieve(i)** ⇒ **e** -> match _qhead <= i < qtail_ with -> _true_ ⇒ return _qi_ -> _false_ ⇒ return _nil_ +``` +match q_head == q_tail with + true => return nil + false => + return q[q_head] +``` -**push(e)** -> set _q[qtail]_ = _e_; set _qtail_ = _qtail + 1_ +`pop => e` -**advance(i)** -> set _qhead_ = _i_; set _qtail_ = _max(qtail, i)_ +``` +match q_head == q_tail with + true => return nil + false => + set q_head = q_head + 1 + return q_head - 1 +``` -**head** ⇒ **i** -> return _qhead_ +`retrieve(i) => e` -**tail** ⇒ **i** -> return _qtail_ +``` +match q_head <= i < q_tail with + true => return q[i] + false => return nil +``` + +`push(e)` + +``` +set q[q_tail] = e +set q_tail = q_tail + 1 +``` + +`advance(i)` + +``` +set q_head = i +set q_tail = max(q_tail, i) +``` + +`head => i` + +``` +return q_head +``` + +`tail => i` + +``` +return q_tail +``` #### 3.2.4 Channel -We introduce the abstraction of an IBC _channel_: a set of the required packet queues to facilitate ordered bidirectional communication between two blockchains _A_ and _B_. An IBC connection, as defined earlier, can have any number of associated channels. IBC connections handle header initialization & updates. All IBC channels use the same connection, but implement independent queues and thus independent ordering guarantees. +We introduce the abstraction of an IBC *channel*: a set of the required packet queues to facilitate ordered bidirectional communication between two blockchains `A` and `B`. An IBC connection, as defined earlier, can have any number of associated channels. IBC connections handle header initialization & updates. All IBC channels use the same connection, but implement independent queues and thus independent ordering guarantees. An IBC channel consists of four distinct queues, two on each chain: -_OutgoingA_: Outgoing IBC packets from chain _A_ to chain _B_, stored on chain _A_ +`outgoing_A`: Outgoing IBC packets from chain `A` to chain `B`, stored on chain `A` -_IncomingA_: Execution logs for incoming IBC packets from chain _B_, stored on chain _A_ +`incoming_A`: IBC receipts (execution logs) for incoming IBC packets from chain `B`, stored on chain `A` -_OutgoingB_: Outgoing IBC packets from chain _B_ to chain _A_, stored on chain _B_ +`outgoing_B`: Outgoing IBC packets from chain `B` to chain `A`, stored on chain `B` -_IncomingB_: Execution logs for incoming IBC packets from chain _A_, stored on chain _B_ +`incoming_B`: IBC receipts (execution logs) for incoming IBC packets from chain `A`, stored on chain `B` ### 3.3 Requirements In order to provide the ordering guarantees specified above, each blockchain utilizing the IBC protocol must provide proofs that particular IBC packets have been stored at particular indices in the outgoing packet queue, and particular IBC packet execution results have been stored at particular indices in the incoming packet queue. -We use the previously-defined Merkle proof _Mk,v,h_ to provide the requisite proofs. In order to do so, we must define a unique, deterministic key in the Merkle store for each message in the queue: +We use the previously-defined Merkle proof `M_kvh` to provide the requisite proofs. In order to do so, we must define a unique, deterministic key in the Merkle store for each message in the queue: -**key**: _(queue name, [head|tail|index])_ +`key = (queue name, head | tail | index)` -The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. _head_ and _tail_ are two special constants that store an integer index, and are chosen such that their serializated representation cannot collide with that of any possible index. +The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. `head` and `tail` are two special constants that store an integer index, and are chosen such that their serializated representation cannot collide with that of any possible index. -Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value _v_ is written to a queue, then every valid proof _Mk,v,h _ must refer to the same _v_. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. +Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value `v` is written to a queue, then every valid proof `M_kvh` must refer to the same `v`. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. -Each incoming & outgoing queue for each connection must be provably associated with another uniquely identified chain, so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with a string unique to the other chain, such as the chain identifier or the hash of the genesis block. +Each incoming & outgoing queue for each connection must be provably associated with another uniquely identified chain, connection, and channel so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with strings unique to the chain (such as chain identifier), connection, and channel. ### 3.4 Sending a packet { todo: unify terms, clarify } -To send an IBC packet, an application module on the source chain must call the send method of the IBC module, providing a packet as defined above. The IBC module must ensure that the destination chain was already properly registered and that the calling module has permission to write this packet. If all is in order, the IBC module simply pushes the packet to the tail of _OutgoingA_, which enables all the proofs described above. +To send an IBC packet, an application module on the source chain must call the send method of the IBC module, providing a packet as defined above. The IBC module must ensure that the destination chain was already properly registered and that the calling module has permission to write this packet. If all is in order, the IBC module simply pushes the packet to the tail of `outgoing_a`, which enables all the proofs described above. -If desired, the packet payload can contain additional module routing information in the form of a _kind_, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module must associate every supported message with a particular handler (_fkind_) and return an error for unsupported types. +The packet must provide routing information in the `type` field, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module on the destination chain must associate every supported packet type with a particular handler (`f_type`) and return an error for unsupported types. -_(IBCsend(D, type, data)_ ⇒ _Success)_ - ⇒ _push(qD.send ,Vsend{type, data})_ +`send(P{type, sequence, source, destination, data})` + +``` +match source == (A, connection, channel) and sequence == tail(outgoing_A) with + true => push(outgoing_A, P); success + false => fail with "wrong sender" +``` + +Note that the `sequence`, `source`, and `destination` can all be encoded in the Merkle tree key for the channel and do not need to be stored for each packet. ### 3.5 Receiving a packet -{ todo: unify terms } +Upon packet receipt, chain `B` must check that the packet is valid, that it was intended for the destination, and that all previous packets have been processed. `receive` must write the receipt queue upon accepting a valid packet, even if the handler execution returned an error, so that future packets can be processed. -We also consider how a given blockchain _A_ is expected to receive the packet from a source chain _S_ with a merkle proof, given the current set of trusted headers for that chain, _TS_: +To receive an IBC packet on blockchain `B` from a source chain `A`, with a Merkle proof `M_kvh` and the current set of trusted headers for that chain `T_A`: -_A:IBCreceive(S, Mk,v,h)_ ⇒ _match_ - * _qS.receipt =_ ∅ ⇒ _Error("unregistered sender"),_ - * _k = (\_, reciept, \_)_ ⇒ _Error("must be a send"),_ - * _k = (d, \_, \_) and d_ ≠ _A_ ⇒ _Error("sent to a different chain"),_ - * _k = (\_, send, i) and head(qS.receipt)_ ≠ _i_ ⇒ _Error("out of order"),_ - * _Hh_ ∉ _TS_ ⇒ _Error("must submit header for height h"),_ - * _valid(Hh ,Mk,v,h ) = false_ ⇒ _Error("invalid merkle proof"),_ - * _v = (type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err)); Success_ +`receive(P{type, sequence, source, destination, data}, M_kvh)` -Note that this requires not only an valid proof, but also that the proper header as well as all prior messages were previously submitted. This returns success upon accepting a proper message, even if the message execution returned an error (which must then be relayed to the sender). +``` +case + incoming_B == nil => fail with "unregistered sender" + destination /= (B, connection, channel) => fail with "wrong destination" + sequence /= head(Incoming_B) => fail with "out of order" + H_h not in T_A => fail with "must submit header for height h" + valid(H_h, M_kvh) == false => fail with "invalid Merkle proof" + otherwise => + set (result, error) = f_type(data) + push(incoming_B, R{}) +``` + +{ todo: check that v + k == packet, clearly define connection / channel } ### 3.6 Handling a receipt { todo: cleanup logic } -When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain A to Bob on chain B, chain A must decrement Alice's account _if and only if_ Bob's account was incremented on chain B. We can achieve that by storing a protected intermediate state on chain A, which is then committed or rolled back based on the result of executing the transaction on chain B. +When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain `A` to Bob on chain `B`, chain `A` must decrement Alice's account *if and only if* Bob's account was incremented on chain `B`. We can achieve that by storing a protected intermediate state on chain `A` (escrowing the assets in question), which is then committed or rolled back based on the result of executing the transaction on chain `B`. -To do this requires that we not only provable send a message from chain A to chain B, but provably return the result of that message (the receipt) from chain B to chain A. As one noticed above in the implementation of _IBCreceive_, if the valid IBC message was sent from A to B, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: +To do this requires that we not only provably send a message from chain `A` to chain `B`, but provably return the result of that message (the receipt) from chain `B` to chain `A`. As one noticed above in the implementation of `receive`, if the valid IBC message was sent from `A` to `B`, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: -_S:IBCreceipt(A, Mk,v,h)_ ⇒ _match_ - * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ - * _k = (\_, send, \_)_ ⇒ _Error("must be a recipient"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ - * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ - * _k = (\_, receipt, head|tail)_ ⇒ _Error("only accepts message proofs"),_ - * _k = (\_, receipt, i) and head(qS.send)_ ≠ _i_ ⇒ _Error("out of order"),_ +`handle_receipt(A, M_kvh)` + +``` +case + outgoing_A == nil => fail with "unregistered sender" + destination /= (A, connection, channel) => fail with "wrong destination" + sequence /= head(incoming_A) => fail with "out of order" + H_h not in T_B => fail with "must submit header for height h" + valid(H_h, M_kvh) == false => fail with "invalid Merkle proof" * _v = (\_, error)_ ⇒ _(type, data) := pop(qS.send ); rollbacktype(data); Success_ * _v = (res, success)_ ⇒ _(type, data) := pop(qS.send ); committype(data, res); Success_ +``` -This enforces that the receipts are processed in order, to allow some the application to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and there is no more need to store this information. +This enforces that the receipts are processed in order, to allow applications to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and no need to store it. ![Successful Transaction](images/Receipts.png) @@ -180,11 +223,13 @@ This enforces that the receipts are processed in order, to allow some the applic { todo: cleanup wording & terms } -The blockchain itself only records the _intention_ to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a _relay_ process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. +{ todo: one relay process can relay all the things } -The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many _relay_ processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. +The blockchain itself only records the *intention* to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a *relay* process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. -As an example, here is a naive algorithm for relaying send messages from A to B, without error handling. We must also concurrently run the relay of receipts from B back to A, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. +The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many *relay* processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. + +As an example, here is a naive algorithm for relaying send messages from `A` to `B`, without error handling. We must also concurrently run the relay of receipts from `B` back to `A`, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. ``` while true diff --git a/docs/spec/ibc/connections.md b/docs/spec/ibc/connections.md index 94993b59d8..c8bf728d25 100644 --- a/docs/spec/ibc/connections.md +++ b/docs/spec/ibc/connections.md @@ -2,83 +2,94 @@ ([Back to table of contents](README.md#contents)) -The basis of IBC is the ability to verify in the on-chain consensus ruleset of chain _B_ that a data packet received on chain _B_ was correctly generated on chain _A_. This establishes a cross-chain linearity guarantee: upon validation of that packet on chain _B_ we know that the packet has been executed on chain _A_ and any associated logic resolved (such as assets being escrowed), and we can safely perform application logic on chain _B_ (such as generating vouchers on chain _B_ for the chain _A_ assets which can later be redeemed with a packet in the opposite direction). +The basis of IBC is the ability to verify in the on-chain consensus ruleset of chain `B` that a data packet received on chain `B` was correctly generated on chain `A`. This establishes a cross-chain linearity guarantee: upon validation of that packet on chain `B` we know that the packet has been executed on chain `A` and any associated logic resolved (such as assets being escrowed), and we can safely perform application logic on chain `B` (such as generating vouchers on chain `B` for the chain `A` assets which can later be redeemed with a packet in the opposite direction). This section outlines the abstraction of an IBC _connection_: the state and consensus ruleset necessary to perform IBC packet verification. ### 2.1 Definitions -- Chain _A_ is the source blockchain from which the IBC packet is sent -- Chain _B_ is the destination blockchain on which the IBC packet is received -- _Hh_ is the signed header of chain _A_ at height _h_ -- _Ch_ is the consensus ruleset of chain _A_ at height _h_ -- _Vk,h_ is the value stored on chain _A_ under key _k_ at height _h_ -- _P_ is the unbonding period of chain _A_, in units of time -- Δ_(a, b)_ is the time difference between events _a_ and _b_ +- Chain `A` is the source blockchain from which the IBC packet is sent +- Chain `B` is the destination blockchain on which the IBC packet is received +- `H_h` is the signed header of chain `A` at height `h` +- `C_h` is a subset of the consensus ruleset of chain `A` at height `h` +- `V_kh` is the value stored on chain `A` under key `k` at height `h` +- `P` is the unbonding period of chain `P`, in units of time +- `dt(a, b)` is the time difference between events `a` and `b` -Note that of all these, only _Hh_ defines a signature and is thus attributable. +Note that of all these, only `H_h` defines a signature and is thus attributable. ### 2.2 Requirements To facilitate an IBC connection, the two blockchains must provide the following proofs: -1. Given a trusted _Hh_ and _Ch_ and an attributable update message _Uh'_, - it is possible to prove _Hh'_ where _Ch' = Ch_ and Δ_(now, Hh) < P_ -2. Given a trusted _Hh_ and _Ch_ and an attributable change message _Xh'_, - it is possible to prove _Hh'_ where _Ch'_ ≠ _Ch_ and Δ _(now, Hh) < P_ -3. Given a trusted _Hh_ and a merkle proof _Mk,v,h_ it is possible to prove _Vk,h_ +1. Given a trusted `H_h` and `C_h` and an attributable update message `U_h`, + it is possible to prove `H_h'` where `C_h' == C_h` and `dt(now, H_h) < P` +2. Given a trusted `H_h` and `C_h` and an attributable change message `X_h`, + it is possible to prove `H_h'` where `C_h' /= C_h` and `dt(now, H_h) < P` +3. Given a trusted `H_h` and a Merkle proof `M_kvh` it is possible to prove `V_kh` -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages _Uh'_ and _Xh'_. The implementation of these requirements with Tendermint consensus is defined in Appendix E. Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages `U_h'` and `X_h'`. The implementation of these requirements with Tendermint consensus is defined in [Appendix E](). Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. -The merkle proof _Mk,v,h_ is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair (_k, v)_ is consistent with a merkle root stored in _Hh_. Handling the case where _k_ is not in the store requires a separate proof of non-existence, which is not supported by all merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. +The Merkle proof `M_kvh` is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair `(k, v)` is consistent with a Merkle root stored in `H_h`. Handling the case where `k` is not in the store requires a separate proof of non-existence, which is not supported by all Merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. -_valid(Hh ,Mk,v,h )_ ⇒ _[true | false]_ +`valid(H_h, M_kvh) => true | false` ### 2.3 Connection Lifecycle -#### 2.3.1 Opening a Connection +#### 2.3.1 Opening a connection -All proofs require an initial _Hh_ and _Ch_ for some _h_, where Δ_(now, Hh) < P_. +All proofs require an initial `H_h` and `C_h` for some `h`, where Δ_(now, Hh) < P_. -Establishing a bidirectional initial root-of-trust between the two blockchains (_A_ to _B_ and _B_ to _A_) — _HAh_ and _CAh_ stored on chain _B_, and _HBh_ and _CBh_ stored on chain _A_ — is necessary before any IBC packets can be sent. +Establishing a bidirectional initial root-of-trust between the two blockchains (`A` to `B` and `B` to `A`) — `H_ah` and `C_ah` stored on chain `B`, and `H_bh` and `C_bh` stored on chain `A` — is necessary before any IBC packets can be sent. Any header may be from a malicious chain (e.g. shadowing a real chain state with a fake validator set), so a subjective decision is required before establishing a connection. This can be performed permissionlessly, in which case users later utilizing the IBC channel must check the root-of-trust themselves, or authorized by on-chain governance for additional assurance. -#### 2.3.2 Following Block Headers +#### 2.3.2 Following block headers -We define two messages _Uh_ and _Xh_, which together allow us to securely advance our trust from some known _Hn_ to some future _Hh_ where _h > n_. Some implementations may require that _h = n + 1_ (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that Δ_vals(Cn, Ch ) < ⅓_ (each step must have a change of less than one-third of the validator set)[[4](./references.md#4)]. +We define two messages `U_h` and `X_h`, which together allow us to securely advance our trust from some known `H_n` to some future `H_h` where `h > n`. Some implementations may require that `h == n + 1` (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that `delta vals(C_n, C_h) < ⅓` (each step must have a change of less than one-third of the validator set)[[4](./references.md#4)]. -Either requirement is compatible with IBC. However, by supporting proofs where _h_-_n > 1_, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains _A_ and _B_, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). +Either requirement is compatible with IBC. However, by supporting proofs where `h_-_n > 1`, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains `A` and `B`, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). -Since these messages _Uh_ and _Xh_ provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such, any attempt to violate the finality guarantees in headers posted to chain _B_ can be submitted back to chain _A_ for punishment, in the same manner that chain _A_ would independently punish (slash) identified Byzantine actors. +Since these messages `U_h` and `X_h` provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such, any attempt to violate the finality guarantees in headers posted to chain `B` can be submitted back to chain `A` for punishment, in the same manner that chain `A` would independently punish (slash) identified Byzantine actors. -More formally, given existing set of trust _T_ = _{(Hi , Ci ), (Hj , Cj ), …}_, we must provide: +More formally, given existing set of trust `T` = `{(H_i, C_i), (H_j, C_j), …}`, we must provide: -_valid(T, Xh | Uh )_ ⇒ _[true | false | unknown]_ +`valid(T, X_h | U_h) => true | false | unknown` -_if Hh-1_ ∈ _T then_: -* _valid(T, Xh | Uh )_ ⇒ _[true | false]_ -* ∃ (Uh | Xh) ⇒ valid(T, Xh | Uh) {aren't there infinite? why is this necessary} +`valid` must fulfill the following properties: -_if Ch_ ∉ _T then_ -* _valid(T, Uh )_ ⇒ _false_ +``` +if H_h-1 ∈ T then + valid(T, X_h | U_h) => true | false + ∃ (U_h | X_h) => valid(T, X_h | U_h) +``` + +``` +if C_h ∉ T then + valid(T, U_h) => false +``` We can then process update transactions as follows: -_update(T, Xh | Uh )_ ⇒ match _valid(T, Xh | Uh )_ with -* _false_ ⇒ fail with `invalid proof` -* _unknown_ ⇒ fail with `need a proof between current and h` -* _true_ ⇒ set _T_ = _T_ ∪ _(Hh ,Ch )_ +`update(T, X_h | U_h) => success | failure` -Define _max(T)_ as _max(h, where Hh_ ∈ _T)_. For any _T_ with _max(T) = h-1_, there must exist some _Xh | Uh_ so that _max(update(T, Xh | Uh )) = h_. -By induction, there must exist a set of proofs, such that _max(update…(T,...)) = h+n_ for any n. +``` +update(T, X_h | U_h) = match valid(T, X_h | U_h) with + false => fail with "invalid proof" + unknown => fail with "need a proof between current and h" + true => + set T = T ∪ (H_h, C_h) +``` -Bisection can be used to discover this set of proofs. That is, given _max(T) = n_ and _valid(T, Xh | Uh ) = unknown_, we then try _update(T, Xb | Ub )_, where _b = (h+n)/2_. The base case is where _valid(T, Xh | Uh ) = true_ and is guaranteed to exist if _h=max(T)+1_. +Define `max(T)` as `max(h, where H_h ∈ T)`. For any `T` with `max(T) == h-1`, there must exist some `X_h | U_h` so that `max(update(T, X_h | U_h)) == h`. +By induction, there must exist a set of proofs, such that `max(update…(T,...)) == h + n` for any `n`. -#### 2.3.3 Closing a Connection +Bisection can be used to discover this set of proofs. That is, given `max(T) == n` and `valid(T, X_h | U_h) == unknown`, we then try `update(T, X_b | U_b)`, where _`b == (h + n) / 2`. The base case is where `valid(T, X_h | U_h) == true` and is guaranteed to exist if `h == max(T) + 1`. -IBC implementations may optionally include the ability to close an IBC connection and prevent further header updates, simply causing _update(T, Xh | Uh )_ as defined above to always return _false_. +#### 2.3.3 Closing a connection -Closing a connection may break application invariants (such as fungiblity - token vouchers on chain _B_ will no longer be redeemable for tokens on chain _A_) and should only be undertaken in extreme circumstances such as Byzantine behavior of the connected chain. +IBC implementations may optionally include the ability to close an IBC connection and prevent further header updates, simply causing `update(T, X_h | U_h)` as defined above to always return `false`. + +Closing a connection may break application invariants (such as fungiblity - token vouchers on chain `B` will no longer be redeemable for tokens on chain `A`) and should only be undertaken in extreme circumstances such as Byzantine behavior of the connected chain. Closure may be permissioned to an on-chain governance system, an identifiable party on the other chain (such as a signer quorum, although this will not work in some Byzantine cases), or any user who submits an application-specific fraud proof. When a connection is closed, application-specific measures may be undertaken to recover assets held on a Byzantine chain. We defer further discussion to [Appendix D](appendices.md#appendix-d-byzantine-recovery-strategies). diff --git a/docs/spec/ibc/overview.md b/docs/spec/ibc/overview.md index ab8c929188..344f69c7d7 100644 --- a/docs/spec/ibc/overview.md +++ b/docs/spec/ibc/overview.md @@ -14,20 +14,20 @@ In this paper, we define a process of posting block headers and Merkle tree proo ### 1.2 Definitions -_Blockchain_ - A replicated fault-tolerant state machine with a distributed consensus algorithm. The smallest unit produced through consensus is a block, which may contain many transactions, each applying some arbitrary mutation to the state. +*Blockchain* - A replicated fault-tolerant state machine with a distributed consensus algorithm. The smallest unit produced through consensus is a block, which may contain many transactions, each applying some arbitrary mutation to the state. -_Module_ - We assume that the state machine of each blockchain is comprised of multiple components that have limited rights to execute some particular set of state transfers (these are modules in the Cosmos SDK or smart contracts in Ethereum). +*Module* - We assume that the state machine of each blockchain is comprised of multiple components that have limited rights to execute some particular set of state transfers (these are modules in the Cosmos SDK or smart contracts in Ethereum). -_Finality_ - The guarantee that a given block will not be reverted within some predefined conditions of a consensus algorithm. All proof-of-work systems offer probabilistic finality, which means that the difficulty of reverting a block increases as the block is embedded more deeply in the chain. Many proof-of-stake systems offer much weaker guarantees, based only on the honesty of the block producers. BFT algorithms such as Tendermint guarantee complete finality upon production of a block (unless over two thirds of the validators collude to break consensus, in which case the offenders can be identified and punished - further discussion of that scenario is outside the scope of this document). +*Finality* - The guarantee that a given block will not be reverted within some predefined conditions of a consensus algorithm. All proof-of-work systems offer probabilistic finality, which means that the difficulty of reverting a block increases as the block is embedded more deeply in the chain. Many proof-of-stake systems offer much weaker guarantees, based only on the honesty of the block producers. BFT algorithms such as Tendermint guarantee complete finality upon production of a block (unless over two thirds of the validators collude to break consensus, in which case the offenders can be identified and punished - further discussion of that scenario is outside the scope of this document). -_Attributable_ - Knowledge of the pseudonymous identity which made a statement, whom we can punish with some deduction of value (slashing) if the statement is false. Synonymous with accountability. +*Attributable* - Knowledge of the pseudonymous identity which made a statement, whom we can punish with some deduction of value (slashing) if the statement is false. Synonymous with accountability. -_Unbonding Period_ - Proof-of-stake algorithms need to lock the stake (prevent transfers) for some time to provide a lower bound for the length of a long-range attack [[3](./references.md#3)]. Complete finality is associated with a subset of the proof-of-stake class of consensus algorithms. We assume the proof-of-stake algorithms utilized by the two blockchains have some unbonding period P. +*Unbonding period* - Proof-of-stake algorithms need to lock the stake (prevent transfers) for some time to provide a lower bound for the length of a long-range attack [[3](./references.md#3)]. Complete finality is associated with a subset of the proof-of-stake class of consensus algorithms. We assume the proof-of-stake algorithms utilized by the two blockchains have some unbonding period P. ### 1.3 Threat Models -_False statements_ - Any information we receive may be false. +*False statements* - Any information we receive may be false. -_Network partitions and delays_ - We assume an asynchronous, adversarial network with unbounded latency. Network messages may be modified, reordered, duplicated, or selectively dropped. Actors may be arbitrarily partitioned by a powerful adversary. The IBC protocol favors correctness over liveness (and provides no particular guarantees of the latter). +*Network partitions and delays* - We assume an asynchronous, adversarial network with unbounded latency. Network messages may be modified, reordered, duplicated, or selectively dropped. Actors may be arbitrarily partitioned by a powerful adversary. The IBC protocol favors correctness over liveness where applicable. -_Byzantine actors_ - An entire blockchain may not act according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Application-level protocols designed on top of IBC should consider this risk and mitigate it as possible in a manner suitable to their application. +*Byzantine actors* - An entire blockchain may not act according to protocol. This must be detectable and provable, allowing the communicating blockchain to revoke trust and take necessary action. Application-level protocols designed on top of IBC should consider and mitigate this risk in a manner suitable to their application. From 1ac8a4b38e7f3ad939686fbe22401fe49350e207 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 21 Apr 2018 14:33:21 +0200 Subject: [PATCH 27/32] Connection / channel / packet clarifications --- docs/spec/ibc/README.md | 2 +- docs/spec/ibc/appendices.md | 2 +- docs/spec/ibc/channels-and-packets.md | 166 +++++++++++++------------- docs/spec/ibc/connections.md | 28 +++-- 4 files changed, 101 insertions(+), 97 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index eca34c80d3..332bf9141b 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -4,7 +4,7 @@ This paper specifies the Cosmos Inter-Blockchain Communication (IBC) protocol. The IBC protocol defines a set of semantics for authenticated, strictly-ordered message passing between two blockchains with independent consensus algorithms. -The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving, under particular security assumptions of the underlying blockchains, the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. +The core IBC protocol is payload-agnostic. On top of IBC, developers can implement the semantics of a particular application, enabling users to transfer valuable assets between different blockchains while preserving the contractual guarantees of the asset in question - such as scarcity and fungibility for a currency or global uniqueness for a digital kitty-cat. IBC requires two blockchains with cheaply verifiable rapid finality and Merkle tree substate proofs. The protocol makes no assumptions of block confirmation times or maximum network latency of packet transmissions, and the two consensus algorithms remain completely independent. Each chain maintains a local partial order and inter-chain message sequencing ensures cross-chain linearity. Once the two chains have registered a trust relationship, cryptographically verifiable packets can be sent between them. diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 736167d2ae..14e4ebde68 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -74,7 +74,7 @@ In order to prove a merkle root, we must fully define the headers, signatures, a Building Blocks: Header, PubKey, Signature, Commit, ValidatorSet --> needs input/support from Tendermint Core team (and go-crypto) +→ needs input/support from Tendermint Core team (and go-crypto) Registering Chain diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 128a0c01ac..353bf7d205 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -6,29 +6,29 @@ IBC uses a cross-chain message passing model that makes no assumptions about network synchrony. IBC *data packets* (hereafter just *packets*) are relayed from one blockchain to the other by external infrastructure. Chain `A` and chain `B` confirm new blocks independently, and packets from one chain to the other may be delayed or censored arbitrarily. The speed of packet transmission and confirmation is limited only by the speed of the underlying chains. -The IBC protocol as defined here is payload-agnostic. The packet receiver on chain `B` decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received and either accepted or rejected. +The IBC protocol as defined here is payload-agnostic. The packet receiver on chain `B` decides how to act upon the incoming message, and may add its own application logic to determine which state transactions to apply according to what data the packet contains. Both chains must only agree that the packet has been received. To facilitate useful application logic, we introduce an IBC *channel*: a set of reliable messaging queues that allows us to guarantee a cross-chain causal ordering[[5](./references.md#5)] of IBC packets. Causal ordering means that if packet *x* is processed before packet *y* on chain `A`, packet *x* must also be processed before packet *y* on chain `B`. -IBC channels implement a vector clock[[2](references.md#2)] for the restricted case of two processes (in our case, blockchains). Given *x* -> *y* means *x* is causally before *y*, chains `A` and `B`, and *a* => *b* means *a* implies *b*: +IBC channels implement a vector clock[[2](references.md#2)] for the restricted case of two processes (in our case, blockchains). Given *x* → *y* means *x* is causally before *y*, chains `A` and `B`, and *a* ⇒ *b* means *a* implies *b*: -*A:send(msgi )* -> *B:receive(msgi )* +*A:send(msgi )* → *B:receive(msgi )* -*B:receive(msgi )* -> *A:receipt(msgi )* +*B:receive(msgi )* → *A:receipt(msgi )* -*A:send(msgi )* -> *A:send(msgi+1 )* +*A:send(msgi )* → *A:send(msgi+1 )* -*x* -> *A:send(msgi )* => -*x* -> *B:receive(msgi )* +*x* → *A:send(msgi )* ⇒ +*x* → *B:receive(msgi )* -*y* -> *B:receive(msgi )* => -*y* -> *A:receipt(msgi )* +*y* → *B:receive(msgi )* ⇒ +*y* → *A:receipt(msgi )* Every transaction on the same chain already has a well-defined causality relation (order in history). IBC provides an ordering guarantee across two chains which can be used to reason about the combined state of both chains as a whole. For example, an application may wish to allow a single tokenized asset to be transferred between and held on multiple blockchains while preserving fungibility and conservation of supply. The application can mint asset vouchers on chain `B` when a particular IBC packet is committed to chain `B`, and require outgoing sends of that packet on chain `A` to escrow an equal amount of the asset on chain `A` until the vouchers are later redeemed back to chain `A` with an IBC packet in the reverse direction. This ordering guarantee along with correct application logic can ensure that total supply is preserved across both chains and that any vouchers minted on chain `B` can later be redeemed back to chain `A`. -This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-format). +This section provides definitions for packets and channels, a high-level specification of the queue interface, and a list of the necessary proofs. To implement wire-compatible IBC, chain `A` and chain `B` must also use a common encoding format. An example binary encoding format can be found in [Appendix C](appendices.md#appendix-c-merkle-proof-formats). ### 3.2 Definitions @@ -56,11 +56,11 @@ We define an IBC *receipt* `R` as the four-tuple `(sequence, source, destination `destination` is a string uniquely identifying the chain, connection, and channel which should receive this packet -`result` is a code of either `success` or `failure` +`result` is an opaque application result payload #### 3.2.3 Queue -To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - `q_head` and `q_tail` - bound the slice, such that for every `index` where `q_head <= index < q_tail`, there is a queue element `q[q_index]`. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, `advance`, to facilitate efficient queue cleanup. +To implement strict message ordering, we introduce an ordered *queue*. A queue can be conceptualized as a slice of an infinite array. Two numerical indices - `q_head` and `q_tail` - bound the slice, such that for every `index` where `q_head <= index < q_tail`, there is a queue element `q[index]`. Elements can be appended to the tail (end) and removed from the head (beginning). We introduce one further method, `advance`, to facilitate efficient queue cleanup. Each IBC-supporting blockchain must provide a queue abstraction with the following functionality: @@ -71,31 +71,31 @@ set q_head = 0 set q_tail = 0 ``` -`peek => e` +`peek ⇒ e` ``` match q_head == q_tail with - true => return nil - false => + true ⇒ return nil + false ⇒ return q[q_head] ``` -`pop => e` +`pop ⇒ e` ``` match q_head == q_tail with - true => return nil - false => + true ⇒ return nil + false ⇒ set q_head = q_head + 1 return q_head - 1 ``` -`retrieve(i) => e` +`retrieve(i) ⇒ e` ``` match q_head <= i < q_tail with - true => return q[i] - false => return nil + true ⇒ return q[i] + false ⇒ return nil ``` `push(e)` @@ -112,13 +112,13 @@ set q_head = i set q_tail = max(q_tail, i) ``` -`head => i` +`head ⇒ i` ``` return q_head ``` -`tail => i` +`tail ⇒ i` ``` return q_tail @@ -132,11 +132,11 @@ An IBC channel consists of four distinct queues, two on each chain: `outgoing_A`: Outgoing IBC packets from chain `A` to chain `B`, stored on chain `A` -`incoming_A`: IBC receipts (execution logs) for incoming IBC packets from chain `B`, stored on chain `A` +`incoming_A`: IBC receipts for incoming IBC packets from chain `B`, stored on chain `A` `outgoing_B`: Outgoing IBC packets from chain `B` to chain `A`, stored on chain `B` -`incoming_B`: IBC receipts (execution logs) for incoming IBC packets from chain `A`, stored on chain `B` +`incoming_B`: IBC receipts for incoming IBC packets from chain `A`, stored on chain `B` ### 3.3 Requirements @@ -148,72 +148,78 @@ We use the previously-defined Merkle proof `M_kvh` to provide the requisite proo The index is stored as a fixed-length unsigned integer in big endian format, so that the lexicographical order of the byte representation of the key is consistent with their sequence number. This allows us to quickly iterate over the queue, as well as prove the content of a packet (or lack of packet) at a given sequence. `head` and `tail` are two special constants that store an integer index, and are chosen such that their serializated representation cannot collide with that of any possible index. -Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value `v` is written to a queue, then every valid proof `M_kvh` must refer to the same `v`. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. This property is essential to safely process asynchronous messages. +Once written to the queue, a packet must be immutable (except for deletion when popped from the queue). That is, if a value `v` is written to a queue, then every valid proof `M_kvh` must refer to the same `v`. In practice, this means that an IBC implementation must ensure that only the IBC module can write to the IBC subspace of the blockchain's Merkle store. Each incoming & outgoing queue for each connection must be provably associated with another uniquely identified chain, connection, and channel so that an observer can prove that a message was intended for that chain and only that chain. This can easily be done by prefixing the queue keys in the Merkle store with strings unique to the chain (such as chain identifier), connection, and channel. ### 3.4 Sending a packet -{ todo: unify terms, clarify } - To send an IBC packet, an application module on the source chain must call the send method of the IBC module, providing a packet as defined above. The IBC module must ensure that the destination chain was already properly registered and that the calling module has permission to write this packet. If all is in order, the IBC module simply pushes the packet to the tail of `outgoing_a`, which enables all the proofs described above. -The packet must provide routing information in the `type` field, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module on the destination chain must associate every supported packet type with a particular handler (`f_type`) and return an error for unsupported types. +The packet must provide routing information in the `type` field, so that different modules can write different kinds of packets and maintain any application-level invariants related to this area. For example, a "coin" module can ensure a fixed supply, or a "NFT" module can ensure token uniqueness. The IBC module on the destination chain must associate every supported packet type with a particular handler (`f_type`). -`send(P{type, sequence, source, destination, data})` +To send an IBC packet from blockchain `A` to blockchain `B`: -``` -match source == (A, connection, channel) and sequence == tail(outgoing_A) with - true => push(outgoing_A, P); success - false => fail with "wrong sender" -``` - -Note that the `sequence`, `source`, and `destination` can all be encoded in the Merkle tree key for the channel and do not need to be stored for each packet. - -### 3.5 Receiving a packet - -Upon packet receipt, chain `B` must check that the packet is valid, that it was intended for the destination, and that all previous packets have been processed. `receive` must write the receipt queue upon accepting a valid packet, even if the handler execution returned an error, so that future packets can be processed. - -To receive an IBC packet on blockchain `B` from a source chain `A`, with a Merkle proof `M_kvh` and the current set of trusted headers for that chain `T_A`: - -`receive(P{type, sequence, source, destination, data}, M_kvh)` +`send(P{type, sequence, source, destination, data}) ⇒ success | failure` ``` case - incoming_B == nil => fail with "unregistered sender" - destination /= (B, connection, channel) => fail with "wrong destination" - sequence /= head(Incoming_B) => fail with "out of order" - H_h not in T_A => fail with "must submit header for height h" - valid(H_h, M_kvh) == false => fail with "invalid Merkle proof" - otherwise => - set (result, error) = f_type(data) - push(incoming_B, R{}) + source /= (A, connection, channel) ⇒ fail with "wrong sender" + sequence /= tail(outgoing_A) ⇒ fail with "wrong sequence" + otherwise ⇒ + push(outgoing_A, P) + success ``` -{ todo: check that v + k == packet, clearly define connection / channel } +Note that the `sequence`, `source`, and `destination` can all be encoded in the Merkle tree key for the channel and do not need to be stored individually in each packet. + +### 3.5 Receiving a packet + +Upon packet receipt, chain `B` must check that the packet is valid, that it was intended for the destination, and that all previous packets have been processed. `receive` must write the receipt queue upon accepting a valid packet regardless of the result of handler execution so that future packets can be processed. + +To receive an IBC packet on blockchain `B` from a source chain `A`, with a Merkle proof `M_kvh` and the current set of trusted headers for that chain `T_A`: + +`receive(P{type, sequence, source, destination, data}, M_kvh) ⇒ success | failure` + +``` +case + incoming_B == nil ⇒ fail with "unregistered sender" + destination /= (B, connection, channel) ⇒ fail with "wrong destination" + sequence /= head(Incoming_B) ⇒ fail with "out of order" + H_h not in T_A ⇒ fail with "must submit header for height h" + valid(H_h, M_kvh) == false ⇒ fail with "invalid Merkle proof" + otherwise ⇒ + set result = f_type(data) + push(incoming_B, R{tail(incoming_B), (B, connection, channel), (A, connection, channel), result}) + success +``` ### 3.6 Handling a receipt { todo: cleanup logic } -When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the receipts from sending the original message. For example, if I want to send tokens from Alice on chain `A` to Bob on chain `B`, chain `A` must decrement Alice's account *if and only if* Bob's account was incremented on chain `B`. We can achieve that by storing a protected intermediate state on chain `A` (escrowing the assets in question), which is then committed or rolled back based on the result of executing the transaction on chain `B`. +When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the execution result returned in the IBC receipt. For example, if I want to send tokens from Alice on chain `A` to Bob on chain `B`, chain `A` must decrement Alice's account *if and only if* Bob's account was incremented on chain `B`. We can achieve that by storing a protected intermediate state on chain `A` (escrowing the assets in question), which is then committed or rolled back based on the result of executing the transaction on chain `B`. -To do this requires that we not only provably send a message from chain `A` to chain `B`, but provably return the result of that message (the receipt) from chain `B` to chain `A`. As one noticed above in the implementation of `receive`, if the valid IBC message was sent from `A` to `B`, then the result of executing it, even if it was an error, is stored in _B:qA.receipt_. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to _S_ for a message previously sent to _A_: +To do this requires that we not only provably send a packet from chain `A` to chain `B`, but provably return the result of executing that packet (the receipt `data`) from chain `B` to chain `A`. If a valid IBC packet was sent from `A` to `B`, then the result of executing it is stored in `incoming_B`. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to `A` for a message previously sent to `B`. Receipts, like packets, are processed in order. -`handle_receipt(A, M_kvh)` +To handle an IBC receipt on blockchain `A` received from blockchain `B`, with a Merkle proof `M_kvh` and the current set of trusted headers for that chain `T_B`: + +`handle_receipt(R{sequence, source, destination, data}, M_kvh)` ``` case - outgoing_A == nil => fail with "unregistered sender" - destination /= (A, connection, channel) => fail with "wrong destination" - sequence /= head(incoming_A) => fail with "out of order" - H_h not in T_B => fail with "must submit header for height h" - valid(H_h, M_kvh) == false => fail with "invalid Merkle proof" - * _v = (\_, error)_ ⇒ _(type, data) := pop(qS.send ); rollbacktype(data); Success_ - * _v = (res, success)_ ⇒ _(type, data) := pop(qS.send ); committype(data, res); Success_ + outgoing_A == nil ⇒ fail with "unregistered sender" + destination /= (A, connection, channel) ⇒ fail with "wrong destination" + sequence /= head(incoming_A) ⇒ fail with "out of order" + H_h not in T_B ⇒ fail with "must submit header for height h" + valid(H_h, M_kvh) == false ⇒ fail with "invalid Merkle proof" + otherwise ⇒ + set P{type, _, _, _, _} = outgoing_A[sequence] + f_type(result) + success ``` -This enforces that the receipts are processed in order, to allow applications to make use of some basic assumptions about ordering. It also removes the message from the send queue, as there is now proof it was processed on the receiving chain and no need to store it. +This allows applications to reason about ordering and enforce application-level guarantees by committing or reverting state changes on chain `A` based on the result of packet execution on chain `B`: ![Successful Transaction](images/Receipts.png) @@ -225,28 +231,24 @@ This enforces that the receipts are processed in order, to allow applications to { todo: one relay process can relay all the things } -The blockchain itself only records the *intention* to send the given message to the recipient chain, it doesn't make any network connections as that would add unbounded delays and non-determinism into the state machine. We define the concept of a *relay* process that connects two chain by querying one for all proofs needed to prove outgoing messages and submit these proofs to the recipient chain. +The blockchain itself only records the *intention* to send the given message to the recipient chain - it does not open any actual network connections. We define the concept of a *relay* process that connects two chains by querying one for all outgoing packets & proofs, then committing those packets & proofs to the recipient chain. -The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Many *relay* processes may run in parallel without violating any safety consideration. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. +The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Relayers may employ application-level methods to recoup these fees. Any number of *relay* processes may be safely run in parallel. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. -As an example, here is a naive algorithm for relaying send messages from `A` to `B`, without error handling. We must also concurrently run the relay of receipts from `B` back to `A`, in order to complete the cycle. Note that all reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. +As an example, here is a naive algorithm for relaying outgoing packets from `A` to `B` and incoming receipts from `B` back to `A`. All reads of variables belonging to a chain imply queries and all function calls imply submitting a transaction to the blockchain. ``` while true - pending := tail(A:qB.send) - received := tail(B:qA.receive) + set pending = tail(outgoing_A) + set received = tail(incoming_B) if pending > received - Uh := A:latestHeader - B:updateHeader(Uh) - for i :=received...pending - k := (B, send, i) - packet := A:Mk,v,h - B:IBCreceive(A, packet) - sleep(desiredLatency) + set U_h = A.latestHeader + if U_h /= B.knownHeaderA + B.updateHeader(U_h) + for i from received to pending + set P = outgoing_A[i] + set M_kvh = A.prove(U_h, P) + B.receive(P, M_kvh) ``` -Note that updating a header is a costly transaction compared to posting a merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. - -In the presence of multiple concurrent relays, any given relay can perform local optimizations to minimize the number of headers it submits, but remember the frequency of header submissions defines the latency of the packet transfer. - -Indeed, it is ideal if each user that initiates the creation of an IBC packet also relays it to the recipient chain. The only constraint is that the relay must be able to pay the appropriate fees on the destination chain. However, in order to avoid bottlenecks, a group may sponsor an account to pay fees for a public relayer that moves all unrelayed packets (perhaps with a high latency). +Note that updating a header is a costly transaction compared to posting a Merkle proof for a known header. Thus, a process could wait until many messages are pending, then submit one header along with multiple Merkle proofs, rather than a separate header for each message. This decreases total computation cost (and fees) at the price of additional latency and is a trade-off each relay can dynamically adjust. diff --git a/docs/spec/ibc/connections.md b/docs/spec/ibc/connections.md index c8bf728d25..0e5c17cfeb 100644 --- a/docs/spec/ibc/connections.md +++ b/docs/spec/ibc/connections.md @@ -28,17 +28,19 @@ To facilitate an IBC connection, the two blockchains must provide the following it is possible to prove `H_h'` where `C_h' /= C_h` and `dt(now, H_h) < P` 3. Given a trusted `H_h` and a Merkle proof `M_kvh` it is possible to prove `V_kh` -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages `U_h'` and `X_h'`. The implementation of these requirements with Tendermint consensus is defined in [Appendix E](). Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages `U_h'` and `X_h'`. The implementation of these requirements with Tendermint consensus is defined in [Appendix F](appendices.md#appendix-f-tendermint-header-proofs). Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. The Merkle proof `M_kvh` is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair `(k, v)` is consistent with a Merkle root stored in `H_h`. Handling the case where `k` is not in the store requires a separate proof of non-existence, which is not supported by all Merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it. -`valid(H_h, M_kvh) => true | false` +Blockchains supporting IBC must implement Merkle proof verification: + +`valid(H_h, M_kvh) ⇒ true | false` ### 2.3 Connection Lifecycle #### 2.3.1 Opening a connection -All proofs require an initial `H_h` and `C_h` for some `h`, where Δ_(now, Hh) < P_. +All proofs require an initial `H_h` and `C_h` for some `h`, where `dt(now, H_h) < P`. Establishing a bidirectional initial root-of-trust between the two blockchains (`A` to `B` and `B` to `A`) — `H_ah` and `C_ah` stored on chain `B`, and `H_bh` and `C_bh` stored on chain `A` — is necessary before any IBC packets can be sent. @@ -46,38 +48,38 @@ Any header may be from a malicious chain (e.g. shadowing a real chain state with #### 2.3.2 Following block headers -We define two messages `U_h` and `X_h`, which together allow us to securely advance our trust from some known `H_n` to some future `H_h` where `h > n`. Some implementations may require that `h == n + 1` (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that `delta vals(C_n, C_h) < ⅓` (each step must have a change of less than one-third of the validator set)[[4](./references.md#4)]. +We define two messages `U_h` and `X_h`, which together allow us to securely advance our trust from some known `H_n` to some future `H_h` where `h > n`. Some implementations may require that `h == n + 1` (all headers must be processed in order). IBC implemented on top of Tendermint or similar BFT algorithms requires only that `delta-vals(C_n, C_h) < ⅓` (each step must have a change of less than one-third of the validator set)[[4](./references.md#4)]. -Either requirement is compatible with IBC. However, by supporting proofs where `h_-_n > 1`, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains `A` and `B`, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). +Either requirement is compatible with IBC. However, by supporting proofs where `h - n > 1`, we can follow the block headers much more efficiently in situations where the majority of blocks do not include an IBC packet between chains `A` and `B`, and enable low-bandwidth connections to be implemented at very low cost. If there are packets to relay every block, these two requirements collapse to the same case (every header must be relayed). Since these messages `U_h` and `X_h` provide all knowledge of the remote blockchain, we require that they not just be provable, but also attributable. As such, any attempt to violate the finality guarantees in headers posted to chain `B` can be submitted back to chain `A` for punishment, in the same manner that chain `A` would independently punish (slash) identified Byzantine actors. More formally, given existing set of trust `T` = `{(H_i, C_i), (H_j, C_j), …}`, we must provide: -`valid(T, X_h | U_h) => true | false | unknown` +`valid(T, X_h | U_h) ⇒ true | false | unknown` `valid` must fulfill the following properties: ``` if H_h-1 ∈ T then - valid(T, X_h | U_h) => true | false - ∃ (U_h | X_h) => valid(T, X_h | U_h) + valid(T, X_h | U_h) ⇒ true | false + ∃ (U_h | X_h) ⇒ valid(T, X_h | U_h) ``` ``` if C_h ∉ T then - valid(T, U_h) => false + valid(T, U_h) ⇒ false ``` We can then process update transactions as follows: -`update(T, X_h | U_h) => success | failure` +`update(T, X_h | U_h) ⇒ success | failure` ``` update(T, X_h | U_h) = match valid(T, X_h | U_h) with - false => fail with "invalid proof" - unknown => fail with "need a proof between current and h" - true => + false ⇒ fail with "invalid proof" + unknown ⇒ fail with "need a proof between current and h" + true ⇒ set T = T ∪ (H_h, C_h) ``` From dddf818e204b96db435c7be339716bd831458e89 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 21 Apr 2018 15:04:06 +0200 Subject: [PATCH 28/32] Move Byzantine failure section to Appendix D --- docs/spec/ibc/README.md | 1 - docs/spec/ibc/appendices.md | 14 ++++++++++++++ docs/spec/ibc/channels-and-packets.md | 6 ------ docs/spec/ibc/optimizations.md | 27 +++++++-------------------- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index 332bf9141b..b6689a1768 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -37,7 +37,6 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. **[Optimizations](optimizations.md)** 1. [Timeouts](optimizations.md#41-timeouts) 1. [Cleanup](optimizations.md#42-cleanup) - 1. [Handling Byzantine failures](optimizations.md#43-handling-byzantine-failures) 1. **[Conclusion](conclusion.md)** 1. **[References](references.md)** 1. **[Appendices](appendices.md)** diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 14e4ebde68..1e57d3c5ad 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -54,6 +54,20 @@ See [binary format as protobuf specification](./protobuf/merkle.proto) - Plasma-like fraud proofs - Trusted entity - governance +### 4.3 Handling Byzantine failures + +While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). + +The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. + +If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). + +The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). + +The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? + +Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. + ## Appendix E: Universal IBC Packets { what is this } diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 353bf7d205..80cc9d4b90 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -196,8 +196,6 @@ case ### 3.6 Handling a receipt -{ todo: cleanup logic } - When we wish to create a transaction that atomically commits or rolls back across two chains, we must look at the execution result returned in the IBC receipt. For example, if I want to send tokens from Alice on chain `A` to Bob on chain `B`, chain `A` must decrement Alice's account *if and only if* Bob's account was incremented on chain `B`. We can achieve that by storing a protected intermediate state on chain `A` (escrowing the assets in question), which is then committed or rolled back based on the result of executing the transaction on chain `B`. To do this requires that we not only provably send a packet from chain `A` to chain `B`, but provably return the result of executing that packet (the receipt `data`) from chain `B` to chain `A`. If a valid IBC packet was sent from `A` to `B`, then the result of executing it is stored in `incoming_B`. Since the receipts are stored in a queue with the same key construction as the sending queue, we can generate the same set of proofs for them, and perform a similar sequence of steps to handle a receipt coming back to `A` for a message previously sent to `B`. Receipts, like packets, are processed in order. @@ -227,10 +225,6 @@ This allows applications to reason about ordering and enforce application-level ### 3.7 Packet relayer -{ todo: cleanup wording & terms } - -{ todo: one relay process can relay all the things } - The blockchain itself only records the *intention* to send the given message to the recipient chain - it does not open any actual network connections. We define the concept of a *relay* process that connects two chains by querying one for all outgoing packets & proofs, then committing those packets & proofs to the recipient chain. The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Relayers may employ application-level methods to recoup these fees. Any number of *relay* processes may be safely run in parallel. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index 0c4568e0eb..81cee63dfa 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -2,15 +2,15 @@ ([Back to table of contents](README.md#contents)) -The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. It guarantees that all messages are processed exactly once and in order, and provides a mechanism for non-blocking atomic transactions spanning two blockchains. However, to increase efficiency over millions of messages with many possible failure modes on both sides of the connection, we can extend the protocol. These extensions allow us to clean up the receipt queue to avoid state bloat, as well as more gracefully recover from cases where large numbers of messages are not being relayed, or other failure modes in the remote chain. +The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. All messages are processed exactly once and in order, and applications can guarantee invariants over their combined state on both chains. IBC can be further extended and optimized to provide additional guarantees and minimize costs on the underlying blockchains. We detail two extensions: packet timeouts, and packet cleanup. ### 4.1 Timeouts -Sometimes it is desirable to have some timeout, an upper limit to how long you will wait for a transaction to be processed before considering it an error. At the same time, this is an obvious attack vector for a double spend, just delaying the relay of the receipt or waiting to send the message in the first place and then relaying it right after the cutoff to take advantage of different local clocks on the two chains. +Application semantics may require some timeout: an upper limit to how long the chain will wait for a transaction to be processed before considering it an error. Since the two chains have different local clocks, this is an obvious attack vector for a double spend - an attacker may delay the relay of the receipt or wait to send the packet until right after the timeout - so applications cannot safely implement naive timeout logic themselves. -One solution to this is to include a timeout in the IBC message itself. When sending it, one can specify a block height or timestamp on the **receiving** chain after which it is no longer valid. If the message is posted before the cutoff, it will be processed normally. If it is posted after that cutoff, it will be a guaranteed error. Note that to make this secure, the timeout must be relative to a condition on the **receiving** chain, and the sending chain must have proof of the state of the receiving chain after the cutoff. +One solution is to include a timeout in the IBC packet itself. When sending a packet, one can specify a block height or timestamp on chain `B` after which the packet is no longer valid. If the packet is posted before the cutoff, it will be processed normally. If it is posted after the cutoff, it will be a guaranteed error. In order to provide the necessary guarantees, the timeout must be specified relative to a condition on the receiving chain, and the sending chain must have proof of this condition after the cutoff. -For a sending chain _A_ and a receiving chain _B_, with _k=(\_, \_, i)_ for _A:qB.send_ or _B:qA.receipt_ we currently have the following guarantees: +For a sending chain `A` and a receiving chain `B`, with an IBC packet `P={_, sequence, _, _, _}`, the base IBC protocol provides the following guarantees: _A:Mk,v,h =_ ∅ _if message i was not sent before height h_ @@ -28,9 +28,10 @@ _Vsend = (maxHeight, maxTime, type, data)_ _expired(Hh ,Vsend )_ ⇒ _[true|false]_ -We then update message handling in _IBCreceive_, so it doesn't even call the handler function if the timeout was reached, but rather directly writes and error in the receipt queue: +We then update message handling in `receive`, so that chain `B` doesn't even call the handler function if the timeout was reached, but instead directly writes an error in the receipt queue: + +`receive` -_IBCreceive:_ * …. * _expired(latestHeader, v)_ ⇒ _push(qS.receipt , (None, TimeoutError)),_ * _v = (\_, \_, type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err));_ @@ -88,17 +89,3 @@ _S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. ![Cleaning up Packets](images/CleanUp.png) - -### 4.3 Handling Byzantine failures - -While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). - -The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. - -If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). - -The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). - -The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? - -Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. From 25a146d43f5f5add111f43a778e04fac0ef0abb4 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Sat, 21 Apr 2018 20:56:53 +0200 Subject: [PATCH 29/32] Update optimizations section --- docs/spec/ibc/channels-and-packets.md | 2 +- docs/spec/ibc/optimizations.md | 77 +++++++++++++++------------ 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 80cc9d4b90..50df1a1f82 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -212,7 +212,7 @@ case H_h not in T_B ⇒ fail with "must submit header for height h" valid(H_h, M_kvh) == false ⇒ fail with "invalid Merkle proof" otherwise ⇒ - set P{type, _, _, _, _} = outgoing_A[sequence] + set P{type, _, _, _, _} = pop(outgoing_A) f_type(result) success ``` diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index 81cee63dfa..d017499e59 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -10,70 +10,75 @@ Application semantics may require some timeout: an upper limit to how long the c One solution is to include a timeout in the IBC packet itself. When sending a packet, one can specify a block height or timestamp on chain `B` after which the packet is no longer valid. If the packet is posted before the cutoff, it will be processed normally. If it is posted after the cutoff, it will be a guaranteed error. In order to provide the necessary guarantees, the timeout must be specified relative to a condition on the receiving chain, and the sending chain must have proof of this condition after the cutoff. -For a sending chain `A` and a receiving chain `B`, with an IBC packet `P={_, sequence, _, _, _}`, the base IBC protocol provides the following guarantees: +For a sending chain `A` and a receiving chain `B`, with an IBC packet `P={_, i, _, _, _}` and some height `h` on chain `B`, the base IBC protocol provides the following guarantees: -_A:Mk,v,h =_ ∅ _if message i was not sent before height h_ +`A:M_kvh == ∅` if message `i` was not sent before height `h` -_A:Mk,v,h =_ ∅ _if message i was sent and receipt received before height h (and the receipts for all messages j < i were also handled)_ +`A:M_kvh == ∅` if message `i` was sent and the corresponding receipt received before height `h` (and the receipts for all messages j < i were also handled) -_A:Mk,v,h _ ≠ ∅ _otherwise (message result is not yet processed)_ +`A:M_kvh /= ∅` otherwise, if message `i` was sent but the receipt has not yet been processed -_B:Mk,v,h =_ ∅ _if message i was not received before height h_ +`B:M_kvh == ∅` if message `i` was not received before height `h` -_B:Mk,v,h _ ≠ ∅ _if message i was received before height h (and all messages j < i were received)_ +`B:M_kvh /= ∅` if message `i` was received before height `h` -Based on these guarantees, we can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue, and defining an expired function that returns true iff _h > maxHeight_ or _timestamp(Hh ) > maxTime_. +We can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue and defining an expired function that returns true iff `h > maxHeight` or `timestamp(H_h) > maxTime`. -_Vsend = (maxHeight, maxTime, type, data)_ +`P = {type, sequence, source, destination, data, maxHeight, maxTime}` -_expired(Hh ,Vsend )_ ⇒ _[true|false]_ +`expired(H_h, P) ⇒ true | false` -We then update message handling in `receive`, so that chain `B` doesn't even call the handler function if the timeout was reached, but instead directly writes an error in the receipt queue: +We then update message handling in `receive`, so that chain `B` doesn't even call the handler function if the timeout was reached but instead directly writes an error in the receipt queue: `receive` - * …. - * _expired(latestHeader, v)_ ⇒ _push(qS.receipt , (None, TimeoutError)),_ - * _v = (\_, \_, type, data)_ ⇒ _(result, err) := ftype(data); push(qS.receipt , (result, err));_ +``` +case + ... + expired(latestHeader, v) ⇒ push(incoming_b, R{..., TimeoutError}) + otherwise ⇒ + set result = f_type(data) + push(incoming_B, R{tail(incoming_B), (B, connection, channel), (A, connection, channel), result}) +``` -and add a new _IBCtimeout_ function to accept tail proofs to demonstrate that the message was not processed at some given header on the recipient chain. This allows the sender chain to assert timeouts locally. +The `receipt_handler` function on chain `A` can now verify timeouts and pass valid timeout receipts to the application handler (which can revert state changes such as escrowing assets): +`receipt_handler` -_S:IBCtimeout(A, Mk,v,h)_ ⇒ _match_ - * _qA.send =_ ∅ ⇒ _Error("unregistered sender"),_ - * _k = (\_, send, \_)_ ⇒ _Error("must be a receipt"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ - * _not valid(Hh , Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ - * _k = (S, receipt, tail)_ ⇒ _match_ - * _tail_ ≥ _head(qS.send )_ ⇒ _Error("receipt exists, no timeout proof")_ - * _not expired(peek(qS.send ))_ ⇒ _Error("message timeout not yet reached")_ - * _default_ ⇒ _(\_, \_, type, data) := pop(qS.send ); rollbacktype(data); Success_ - * _default_ ⇒ _Error("must be a tail proof")_ +``` +case + ... + result == TimeoutError ⇒ case + not expired(H_h, P) ⇒ fail with "message timeout not yet reached" + otherwise ⇒ f_type(R, TimeoutError) + ... +``` -which processes timeouts in order, and adds one more condition to the queues: +This adds one more guarantee: -_A:Mk,v,h =_ ∅ _if message i was sent and timeout proven before height h (and the receipts for all messages j < i were also handled)_ +`A:M_kvh == ∅` if message i was sent and timeout proven before height h (and the receipts for all messages j < i were also handled). -Now chain A can rollback all transactions that were blocked by this flood of unrelayed messages, without waiting for chain B to process them and return a receipt. Adding reasonable time outs to all packets allows us to gracefully handle any errors with the IBC relay processes, or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages, and imposes some reasonable upper limit (or just assigns it automatically), we can guarantee that if message _i_ is not processed by the upper limit of the timeout period, then all previous messages must also have either been processed or reached the timeout period. +Now chain `A` can rollback all transactions that were blocked by this flood of unrelayed packets - since they can never confirm - without waiting for chain `B` to process them and return a receipt. Adding reasonable timeouts to all packets allows us to gracefully handle any errors with the IBC relay processes or a flood of unrelayed "spam" IBC packets. If a blockchain requires a timeout on all messages and imposes some reasonable upper limit, we can guarantee that if a packet is not processed by the upper limit of the timeout period, then all previous packets must also have either been processed or reached the timeout period. Note that in order to avoid any possible "double-spend" attacks, the timeout algorithm requires that the destination chain is running and reachable. One can prove nothing in a complete network partition, and must wait to connect; the timeout must be proven on the recipient chain, not simply the absence of a response on the sending chain. +Additionally, if timestamp-based timeouts are used instead of height-based timeouts, the destination chain's consensus ruleset must enforce always-increasing timestamps (or the sending chain must use a more complex `expired` function). + ### 4.2 Cleanup -While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage requirement for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. +While we clean up the _send queue_ upon getting a receipt, if left to run indefinitely, the _receipt queues_ could grow without limit and create a major storage cost for the chains. However, we must not delete receipts until they have been proven to be processed by the sending chain, or we lose important information and sacrifice reliability. -The observant reader may also notice, that when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a message _i_, which **no longer exists** on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: +Additionally, with the above timeout implementation, when we perform the timeout on the sending chain, we do not update the _receipt queue_ on the receiving chain, and now it is blocked waiting for a packet `i`, which no longer exists on the sending chain. We can update the guarantees of the receipt queue as follows to allow us to handle both: -_B:Mk,v,h =_ ∅ _if message i was not received before height h_ +`B:M_kvh == ∅` if packet `i` was not received before height `h` -_B:Mk,v,h =_ ∅ _if message i was provably resolved on the sending chain before height h_ +`B:M_kvh == ∅` if packet i was provably resolved on the sending chain before height `h` -_B:Mk,v,h _ ≠ ∅ _otherwise (if message i was processed before height h, and no ack of receipt from the sending chain)_ +`B:M_kvh /= ∅` otherwise (if packet `i` was processed before height `h` but chain `A` has not handled the receipt) Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. -Through the definition of the send queue above, we see that all messages _i < head_ have been fully processed, and all messages _head <= i < tail_ are awaiting processing. By proving a much advanced _head_ of the _send queue_, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance our local _receipt queue_ to the new head of the remote _send queue_. +Through the definition of the send queue, we know that all packets `i < head` have been fully processed and all packets `head <= i < tail` are awaiting processing. By proving a much advanced `head` of `outgoing_A`, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance `incoming_B` to the new head of `outgoing_A`. _S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ * _qA.receipt =_ ∅ ⇒ _Error("unknown sender"),_ @@ -86,6 +91,8 @@ _S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go forward"),_ * _default_ ⇒ _advance(qA.receipt , head); Success_ -This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of messages that have been processed by the receiving chain, but not yet posted to the sending chain, _tail(B:qA.reciept ) > head(A:qB.send )_. As such, the _advance_ function must not modify any messages between the head and the tail. +`cleanup` can be invoked to resolve all outstanding packets up to and including `head` with one Merkle proof. This handles both recovering from timeouts and routine cleanup to recover storage. + +This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of packets that have been processed by the receiving chain, but not yet posted to the sending chain, `tail(incoming_B) > head(outgoing_A)`. As such, `advance` must not modify any packets between the head and the tail. ![Cleaning up Packets](images/CleanUp.png) From 43f97496cf9226d398b4dedc4173ee23cf8c71c6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Apr 2018 11:57:40 +0200 Subject: [PATCH 30/32] Update optimizations section --- docs/spec/ibc/channels-and-packets.md | 2 +- docs/spec/ibc/optimizations.md | 29 ++++++++++++--------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/docs/spec/ibc/channels-and-packets.md b/docs/spec/ibc/channels-and-packets.md index 50df1a1f82..0951ca6572 100644 --- a/docs/spec/ibc/channels-and-packets.md +++ b/docs/spec/ibc/channels-and-packets.md @@ -225,7 +225,7 @@ This allows applications to reason about ordering and enforce application-level ### 3.7 Packet relayer -The blockchain itself only records the *intention* to send the given message to the recipient chain - it does not open any actual network connections. We define the concept of a *relay* process that connects two chains by querying one for all outgoing packets & proofs, then committing those packets & proofs to the recipient chain. +The blockchain itself only records the *intention* to send the given message to the recipient chain. Physical network packet relay must be performed by off-chain infrastructure. We define the concept of a *relay* process that connects two chains by querying one for all outgoing packets & proofs, then committing those packets & proofs to the recipient chain. The relay process must have access to accounts on both chains with sufficient balance to pay for transaction fees but needs no other permissions. Relayers may employ application-level methods to recoup these fees. Any number of *relay* processes may be safely run in parallel. However, they will consume unnecessary fees if they submit the same proof multiple times, so some minimal coordination is ideal. diff --git a/docs/spec/ibc/optimizations.md b/docs/spec/ibc/optimizations.md index d017499e59..69776ab2ba 100644 --- a/docs/spec/ibc/optimizations.md +++ b/docs/spec/ibc/optimizations.md @@ -2,7 +2,7 @@ ([Back to table of contents](README.md#contents)) -The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. All messages are processed exactly once and in order, and applications can guarantee invariants over their combined state on both chains. IBC can be further extended and optimized to provide additional guarantees and minimize costs on the underlying blockchains. We detail two extensions: packet timeouts, and packet cleanup. +The above sections describe a secure messaging protocol that can handle all normal situations between two blockchains. All messages are processed exactly once and in order, and applications can guarantee invariants over their combined state on both chains. IBC can be further extended and optimized to provide additional guarantees and minimize costs on the underlying blockchains. We detail two extensions: packet timeouts and packet cleanup. ### 4.1 Timeouts @@ -24,7 +24,7 @@ For a sending chain `A` and a receiving chain `B`, with an IBC packet `P={_, i, We can make a few modifications of the above protocol to allow us to prove timeouts, by adding some fields to the messages in the send queue and defining an expired function that returns true iff `h > maxHeight` or `timestamp(H_h) > maxTime`. -`P = {type, sequence, source, destination, data, maxHeight, maxTime}` +`P = (type, sequence, source, destination, data, maxHeight, maxTime)` `expired(H_h, P) ⇒ true | false` @@ -78,21 +78,18 @@ Additionally, with the above timeout implementation, when we perform the timeout Consider a connection where many messages have been sent, and their receipts processed on the sending chain, either explicitly or through a timeout. We wish to quickly advance over all the processed messages, either for a normal cleanup, or to prepare the queue for normal use again after timeouts. -Through the definition of the send queue, we know that all packets `i < head` have been fully processed and all packets `head <= i < tail` are awaiting processing. By proving a much advanced `head` of `outgoing_A`, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance `incoming_B` to the new head of `outgoing_A`. +Through the definition of the send queue, we know that all packets `i < head` have been fully processed and all packets `head <= i < tail` are awaiting processing. By proving a much advanced `head` of `outgoing_B`, we can demonstrate that the sending chain already handled all messages. Thus, we can safely advance `incoming_A` to the new head of `outgoing_B`. -_S:IBCcleanup(A, Mk,v,h)_ ⇒ _match_ - * _qA.receipt =_ ∅ ⇒ _Error("unknown sender"),_ - * _k = (\_, send, \_)_ ⇒ _Error("must be for the send queue"),_ - * _k = (d, \_, \_) and d_ ≠ _S_ ⇒ _Error("sent to a different chain"),_ - * _k_ ≠ _(\_, \_, head)_ ⇒ _Error("Need a proof of the head of the queue"),_ - * _Hh_ ∉ _TA_ ⇒ _Error("must submit header for height h"),_ - * _not valid(Hh ,Mk,v,h )_ ⇒ _Error("invalid merkle proof"),_ - * _head := v_ ⇒ _match_ - * _head <= head(qA.receipt)_ ⇒ _Error("cleanup must go forward"),_ - * _default_ ⇒ _advance(qA.receipt , head); Success_ +``` +cleanup(A, M_kvh, head) = case + incoming_A == ∅ => fail with "unknown sender" + H_h ∉ T_B => fail with "must submit header for height h" + not valid(H_h, M_kvh, head) => fail with "invalid Merkle proof of outgoing_B queue height" + head >= head(incoming_A) => fail with "cleanup must go forward" + otherwise => + advance(incoming_A, head) +``` -`cleanup` can be invoked to resolve all outstanding packets up to and including `head` with one Merkle proof. This handles both recovering from timeouts and routine cleanup to recover storage. - -This allows us to invoke the _IBCcleanup _function to resolve all outstanding messages up to and including _head_ with one merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of packets that have been processed by the receiving chain, but not yet posted to the sending chain, `tail(incoming_B) > head(outgoing_A)`. As such, `advance` must not modify any packets between the head and the tail. +This allows us to invoke the `cleanup` function to resolve all outstanding messages up to and including `index` with one Merkle proof. Note that if this handles both recovering from a blocked queue after timeouts, as well as a routine cleanup method to recover space. In the cleanup scenario, we assume that there may also be a number of packets that have been processed by the receiving chain, but not yet posted to the sending chain, `tail(incoming_B) > head(outgoing_A)`. As such, `advance` must not modify any packets between the head and the tail. ![Cleaning up Packets](images/CleanUp.png) From 9eeffaa06dca5b945c978c230452f3e5e992f7ea Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 23 Apr 2018 12:09:00 +0200 Subject: [PATCH 31/32] Fix vector clock reference --- docs/spec/ibc/references.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/ibc/references.md b/docs/spec/ibc/references.md index 9262ff99cf..ba9fbee697 100644 --- a/docs/spec/ibc/references.md +++ b/docs/spec/ibc/references.md @@ -6,7 +6,7 @@ [https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc](https://github.com/cosmos/cosmos/blob/master/WHITEPAPER.md#inter-blockchain-communication-ibc) ##### 2: -{ vector clock } +[https://en.wikipedia.org/wiki/Vector_clock](https://en.wikipedia.org/wiki/Vector_clock) ##### 3: [https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d](https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae#215d) From 7dc29c078537a65f364252c16606ca7d4a57cbc7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 2 May 2018 18:01:17 +0200 Subject: [PATCH 32/32] Update appendices --- docs/spec/ibc/README.md | 3 +- docs/spec/ibc/appendices.md | 57 +++++++++++------------------------- docs/spec/ibc/connections.md | 2 +- 3 files changed, 19 insertions(+), 43 deletions(-) diff --git a/docs/spec/ibc/README.md b/docs/spec/ibc/README.md index b6689a1768..e9448bca82 100644 --- a/docs/spec/ibc/README.md +++ b/docs/spec/ibc/README.md @@ -44,5 +44,4 @@ IBC was first outlined in the [Cosmos Whitepaper](https://github.com/cosmos/cosm 1. [Appendix B: IBC Queue Format](appendices.md#appendix-b-ibc-queue-format) 1. [Appendix C: Merkle Proof Format](appendices.md#appendix-c-merkle-proof-formats) 1. [Appendix D: Byzantine Recovery Strategies](appendices.md#appendix-d-byzantine-recovery-strategies) - 1. [Appendix E: Universal IBC Packets](appendices.md#appendix-e-universal-ibc-packets) - 1. [Appendix F: Tendermint Header Proofs](appendices.md#appendix-f-tendermint-header-proofs) + 1. [Appendix E: Tendermint Header Proofs](appendices.md#appendix-e-tendermint-header-proofs) diff --git a/docs/spec/ibc/appendices.md b/docs/spec/ibc/appendices.md index 1e57d3c5ad..751924f469 100644 --- a/docs/spec/ibc/appendices.md +++ b/docs/spec/ibc/appendices.md @@ -4,11 +4,9 @@ ## Appendix A: Encoding Libraries -{ figure out what encoding IBC actually uses } - The specification has focused on semantics and functionality of the IBC protocol. However in order to facilitate the communication between multiple implementations of the protocol, we seek to define a standard syntax, or binary encoding, of the data structures defined above. Many structures are universal and for these, we provide one standard syntax. Other structures, such as _Hh , Uh , _and _Xh_ are tied to the consensus engine and we can define the standard encoding for tendermint, but support for additional consensus engines must be added separately. Finally, there are some aspects of the messaging, such as the envelope to post this data (fees, nonce, signatures, etc.), which is different for every chain, and must be known to the relay, but are not important to the IBC algorithm itself and left undefined. -In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: ethereum's rlp[[6](./references.md#6)] and google's protobuf[[7](./references.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. +In defining a standard binary encoding for all the "universal" components, we wish to make use of a standardized library, with efficient serialization and support in multiple languages. We considered two main formats: Ethereum's RLP[[6](./references.md#6)] and Google's Protobuf[[7](./references.md#7)]. We decided for protobuf, as it is more widely supported, is more expressive for different data types, and supports code generation for very efficient (de)serialization codecs. It does have a learning curve and more setup to generate the code from the type specifications, but the ibc data types should not change often and this code generation setup only needs to happen once per language (and can be exposed in a common repo), so this is not a strong counter-argument. Efficiency, expressiveness, and wider support rule in its favor. It is also widely used in gRPC and in many microservice architectures. The tendermint-specific data structures are encoded with go-wire[[8](./references.md#8)], the native binary encoding used inside of tendermint. Most blockchains define their own formats, and until some universal format for headers and signatures among blockchains emerge, it seems very premature to enforce any encoding here. These are defined as arbitrary byte slices in the protocol, to be parsed in an consensus engine-dependent manner. @@ -16,9 +14,7 @@ For the following appendixes, the data structure specifications will be in proto ## Appendix B: IBC Queue Format -{ include queue details here instead of in the other section } - -The foundational data structure of the IBC protocol are the message queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: +The foundational data structure of the IBC protocol are the packet queues stored inside each chain. We start with a well-defined binary representation of the keys and values used in these queues. The encodings mirror the semantics defined above: _key = _(_remote id, [send|receipt], [head|tail|index])_ @@ -26,59 +22,42 @@ _Vsend = (maxHeight, maxTime, type, data)_ _Vreceipt = (result, [success|error code])_ -Keys and values are binary encoded and stored as bytes in the merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the merkle proofs for deterministically generating the sequence of hashes, and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the merkle proof and header, the bytes can be deserialized and the contents interpreted by the protocol. +Keys and values are binary encoded and stored as bytes in the Merkle tree in order to generate the root hash stored in the block header, which validates all proofs. They are treated as arrays of bytes by the Merkle proofs for deterministically generating the sequence of hashes and passed as such in all interchain messages. Once the validity of a key value pair has been determined from the Merkle proof and header, the payload bytes can be deserialized and interpreted by the protocol. See [binary format as protobuf specification](./protobuf/queue.proto) ## Appendix C: Merkle Proof Formats -{ link to the implementation } +A Merkle tree (or a trie) generates a single hash that can be used to prove any element of the tree. In order to generate this hash, we first hash the leaf nodes, then hash multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree), and repeat for each level of the tree until we end up with one root hash. +With a known root hash (which is included in the block headers), the existence of a particular key/value in the tree can be proven by tracing the path to the value and revealing the (k-1) hashes for the paths not taken on each level ([[10](./references.md#10)]). -A merkle tree (or a trie) generates one hash that can prove every element of the tree. Generating this hash starts with hashing the leaf nodes. Then hashing multiple leaf nodes together to get the hash of an inner node (two or more, based on degree k of the k-ary tree). And continue hashing together the inner nodes at each level of the tree, until it reaches a root hash. Once you have a known root hash, you can prove key/value belongs to this tree by tracing the path to the value and revealing the (k-1) hashes for all the paths we did not take on each level. If this is new to you, you can read a basic introduction[[10](./references.md#10)]. +There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent Merkle proofs from a variety of data stores, and provide for chaining proofs to allow for subtrees. While searching for a solution, we did find the chainpoint proof format[[11](./references.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. -There are a number of different implementations of this basic idea, using different hash functions, as well as prefixes to prevent second preimage attacks (differentiating leaf nodes from inner nodes). Rather than force all chains that wish to participate in IBC to use the same data store, we provide a data structure that can represent merkle proofs from a variety of data stores, and provide for chaining proofs to allow for sub-trees. While searching for a solution, we did find the chainpoint proof format[[11](./references.md#11)], which inspired this design significantly, but didn't (yet) offer the flexibility we needed. +We generalize the left/right idiom to the concatenation a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility with arbitrary data stores. -We generalize the left/right idiom to concatenating a (possibly empty) fixed prefix, the (just calculated) last hash, and a (possibly empty) fixed suffix. We must only define two fields on each level and can represent any type, even a 16-ary Patricia tree, with this structure. One must only translate from the store's native proof to this format, and it can be verified by any chain, providing compatibility for arbitrary data stores. +The proof format also allows for chaining of trees, combining multiple Merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express Merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces its own Merkle root, and these are then hashed together to produce the root hash that is stored in the block header. -The proof format also allows for chaining of trees, combining multiple merkle stores into a "multi-store". Many applications (such as the EVM) define a data store with a large proof size for internal use. Rather than force them to change the store (impossible), or live with huge proofs (inefficient), we provide the possibility to express merkle proofs connecting multiple subtrees. Thus, one could have one subtree for data, and a second for IBC. Each tree produces their own merkle root, and these are then hashed together to produce the root hash that is stored in the block header. +A valid Merkle proof for IBC must either consist of a proof of one tree, and prepend `ibc` to all key names as defined above, or use a subtree named `ibc` in the first section, and store the key names as above in the second tree. -A valid merkle proof for IBC must either consist of a proof of one tree, and prepend "ibc" to all key names as defined above, or use a subtree named "ibc" in the first section, and store the key names as above in the second tree. - -For those who wish to minimize the size of their merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./references.md#12)], which is designed for optimal proof size, and freely available for use. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a quite large state. +In order to minimize the size of their Merkle proofs, we recommend using Tendermint's IAVL+ tree implementation[[12](./references.md#12)], which is designed for optimal proof size and released under a permissive license. It uses an AVL tree (a type of binary tree) with ripemd160 as the hashing algorithm at each stage. This produces optimally compact proofs, ideal for posting in blockchain transactions. For a data store of _n_ values, there will be _log2(n)_ levels, each requiring one 20-byte hash for proving the branch not taken (plus possible metadata for the level). We can express a proof in a tree of 1 million elements in something around 400 bytes. If we further store all IBC messages in a separate subtree, we should expect the count of nodes in this tree to be a few thousand, and require less than 400 bytes, even for blockchains with a large state. See [binary format as protobuf specification](./protobuf/merkle.proto) ## Appendix D: Byzantine Recovery Strategies -- Goal: keep application invariants -- Plasma-like fraud proofs -- Trusted entity - governance +IBC guarantees reliable, ordered packet delivery in the face of malicious nodes or relays, on top of which application invariants can be ensured. However, all guarantees break down when the blockchain on the other end of the connection exhibits Byzantine behavior. This can take two forms: a failure of the consensus mechanism (reverting previously finalized blocks), or a failure at the application level (not correctly performing the application-level functions on the packet). -### 4.3 Handling Byzantine failures +The IBC protocol can detect a limited class of Byzantine faults at the consensus level by identifying duplicate headers -- if an IBC module ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it can freeze the connection immediately. State reconciliation (e.g. restoring token balances to owners of vouchers on the other chain) must be handled by blockchain governance. -While every message is guaranteed reliable in the face of malicious nodes or relays, all guarantees break down when the entire blockchain on the other end of the connection exhibits byzantine faults. These can be in two forms: failures of the consensus mechanism (reversing "final" blocks), or failure at the application level (not performing the action defined by the message). +If there is a big divide in the remote chain and the validation set splits (e.g. 60-40 weighted) as to the direction of the chain, then the light-client header update protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then the connection(s) will need to be reopened manually (by governance on the local chain) and set to the new header set(s). The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork. -The IBC protocol can only detect byzantine faults at the consensus level, and is designed to halt with an error upon detecting any such fault. That is, if it ever sees two different headers for the same height (or any evidence that headers belong to different forks), then it must freeze the connection immediately. The resolution of the fault must be handled by the blockchain governance, as this is a serious incident and cannot be predefined. +Another kind of Byzantine action is at the application level. Let us assume packets represent transfer of value. If chain `A` sends a message with `x` tokens to chain `B`, then it promises to remove `x` tokens from the local supply. And if chain `B` handles this message successfully, it promises to credit `x` token vouchers to the account indicated in the packet. If chain `A` does not remove tokens from supply, or chain `B` does not generate vouchers, the application invariants (conservation of supply & fungibility) break down. -If there is a big divide in the remote chain and they split eg. 60-40 as to the direction of the chain, then the light-client protocol will refuses to follow either fork. If both sides declare a hard fork and continue with new validator sets that are not compatible with the consensus engine (they don't have ⅔ support from the previous block), then users will have to manually tell their local client which chain to follow (or fork and follow both with different IDs). +The IBC protocol does not handle these kinds of errors. They must be handled individually by each application. Applications could use Plasma-like fraud proofs to allow state recovery on one chain if fraud can be proved on the other chain. Although complex to implement, a correct implementation would allow applications to guarantee their invariants as long as *either* blockchain's consensus algorithm behaves correctly (and this could be extended to `n` chains). Economic incentives can additionally be used to disincentivize any kind of provable fraud. -The IBC protocol doesn't have the option to follow both chains as the queue and associated state must map to exactly one remote chain. In a fork, the chain can continue the connection with one fork, and optionally make a fresh connection with the other fork (which will also have to adjust internally to wipe its view of the connection clean). +## Appendix E: Tendermint Header Proofs -The other major byzantine action is at the application level. Let us assume messages represent transfer of value. If chain A sends a message with X tokens to chain B, then it promises to remove X tokens from the local supply. And if chain B handles this message with a success code, it promises to credit X tokens to the account mentioned in the message. What if A isn't actually removing tokens from the supply, or if B is not actually crediting accounts? - -Such application level issues cannot be proven in a generic sense, but must be handled individually by each application. The activity should be provable in some manner (as it is all in an auditable blockchain), but there are too many failure modes to attempt to enumerate, so we rely on the vigilance of the participants in the extremely rare case of a rogue blockchain. Of course, this misbehavior is provable and can negatively impact the value of the offending chain, providing economic incentives for any normal chain not to run malicious applications over IBC. - -## Appendix E: Universal IBC Packets - -{ what is this } - -The structures above can be used to define standard encodings for the basic IBC transactions that must be exposed by a blockchain: _IBCreceive_, _IBCreceipt_,_ IBCtimeout_, and _IBCcleanup_. As mentioned above, these are not complete transactions to be posted as is to a blockchain, but rather the "data" content of a transaction, which must also contain fees, nonce, and signatures. The other IBC transaction types _IBCregisterChain_, _IBCupdateHeader_, and _IBCchangeValidators_ are specific to the consensus engine and use unique encodings. We define the tendermint-specific format in the next section. - -See [binary format as protobuf specification](./protobuf/messages.proto) - -## Appendix F: Tendermint Header Proofs - -{ is this finalized? } +{ Ensure this is correct. } **TODO: clean this all up** @@ -123,5 +102,3 @@ A validator change in Tendermint can be securely verified with the following che * Verify at least 2/3 of the voting power of our trusted set, which are also in the new set, properly signed a commit to the new header In that case, we can update to this header, and update the trusted validator set, with the same guarantees as above (the ability to slash at least one third of all staked tokens on any false proof). - - diff --git a/docs/spec/ibc/connections.md b/docs/spec/ibc/connections.md index 0e5c17cfeb..a5d5b939f0 100644 --- a/docs/spec/ibc/connections.md +++ b/docs/spec/ibc/connections.md @@ -28,7 +28,7 @@ To facilitate an IBC connection, the two blockchains must provide the following it is possible to prove `H_h'` where `C_h' /= C_h` and `dt(now, H_h) < P` 3. Given a trusted `H_h` and a Merkle proof `M_kvh` it is possible to prove `V_kh` -It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages `U_h'` and `X_h'`. The implementation of these requirements with Tendermint consensus is defined in [Appendix F](appendices.md#appendix-f-tendermint-header-proofs). Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. +It is possible to make use of the structure of BFT consensus to construct extremely lightweight and provable messages `U_h'` and `X_h'`. The implementation of these requirements with Tendermint consensus is defined in [Appendix E](appendices.md#appendix-e-tendermint-header-proofs). Another algorithm able to provide equally strong guarantees (such as Casper) is also compatible with IBC but must define its own set of update and change messages. The Merkle proof `M_kvh` is a well-defined concept in the blockchain space, and provides a compact proof that the key value pair `(k, v)` is consistent with a Merkle root stored in `H_h`. Handling the case where `k` is not in the store requires a separate proof of non-existence, which is not supported by all Merkle stores. Thus, we define the proof only as a proof of existence. There is no valid proof for missing keys, and we design the algorithm to work without it.

>>>>> gd2md-html alert: equation: use MathJax/LaTeX if your publishing platform supports it.
(
Back to top)(Next alert)
>>>>>