cosmos-sdk/docs/spec/ibc/proofs.md

6.4 KiB
Raw Blame History

2 Proofs

(Back to table of 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).

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

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.3 Establishing a Root of Trust

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

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

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.

More formally, given existing set of trust T = {(Hi , Ci ), (Hj , Cj ), …}, we must provide:

valid(T, Xh | Uh )[true | false | unknown]

if Hh-1T then:

  • valid(T, Xh | Uh )[true | false]
  • ∃ (Uh | Xh) ⇒ valid(T, Xh | Uh) {aren't there infinite? why is this necessary}

if ChT then

  • valid(T, Uh )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 )

Define max(T) as max(h, where HhT). 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.

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.