Skip to content
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

Merged
merged 33 commits into from
May 7, 2018
Merged

WIP: IBC Specification #454

merged 33 commits into from
May 7, 2018

Conversation

ethanfrey
Copy link
Contributor

@ethanfrey ethanfrey commented Feb 13, 2018

Converted most recent IBC spec into markdown (Issue #458)

Need to work with Zarko to refine this.

@ethanfrey ethanfrey requested a review from ebuchman as a code owner February 13, 2018 20:19
@codecov
Copy link

codecov bot commented Feb 13, 2018

Codecov Report

Merging #454 into develop will increase coverage by 3.29%.
The diff coverage is n/a.

@@             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

@adrianbrink
Copy link
Contributor

adrianbrink commented Feb 26, 2018

@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 proofs.md and queues.md.


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.
Copy link

@milosevic milosevic Feb 27, 2018

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.

Copy link
Contributor Author

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)?

Copy link
Contributor

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.


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.

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.

Copy link
Contributor Author

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

Copy link
Contributor

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.


_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.

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.

Copy link
Contributor

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.


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
Copy link

@milosevic milosevic Feb 27, 2018

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.

Copy link
Contributor

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.

@@ -0,0 +1,58 @@
## 2 Proofs
Copy link

@milosevic milosevic Feb 27, 2018

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.

Copy link
Contributor

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?


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.

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?

Copy link
Contributor

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.


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.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Semantics


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).

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).


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>)_ &#8594; _B:receive(msg<sub>i </sub>)_

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.


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_

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?

### 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.

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?

Copy link
Contributor

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.


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)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Routing is never defined.

Copy link
Contributor Author

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.

Copy link
Contributor

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.


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.

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.

Copy link
Contributor

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.

* _q<sub>S.receipt</sub> =_ &#8709; &#8658; _Error("unregistered sender"),_
* _k = (\_, reciept, \_)_ &#8658; _Error("must be a send"),_
* _k = (d, \_, \_) and d_ &#8800; _A_ &#8658; _Error("sent to a different chain"),_
* _k = (\_, send, i) and head(q<sub>S.receipt</sub>)_ &#8800; _i_ &#8658; _Error("out of order"),_

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?

Copy link
Contributor

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?


### 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.

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.


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_:

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?

Copy link
Contributor

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?

@martindale
Copy link

I'm closing this in favor of #608. Thanks!

@cwgoes
Copy link
Contributor

cwgoes commented Apr 17, 2018

Per discussion in the IBC channel, a new consensus on terminology:

  • Connection refers to the mutual header-tracking between two chains. Only one connection needs to exist for any pair of two chains.
  • Channel refers to the set of sequenced queues guaranteeing cross-chain linearity. One connection can have an arbitrary number of channels. Applications which need shared linearity guarantees must use the same channel, whereas applications which do not can use separate channels.

@cwgoes
Copy link
Contributor

cwgoes commented Apr 23, 2018

@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.

@cwgoes cwgoes requested a review from fedekunze April 24, 2018 16:49
ebuchman
ebuchman previously approved these changes May 6, 2018
@cwgoes
Copy link
Contributor

cwgoes commented May 7, 2018

Notes from meeting:

  • Add f_type handler on source chain to section 3, clarify that the abstraction provided is reasoning about the order of handler execution on source/destination chains.
  • Could elect to reason about modules (f_type) - arbitrary state machines - coming online/offline dynamically.
  • Separate token transfer example into own section, with implementation semantics.
  • Separate IBC specification paper / IBC implementation description.

@ebuchman ebuchman merged commit 297dc54 into develop May 7, 2018
@ebuchman ebuchman deleted the feature/ibc branch May 7, 2018 13:43
@cwgoes cwgoes mentioned this pull request May 7, 2018

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.

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.

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

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`

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.

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)].

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

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`.

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`.

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`

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.

ParthDesai pushed a commit to ChorusOne/cosmos-sdk that referenced this pull request Apr 19, 2021
kocubinski pushed a commit that referenced this pull request Jul 4, 2023
* 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]>
Neoplayer pushed a commit to Neoplayer/cosmos-sdk that referenced this pull request Jul 18, 2023
kocubinski pushed a commit that referenced this pull request Oct 24, 2023
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants