-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: IBC Specification #454
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #454 +/- ##
===========================================
+ Coverage 62.18% 65.48% +3.29%
===========================================
Files 71 67 -4
Lines 3443 3546 +103
===========================================
+ Hits 2141 2322 +181
+ Misses 1129 1034 -95
- Partials 173 190 +17 |
@rigelrozanski @sunnya97 @zmanian @milosevic I think the specification is quite nice. In some parts it might be too wordy and we can make it more dense and concise. I'd like your feedback, specifically on |
docs/spec/ibc/overview.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused with the notion of queue at this level as I thought IBC is TCP for blockchain apps. Queue might be right data structure at an implementation level, but we need to be clear what is the semantics of the communication primitive that is being exposed to the blockchain app through basic send/receive interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't TCP also have the notion of a queue (You get messages in FIFO order, even if the network delivers them out of order)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added several sections about the kinds of invariants applications may want to enforce, and how they could enforce them through IBC.
docs/spec/ibc/overview.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem like a proper abstraction level ("posting block headers and merkle proofs") for an overview; seems like implementation details.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is essential.
We can say "proof that the blockchain signed the packet", but that is a bit abstract.
Header and merkle proof is a standard way of saying the above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Header / proof details have been moved from the the overview to connection / channel / packet details.
docs/spec/ibc/overview.md
Outdated
|
||
_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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Provable, attributable and root of trust terms are very abstract. I am not sure it really helps. I feel we need more formalisation regarding semantics and properties of the communication primitive we want to provide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed definitions of a few terms, clarified others. Open to more formalization if you have suggestions.
docs/spec/ibc/overview.md
Outdated
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IBC as an abstraction relies on some communication infrastructure (protocol) similar like TCP depends on IP. It would be good stating what we assume from the underlying communication protocol, and then as part of the underlying model we can say that messages can be dropped, modified or duplicated, that we assume asynchronous network, etc. As there is no clean way to deal with corrupted blockchain (forks) we should probably assume that a blockchain app that is creating and receiving IBC messages is a correct process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added briefly in overview, and in more detail in Channels & Packets
.
Added appendix on Byzantine recovery, which we should at least discuss even if we don't provide implementation details.
docs/spec/ibc/proofs.md
Outdated
@@ -0,0 +1,58 @@ | |||
## 2 Proofs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel that this section would be much more clear if done in the context of proving validity of the message sent using IBC primitive, i.e., one way to look at this content is that it explains how we can ensure message validity in blockchain systems with instant finality. But I don't see big benefit in being abstract at this level as Tendermint is at this point only blockchain system with instant finality. We can be abstract at the level of IBC primitive by defining what we mean by validity property, and then show how we can provide it in the context of Tendermint-based blockchain systems.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section has been changed to describe the primitives required to implement an IBC "connection", and the lifecycle of connections. Do you think it's still too abstract?
docs/spec/ibc/queues.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"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." I am not sure about the semantic that is being implied by this sentence as it consider the content of IBC message to be an action. Is IBC payload agnostic or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarified in several places that this spec is payload-agnostic.
docs/spec/ibc/queues.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is queue the semantics of IBC primitive or it is only used at the implementation level?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Semantics
docs/spec/ibc/queues.md
Outdated
|
||
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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ordering semantics should probably be defined as a property of the communication primitive (IBC).
docs/spec/ibc/queues.md
Outdated
|
||
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(msg<sub>i </sub>)_ → _B:receive(msg<sub>i </sub>)_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I am not wrong, it is never defined what is the semantics of send and receive calls.
docs/spec/ibc/queues.md
Outdated
|
||
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**: _q<sub>head</sub> = q<sub>tail</sub> = 0_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is IBC interface defined with those primitives?
docs/spec/ibc/queues.md
Outdated
### 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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is payload blob or we assume some structure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Payload is a blob, clarified this in a few places.
docs/spec/ibc/queues.md
Outdated
|
||
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:q<sub>B.send</sub>[i]_ are stored at _B:q<sub>A.receipt</sub>[i]_. (read: the message at index _i_ in the _send_ queue for chain B as stored on chain A) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Routing is never defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unclear.
Routing happens inside one blockchain to the appropriate dapp/smart contract.
It's not part of the IBC communication protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated IBC send packet definition to include an opaque type field for routing - this is only necessary to pick a handler on the destination chain, which needs to return success
/failure
.
docs/spec/ibc/queues.md
Outdated
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we explain how "destination chain is properly registered". More general, I feel that it is missing discussion on connection creation and connection lifecycle as I assume IBC is connection oriented primitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Connection lifecycle added to the Connections
section.
docs/spec/ibc/queues.md
Outdated
* _q<sub>S.receipt</sub> =_ ∅ ⇒ _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(q<sub>S.receipt</sub>)_ ≠ _i_ ⇒ _Error("out of order"),_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we buffer messages if they are received out of order? Or we assume that on sending side, we don't send message with index i+1 before receiving receipt for message i?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure what you mean by "buffer messages" - off-chain packet relayers can store packets as they receive them, but they must be committed to the destination chain in order.
At the moment, receipt and send ordering are independent. It would be possible to add the constraint you specify (receipt for packet i must be received before packet i+1 can be sent); did you have in mind any potential applications which would need that guarantee?
docs/spec/ibc/queues.md
Outdated
|
||
### 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atomic swap is one of use cases for IBC but I am not sure that IBC should be having semantics of atomic swap. I am not sure we should discuss it here.
docs/spec/ibc/queues.md
Outdated
|
||
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:q<sub>A.receipt</sub>_. 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_: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want IBC receipts to have this semantics, i.e., that it means that message is being processed by the destination chain? This is different than TCP semantics. It feels like tightly coupling apps. What is the reason for doing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so, as this allows us to release escrowed assets on chain A if processing failed - since we know vouchers were never minted on chain B. What alternative semantics would you suggest?
I'm closing this in favor of #608. Thanks! |
Per discussion in the IBC channel, a new consensus on terminology:
|
@milosevic I haven't finished the appendices yet, but this is at a point where your review would be helpful. Substantive changes are summarized in the above comment; I've also tried to unify the wording & notation. Also responded to your comments on the last version. |
Notes from meeting:
|
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by linearity?
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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"subset of the consensus ruleset" this is not clear and sounds very complex. Maybe you can remove the second part of the sentence.
|
||
### 2.1 Definitions | ||
|
||
- Chain `A` is the source blockchain from which the IBC packet is sent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about defining what is source and destination chain and then use A and B just to refer to it in follow up definitions.
- 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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use V_k^h or V(h,k)
- `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 `H_h` defines a signature and is thus attributable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure to understand the benefit of introducing the term attributable. Maybe we can suppress it.
|
||
#### 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)]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have agreed to modify Tendermint so there is no requirement on less than one-third validator set change. Maybe you can remove this requirement.
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`. | ||
|
||
#### 2.3.3 Closing a connection | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. We are mixing connection management with application
specific stuffs. We should define what it means closing connection and then
upper layer responsibility is to use it properly and leave system in the
clear and proper state before closing connection. For some applications,
closing connection will not be an option.
|
||
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`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part could probably be made more clear to emphasise better what is the channel as an abstraction, or removed to different document.
|
||
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`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be moved to introduction as a motivation or in example section.
|
||
Each IBC-supporting blockchain must provide a queue abstraction with the following functionality: | ||
|
||
`init` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why formalism for queue, it seems like standard queue set of operations.
* feat: Add gas limits for queries (#454) * WIP on adding query gas limit * Add baseapp test * Add default * Add changelog * Delete extra newline * Add allow list * For some reason, in our SDK fork gas panics when gas used = gas limit (cherry picked from commit 8dc41d6) # Conflicts: # baseapp/baseapp_test.go * Fix merge conflict in test --------- Co-authored-by: Dev Ojha <[email protected]> Co-authored-by: Dev Ojha <[email protected]>
Co-authored-by: Roman <[email protected]>
* WIP on adding query gas limit * Add baseapp test * Add default * Add changelog * Delete extra newline * Add allow list * For some reason, in our SDK fork gas panics when gas used = gas limit
Converted most recent IBC spec into markdown (Issue #458)
Need to work with Zarko to refine this.