概述
IBC 协议通过使用对手方状态机的客户端来验证数据包消息,从而在不同账本上的应用之间提供安全的数据包流转。ICS-4 定义了两条链之间数据包流转的核心逻辑,以及它们为实现通信必须作出的可证明承诺;而本标准 ICS-2 则规定了链应当如何验证对手方的 IBC 可证明承诺,这对于安全地接收和处理来自对手方的数据包流转消息至关重要。 本标准重点说明如何跟踪对手方共识并验证状态机;它还规定了实现区块链间通信(IBC)协议的状态机之共识算法必须满足的属性。 这些属性对于在更高层协议抽象中实现高效且安全的验证是必要的。 IBC 中用于验证远程状态机状态更新的算法称为有效性谓词。 将有效性谓词与一个可信状态(即验证者假定为正确的状态)配对, 即可在宿主状态机上为远程状态机实现一个轻客户端(通常简称为客户端)。 除状态更新验证之外,每个轻客户端还能够通过错误行为谓词检测共识错误行为。 除本规范所描述的属性外,IBC 不对状态机及其共识算法的内部运行方式施加任何要求。 状态机可以是一个使用私钥签署操作的单一进程(即所谓的 “solo machine”),也可以是一个一致签名的进程仲裁集, 也可以是多个运行拜占庭容错共识算法(例如 Tendermint)的进程,或其他尚未被发明的配置 —— 从 IBC 的视角看,状态机完全由其轻客户端验证逻辑和错误行为检测逻辑来定义。 本标准还规定了如何注册轻客户端的功能,以及 IBC 协议如何存储和更新其数据。 存储的客户端实例可以被第三方参与者检查, 例如用户检查状态机的状态并决定是否发送 IBC 数据包。动机
IBC 协议需要能够验证另一状态机(即远程状态机)状态的更新。 这意味着只能接受由远程状态机的共识算法达成一致的状态更新。 远程状态机的轻客户端,就是使参与者能够验证该状态机状态更新的算法。 需要注意的是,轻客户端通常不会包含对完整状态转移逻辑的验证 (因为那等同于直接执行另一条状态机),但在特定情况下, 它可以选择验证状态转移的某些部分。 本标准对轻客户端模型及其要求进行了形式化。 因此,只要提供满足所列要求的必要轻客户端算法, IBC 协议就可以轻松集成运行新型共识算法的新状态机。 IBC 协议也可用于与概率终局性共识算法交互。 在这种情况下,不同应用可能需要不同的有效性谓词。对于概率终局性共识,有效性谓词由终局阈值定义(例如,该阈值定义了一个区块之上还需要叠加多少个区块,才可将其视为已终局)。 因此,客户端可以作为其他客户端的阈值视图: 一个仅写客户端可用于存储状态更新(但不具备验证这些更新的能力), 而多个具有不同终局阈值的只读客户端(即在经过相应确认深度后 状态更新才被视为终局)则用于验证状态更新。 客户端接口也应当以安全的方式构造,以便在运行时提供自定义验证逻辑 来定义自定义客户端,前提是底层状态机能够提供适当的 gas 计量机制, 用于对计算和存储收费。例如,在支持 WASM 执行的宿主状态机上, 可以在创建客户端实例时,将有效性谓词和错误行为谓词 作为可执行的 WASM 函数提供。定义
-
Consensus是一种生成状态更新的算法。它接收状态机的前一状态, 以及一组消息(即状态机交易),并生成该状态机的有效状态更新。 每个状态机都必须拥有一个Consensus,从创世状态开始生成唯一且有序的状态更新列表。 本规范要求Consensus生成的状态更新 满足以下属性:- 每个状态更新都不得在状态更新列表中拥有多于一个直接后继。 换言之,状态机必须保证终局性和安全性。
- 每个状态更新最终都必须在状态更新列表中拥有一个后继。 换言之,状态机必须保证活性。
- 每个状态更新都必须是有效的(即有效的状态转移)。
换言之,
Consensus必须是诚实的, 例如,当Consensus是拜占庭容错共识算法时, 如 Tendermint,拜占庭式区块生产者的数量可以少于三分之一。
-
Height指定状态机状态更新的顺序,例如序列号。 这意味着每个状态更新都映射到一个Height。 -
ClientMessage是由客户端类型定义的任意消息,中继者可以提交该消息以更新客户端。ClientMessage可以是常规更新,用于添加新的共识状态以进行证明验证;也可以包含 错误行为,从而应当冻结客户端。 -
ValidityPredicate是一个函数,用于验证中继者为更新客户端而发送的ClientMessage。 使用ValidityPredicate在计算上应当比执行Consensus更高效。
-
ConsensusState是状态机在某个特定Height上状态的可信视图。 它必须包含足够的信息,以使ValidityPredicate能够验证未来的状态更新, 然后据此生成新的ConsensusState。 -
ClientState是客户端的状态。它必须向更高层协议抽象暴露接口, 例如,用于验证在特定Height的特定路径上某个特定值存在性的证明函数。 -
MisbehaviourPredicate是一个函数,用于检查Consensus的规则是否被破坏, 若被破坏,则客户端必须被冻结,即之后不能再生成任何ConsensusState。 客户端被冻结后,针对它的验证也将失败。
Misbehaviour是MisbehaviourPredicate用来判断是否发生 共识协议违规所需的证明。例如,当状态机 是一条区块链时,Misbehaviour可能由两个已签名的区块头组成, 它们在同一个Height上对应不同的ConsensusState。
期望属性
轻客户端必须提供状态验证函数,以便基于现有的ConsensusState
安全地验证远程状态机的状态。
这些状态验证函数使更高层协议抽象能够
验证远程状态机状态中的子组件。
ValidityPredicate 必须反映远程状态机及其 Consensus 的行为,也就是说,
ValidityPredicate 只能接受包含由远程状态机 Consensus 生成的状态更新的状态更新。
在发生错误行为时,ValidityPredicate 的行为可能与
远程状态机及其 Consensus 的行为不同(因为客户端不会执行远程状态机的 Consensus)。
在这种情况下,应当向宿主状态机提交 Misbehaviour,
从而导致客户端被冻结。客户端一旦被冻结,在客户端处理可以恢复之前,
必须先发生一种用于应对此情况的恢复机制。该恢复机制不属于
IBC 协议的范围,因为所需的具体恢复方式高度依赖具体场景。
技术规范
本规范概述了每种客户端类型必须定义的内容。客户端类型是一组定义, 包括运行轻客户端所需的数据结构、初始化逻辑、有效性谓词和错误行为谓词。 实现 IBC 协议的状态机可以支持任意数量的客户端 类型,并且每种客户端类型都可以使用不同的初始共识状态进行实例化,以跟踪 不同的共识实例。 具体的客户端类型及其规范定义在本仓库的轻客户端章节中。数据结构
Height
Height 是由客户端类型定义的不透明数据结构。
它必须构成一个偏序集,并提供比较操作。
LT(小于)、EQ(等于)或 GT(大于)。
在本规范其余部分中,>=、>、===、<、<= 都被定义为 compare 的别名。
高度类型还必须存在一个零元素,记为 0,它小于所有非零高度。
ConsensusState
ConsensusState 是由客户端类型定义的不透明数据结构,有效性谓词使用它来
验证新的提交和状态根。该结构很可能包含由
共识过程生成的最近一次提交,包括签名和验证者集元数据。
ConsensusState 必须从 Consensus 的实例生成,并且该实例会为每个 ConsensusState 分配唯一高度
(使得每个高度恰好对应一个共识状态)。
同一高度不得存在两个有效的 ConensusState。
此类事件称为“等价双签”(equivocation),并且必须被归类为
错误行为。一旦发生,应生成并提交相应证明,以便冻结客户端,
并在必要时使先前的状态根失效。
ConsensusState 必须定义一个 getTimestamp() 方法,用于返回与该共识状态关联的、以秒为单位的时间戳。
该时间戳必须是对手方状态机中使用并由 Consensus 达成一致的时间戳。
ClientState
ClientState 是由某种客户端类型定义的不透明数据结构。
它可以维护任意内部状态,以跟踪已验证的根和过去的错误行为。
轻客户端在表示上是不透明的,不同的共识算法可以定义不同的轻客户端更新算法,
但它们必须向 IBC 处理器暴露这一组通用查询函数。
ClientMessage
ClientMessage 是由客户端类型定义的不透明数据结构,用于提供更新客户端所需的信息。
可以将 ClientMessage 提交给关联的客户端,以添加新的 ConsensusState 或更新 ClientState。它们通常包含高度、证明、承诺根,以及可能的有效性谓词更新。
CommitmentProof
CommitmentProof 是由客户端类型定义的不透明数据结构。
状态验证
客户端类型必须定义函数,用于认证客户端所跟踪的状态机的内部状态。 内部实现细节可以不同(例如,回环客户端可以直接从状态中读取,因此不需要任何证明)。verifyMembership 是通用的证明验证方法,用于验证在指定高度下,给定 CommitmentPath 处某个值存在的证明。如果验证不成功,必须返回错误。
调用方应当根据 CommitmentPrefix 和标准化路径(定义见 ICS 4)构造完整的 CommitmentPath。
verifyNonMembership 是通用的证明验证方法,用于验证在指定高度下,给定 CommitmentPath 不存在的证明。如果验证不成功,必须返回错误。
调用方应当根据 CommitmentPrefix 和标准化路径(定义见 ICS 24)构造完整的 CommitmentPath。
由于该验证方法旨在给予客户端实现完整控制权,因此客户端可以通过验证一个非空哨兵值 ABSENCE 的存在,来支持那些不提供不存在性证明的链。因此,在这些特殊情况下,所提供的证明将是存在性证明,而客户端将验证在给定高度下,该路径下存储的是 ABSENCE 值。
实现策略
回环
本地状态机的回环客户端只需从本地状态读取,而它必须能够访问该状态。简单签名
已知公钥的单体状态机客户端会检查由该本地状态机发送消息上的签名, 这些签名通过Proof 参数提供。height 参数可以用作防重放保护的 nonce。
多重签名或门限签名方案也可以采用这种方式使用。
代理客户端
代理客户端通过验证另一个(代理)状态机对目标状态机的验证来工作,具体方式是: 在证明中首先包含代理状态机上客户端状态的证明,然后包含目标状态机子状态相对于 代理状态机上该客户端状态的二级证明。这样,代理客户端就无需自行存储和跟踪目标状态机的共识状态, 代价是增加了对代理状态机正确性的安全假设。默克尔化状态树
对于具有默克尔化状态树的状态机客户端,这些函数可以实现为 MerkleTree 的存在性与不存在性证明。客户端实现可以选择针对对手链使用的特定树来实现这些方法,也可以使用通用树的 ICS-23verifyMembership 或 verifyNonMembership 方法,结合存储在 ClientState 中的已验证 Merkle 根,以及描述树构造方式的 ProofSpec,来验证任意符合 ICS-23 的树在特定高度下状态中某些特定键值对的存在或不存在。在这种情况下,初始化客户端时必须向其提供 ICS-23 的 ProofSpec。
子协议
IBC 处理器必须实现下文定义的函数。标识符校验
客户端存储在唯一的Identifier 前缀下。
本 ICS 不要求客户端标识符必须以特定方式生成,只要求它们是唯一的。
不过,如果有需要,也可以限制 Identifier 的空间。
可以提供校验函数 validateClientIdentifier。
validateClientIdentifier 将始终返回 true。
利用历史根
为了避免客户端更新(会改变状态根)与握手或数据包接收中携带证明的交易之间出现竞争条件, 许多 IBC 处理器函数允许调用方指定要引用的某个历史根,并通过高度进行查找。执行此操作的 IBC 处理器函数 必须确保同时对调用方传入的高度执行所有必要检查,以保证逻辑正确性。CreateClient
使用客户端状态和初始共识状态调用createClient 会创建一个新客户端。该客户端的发起者负责设置 ClientState 的全部初始参数以及初始信任根 ConsensusState。随后,客户端实现负责基于这些初始参数执行轻客户端的 ValidityPredicate。因此,一旦信任根被实例化,轻客户端便保证在 ClientState 所参数化的安全模型范围内维持该信任。如果用户曾验证某个客户端是对手链的有效客户端一次,那么只要 MisbehaviourPredicate 没有被触发,就可以保证它在未来仍然是有效客户端。然而,如果 MisbehaviourPredicate 被触发,则可以将其作为错误行为提交,以冻结 IBC 轻客户端操作。
CreateClient 输入:
clientType: string:这是客户端类型,用于引用链上的某个特定轻客户端实现。CreateClient 消息将创建给定客户端类型的一个新实例。
ClientState: bytes:这是针对给定客户端类型定义的不透明客户端状态。它将包含用于验证客户端更新以及基于某个 ConsensusState 执行证明验证所需的任何参数。ClientState 对由该客户端类型实现的安全模型进行参数化。
ConsensusState: bytes:这是针对给定客户端类型定义的不透明共识状态。它是提供的初始共识状态,并且必须能够被 ValidityPredicate 用于向客户端添加新的 ConsensusState。初始 ConsensusState 也可以用于证明验证,但这并非必需。
Height: Number:这是与初始共识状态关联的高度。
CreateClient 前置条件:
- 提供的
clientType被该链支持,并且 IBC 处理器能够将请求路由到它。
- 为该客户端生成唯一标识符
clientId - 提供的
ClientState会持久化到状态中,并可通过clientId取回。 - 提供的
ConsensusState会持久化到状态中,并可通过clientId和height取回。
- 提供的
ClientState对于该客户端类型无效。 - 提供的
ConsensusState对于该客户端类型无效。 Height不是正数。
RegisterCounterparty
IBC 第 2 版引入了一个registerCounterparty 过程。使用 clientId 调用 registerCounterparty 将会注册对手方的 clientId,对手方将使用该标识写入面向我们链的 packet 消息。所有通往我们链的 ICS24 可证明路径都将以对手方 clientId 作为键,因此每个客户端都必须知晓对手方的标识符,以便构造用于键验证的路径,并确保客户端之间存在一条经过认证的 packet 数据流,且该数据流不会被其他客户端写入。
registerCounterparty 还包含对手方链要使用的 CommitmentPrefix。大多数链不会将 ICS24 直接存储在 MerkleTree 的根下,而是会在一个自定义前缀下存储标准化路径,因此必须将此信息提供给对手方客户端,才能正确验证证明。CommitmentPrefix 被定义为字节数组的数组,以支持嵌套 Merkle 树。在这种情况下,外层数组中的每个元素都是嵌套结构中各层树的键,顺序从最上层树到最低层树。此时,ICS24 路径会附加到最低层树的键上(即 commitment prefix 的最后一个元素),从而得到用于证明验证的完整 CommitmentPath。
RegisterCounterparty 输入:
clientId: bytes:执行链上的 clientId。
counterpartyClientId: bytes:对手方链用于验证执行链的客户端标识符。
counterpartyCommitmentPrefix: []bytes:对手方链使用的前缀。
RegisterCounterparty 前置条件:
- 已经为
clientId创建了客户端
- 给定
clientId时,可以检索到counterpartyClientId。 - 给定
clientId时,可以检索到counterpartyCommitmentPrefix。
- 不存在与给定
clientId对应的客户端 - 已经针对给定
clientId调用过RegisterCounterparty
RegisterCounterparty 设置的信息对于使用我们客户端正确验证 IBC 消息证明至关重要。因此,必须对其进行适当认证。RegisterCounterparty 消息可以设计为无权限门槛,在这种情况下,这些字段必须通过客户端相对于对手方链进行认证,这可能会比较困难且繁琐。更推荐的做法是确保创建客户端的地址与注册对手方的地址相同。一旦客户端和对手方由同一创建者设置,用户就可以通过链下方式决定该配置是否安全。
Update
更新客户端是通过提交新的ClientMessage 来完成的。Identifier 用于指向逻辑将要更新的已存储 ClientState。当新的 ClientMessage 使用已存储的 ClientState 和先前存储的 ConsensusState 通过 ValidityPredicate 验证后,客户端随后必须添加一个具有新 Height 的新 ConsensusState。
如果客户端无法再被更新(例如,信任期已经过去),则新的 packet 流将无法处理。此时必须进行人工干预,以重置客户端状态或迁移客户端。这无法完全安全地自动完成,但实现 IBC 的链可以选择允许治理机制执行这些操作(甚至可以在多签或合约中按客户端/连接/channel 细分授权)。
UpdateClient 输入:
clientId: bytes:正在更新的客户端标识符。
clientMessage: bytes:根据给定 clientType 定义的、用于更新客户端的不透明 clientMessage。它必须包含我们希望从其更新的 trustedHeight。该 trustedHeight 将用于检索一个受信任的 ConsensusState,我们将利用它和 ValidityPredicate 更新到新的共识状态。
UpdateClient 前置条件:
- 已经为
clientId创建了客户端
- 一个新的
ConsensusState被添加到客户端,并以新的Height持久化保存 - 如果更新本身就是恶意行为的证明,实现 MAY 在
UpdateClient中自动检测恶意行为(例如,给定高度已经存在不同的ConsensusState,或者时间单调性被破坏)。建议在这种情况下自动冻结客户端,以避免还需要额外发送冗余的submitMisbehaviour消息。
ClientMessage中引用的受信任ConsensusState在状态中不存在ValidityPredicate(clientState, trustedConsensusState, trustedHeight)返回错误
Misbehaviour
如果对手方链的Consensus 被破坏,那么 relayer 可以提交相关证明作为恶意行为。一旦客户端被冻结,就不能再进行任何更新,且所有证明验证都会失败。当对手方 Consensus 的信任恢复,并且执行链上因 Consensus 破坏而导致的任何无效状态都已回滚后,客户端可以通过链下协议解冻。
SubmitMisbehaviour 输入:
clientId: bytes:将被冻结的客户端标识符。
clientMessage: bytes:根据给定 clientType 定义的、用于冻结客户端的不透明 clientMessage。它必须包含我们希望据以验证恶意行为的 trustedHeight。该 trustedHeight 将用于检索一个受信任的 ConsensusState,我们将利用它和 MisbehaviourPredicate 冻结客户端。它还必须包含所提交的恶意行为内容。
SubmitMisbehaviour 前置条件:
- 已经为
clientId创建了客户端。
- 客户端被冻结,在再次解冻之前,更新和证明验证都将失败。
ClientMessage中引用的受信任ConsensusState在状态中不存在。MisbehaviourPredicate(clientState, trustedConsensusState, trustedHeight, misbehaviour)返回false。
VerifyMembership 和 VerifyNonmembership
IBC 核心 packet 处理器使用在UpdateClient 中创建的共识状态来验证 ICS-4 标准化路径,以认证 packet 消息。为此,IBC packet 处理器会针对给定的 packet 流消息构造期望的键/值,并将期望的路径和值以及 relayer 提供的证明一并发送给客户端进行验证。需要注意的是,证明由 relayer 提供,但路径和值是 IBC packet 处理器针对给定 packet 构造的。因此,relayer 无法为未被发送的 packet 伪造证明。IBC Packet 处理器还必须能够证明给定路径的不属于性,以支持超时处理。因此,客户端必须暴露以下 verifyMembership 和 verifyNonMembership 方法:
clientId: bytes:将要验证证明的客户端标识符。Height: Number:证明将据以验证的共识状态高度。Path: CommitmentPath:被证明键的路径。在 IBC 协议中,这将是一个以对手方已注册CommitmentPrefix为前缀的 ICS24 标准化路径。Path必须由 IBC 处理器根据 IBC 消息构造,绝不能由 relayer 提供,因为 relayer 不可信。Value: Optional<bytes>:被证明的值。如果它非空,则这是成员证明;如果该值为 nil,则这是非成员证明。
- 已经为
clientId创建了客户端。 - 已为给定的
Height存储了一个ConsensusState。
- 在大多数情况下,证明验证应当是无状态的。如果证明验证属于签名检查,我们可能希望递增一个 nonce,以防止重放攻击。
CommitmentProof无法使用提供的CommitmentPath和Value,结合为所提供Height检索到的ConsensusState,成功完成验证。
属性与不变量
- 客户端标识符是不可变的,并遵循先到先得原则。客户端不能被删除(允许删除可能会在标识符被重用时,导致未来重放过去的 packet)。
向后兼容性
不适用。向前兼容性
IBC 实现可以按需添加新的客户端类型,只要它们符合此接口。示例实现
请参阅 ibc-go 中轻客户端的实现,了解如何实现你自己的客户端示例:(https://github.com/cosmos/ibc-go/blob/main/modules/light-clients)。历史
2019 年 3 月 5 日 - 初始草稿完成并作为 PR 提交 2019 年 5 月 29 日 - 多项修订,尤其是支持多个 commitment root 2019 年 8 月 15 日 - 为澄清客户端接口进行了重大重构 2020 年 1 月 13 日 - 针对客户端类型分离和路径修改的修订 2020 年 1 月 26 日 - 新增查询接口 2022 年 7 月 27 日 - 新增verifyClientState 函数,并将 ClientState 移至 provableStore
2022 年 8 月 4 日 - 修改 ClientState 接口及其关联处理器,以对齐 02-client-refactor ADR 中的变更:(https://github.com/cosmos/ibc-go/pull/1871)
2024 年 8 月 22 日 - IBC/TAO V2 变更
版权
本文所有内容均依据 Apache 2.0 许可证授权。Synopsis
The IBC protocol provides secure packet flow between applications on different ledgers by verifying the packet messages using clients of the counterparty state machines. While ICS-4 defines the core packet flow logic between two chains and the provable commitments they must make in order to communicate, this standard ICS-2 specifies how a chain verifies the IBC provable commitments of the counterparty which is crucial to securely receive and process a packet flow message arriving from the counterparty. This standard focuses on how to keep track of the counterparty consensus and verify the state machine; it also specifies the properties that consensus algorithms of state machines implementing the inter-blockchain communication (IBC) protocol are required to satisfy. These properties are necessary for efficient and safe verification in the higher-level protocol abstractions. The algorithm utilised in IBC to verify the state updates of a remote state machine is referred to as a validity predicate. Pairing a validity predicate with a trusted state (i.e., a state that the verifier assumes to be correct), implements the functionality of a light client (often shortened to client) for a remote state machine on the host state machine. In addition to state update verification, every light client is able to detect consensus misbehaviours through a misbehaviour predicate. Beyond the properties described in this specification, IBC does not impose any requirements on the internal operation of the state machines and their consensus algorithms. A state machine may consist of a single process signing operations with a private key (the so-called “solo machine”), a quorum of processes signing in unison, many processes operating a Byzantine fault-tolerant consensus algorithm (e.g., Tendermint), or other configurations yet to be invented — from the perspective of IBC, a state machine is defined entirely by its light client validation and misbehaviour detection logic. This standard also specifies how the light client’s functionality is registered and how its data is stored and updated by the IBC protocol. The stored client instances can be introspected by a third party actor, such as a user inspecting the state of the state machine and deciding whether or not to send an IBC packet.Motivation
The IBC protocol needs to be able to verify updates to the state of another state machine (i.e., the remote state machine). This entails accepting only the state updates that were agreed upon by the remote state machine’s consensus algorithm. A light client of the remote state machine is the algorithm that enables the actor to verify state updates of that state machine. Note that light clients will generally not include validation of the entire state transition logic (as that would be equivalent to simply executing the other state machine), but may elect to validate parts of state transitions in particular cases. This standard formalises the light client model and requirements. As a result, the IBC protocol can easily be integrated with new state machines running new consensus algorithms, as long as the necessary light client algorithms fulfilling the listed requirements are provided. The IBC protocol can be used to interact with probabilistic-finality consensus algorithms. In such cases, different validity predicates may be required by different applications. For probabilistic-finality consensus, a validity predicate is defined by a finality threshold (e.g., the threshold defines how many block needs to be on top of a block in order to consider it finalized). As a result, clients could act as thresholding views of other clients: One write-only client could be used to store state updates (without the ability to verify them), while many read-only clients with different finality thresholds (confirmation depths after which state updates are considered final) are used to verify state updates. Client interfaces should also be constructed so that custom validation logic can be provided safely to define a custom client at runtime, as long as the underlying state machine can provide an appropriate gas metering mechanism to charge for compute and storage. On a host state machine which supports WASM execution, for example, the validity predicate and misbehaviour predicate could be provided as executable WASM functions when the client instance is created.Definitions
-
Consensusis a state update generating algorithm. It takes the previous state of a state machine together with a set of messages (i.e., state machine transactions) and generates a valid state update of the state machine. Every state machine MUST have aConsensusthat generates a unique, ordered list of state updates starting from a genesis state. This specification expects that the state updates generated byConsensussatisfy the following properties:- Every state update MUST NOT have more than one direct successor in the list of state updates. In other words, the state machine MUST guarantee finality and safety.
- Every state update MUST eventually have a successor in the list of state updates. In other words, the state machine MUST guarantee liveness.
- Every state update MUST be valid (i.e., valid state transitions).
In other words,
ConsensusMUST be honest, e.g., in the caseConsensusis a Byzantine fault-tolerant consensus algorithm, such as Tendermint, less than a third of block producers MAY be Byzantine.
-
Heightspecifies the order of the state updates of a state machine, e.g., a sequence number. This entails that each state update is mapped to aHeight. -
ClientMessageis an arbitrary message defined by the client type that relayers can submit in order to update the client. The ClientMessage may be intended as a regular update which may add new consensus state for proof verification, or it may contain misbehaviour which should freeze the client. -
ValidityPredicateis a function that validates a ClientMessage sent by a relayer in order to update the client. Using theValidityPredicateSHOULD be more computationally efficient than executingConsensus.
-
ConsensusStateis the trusted view of the state of a state machine at a particularHeight. It MUST contain sufficient information to enable theValidityPredicateto validate future state updates, which can then be used to generate newConsensusStates. -
ClientStateis the state of a client. It MUST expose an interface to higher-level protocol abstractions, e.g., functions to verify proofs of the existence of particular values at particular paths at particularHeights. -
MisbehaviourPredicateis a function that checks whether the rules ofConsensuswere broken, in which case the client MUST be frozen, i.e., no subsequentConsensusStates can be generated. Verification against the client after it is frozen will also fail.
Misbehaviouris the proof needed by theMisbehaviourPredicateto determine whether a violation of the consensus protocol occurred. For example, in the case the state machine is a blockchain, aMisbehaviourmight consist of two signed block headers with differentConsensusStatefor the sameHeight.
Desired Properties
Light clients MUST provide state verification functions that provide a secure way to verify the state of the remote state machines using the existingConsensusStates.
These state verification functions enable higher-level protocol abstractions to
verify sub-components of the state of the remote state machines.
ValidityPredicates MUST reflect the behaviour of the remote state machine and its Consensus, i.e.,
ValidityPredicates accept only state updates that contain state updates generated by
the Consensus of the remote state machine.
In case of misbehavior, the behaviour of the ValidityPredicate might differ from the behaviour of
the remote state machine and its Consensus (since clients do not execute the Consensus of the
remote state machine). In this case, a Misbehaviour SHOULD be submitted to the host state machine,
which would result in the client being frozen. Once the client is frozen, a recovery mechanism to address
the situation must occur before client processing can presume. This recovery mechanism is out-of-scope
of the IBC protocol as the specific recovery needed is highly case-dependent.
Technical Specification
This specification outlines what each client type must define. A client type is a set of definitions of the data structures, initialisation logic, validity predicate, and misbehaviour predicate required to operate a light client. State machines implementing the IBC protocol can support any number of client types, and each client type can be instantiated with different initial consensus states in order to track different consensus instances. Specific client types and their specifications are defined in the light clients section of this repository.Data Structures
Height
Height is an opaque data structure defined by a client type.
It must form a partially ordered set & provide operations for comparison.
LT (less than), EQ (equal to), or GT (greater than) another height.
>=, >, ===, <, <= are defined through the rest of this specification as aliases to compare.
There must also be a zero-element for a height type, referred to as 0, which is less than all non-zero heights.
ConsensusState
ConsensusState is an opaque data structure defined by a client type, used by the validity predicate to
verify new commits & state roots. Likely the structure will contain the last commit produced by
the consensus process, including signatures and validator set metadata.
ConsensusState MUST be generated from an instance of Consensus, which assigns unique heights
for each ConsensusState (such that each height has exactly one associated consensus state).
There MUST NOT be two valid ConensusStates for the same height.
Such an event is called an “equivocation” and MUST be classified
as misbehaviour. Should one occur, a proof should be generated and submitted so that the client can be frozen
and previous state roots invalidated as necessary.
ConsensusState MUST define a getTimestamp() method which returns the timestamp in seconds associated with that consensus state.
This timestamp MUST be the timestamp used in the counterparty state machine and agreed to by Consensus.
ClientState
ClientState is an opaque data structure defined by a client type.
It may keep arbitrary internal state to track verified roots and past misbehaviours.
Light clients are representation-opaque — different consensus algorithms can define different light client update algorithms —
but they must expose this common set of query functions to the IBC handler.
ClientMessage
A ClientMessage is an opaque data structure defined by a client type which provides information to update the client.
ClientMessages can be submitted to an associated client to add new ConsensusState(s) and/or update the ClientState. They likely contain a height, a proof, a commitment root, and possibly updates to the validity predicate.
CommitmentProof
CommitmentProof is an opaque data structure defined by the client type.
State verification
Client types must define functions to authenticate internal state of the state machine which the client tracks. Internal implementation details may differ (for example, a loopback client could simply read directly from the state and require no proofs).verifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height. It MUST return an error if the verification is not successful.
The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 4).
verifyNonMembership is a generic proof verification method which verifies a proof of absence of a given CommitmentPath at the specified height. It MUST return an error if the verification is not successful.
The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
Since the verification method is designed to give complete control to client implementations, clients can support chains that do not provide absence proofs by verifying the existence of a non-empty sentinel ABSENCE value. Thus in these special cases, the proof provided will be an Existence proof, and the client will verify that the ABSENCE value is stored under the given path for the given height.
Implementation strategies
Loopback
A loopback client of a local state machine merely reads from the local state, to which it must have access.Simple signatures
A client of a solo state machine with a known public key checks signatures on messages sent by that local state machine, which are provided as theProof parameter. The height parameter can be used as a replay protection nonce.
Multi-signature or threshold signature schemes can also be used in such a fashion.
Proxy clients
Proxy clients verify another (proxy) state machine’s verification of the target state machine, by including in the proof first a proof of the client state on the proxy state machine, and then a secondary proof of the sub-state of the target state machine with respect to the client state on the proxy state machine. This allows the proxy client to avoid storing and tracking the consensus state of the target state machine itself, at the cost of adding security assumptions of proxy state machine correctness.Merklized state trees
For clients of state machines with Merklized state trees, these functions can be implemented as MerkleTree Existence and NonExistence proofs. Client implementations may choose to implement these methods for the specific tree used by the counterparty chain or they can use the tree-generic ICS-23verifyMembership or verifyNonMembership methods, using a verified Merkle
root stored in the ClientState, to verify presence or absence of particular key/value pairs in state at particular heights for any ICS-23 compliant tree given a ProofSpec that describes how the tree is constructed. In this case, the ICS-23 ProofSpec MUST be provided to the client on initialization.
Sub-protocols
IBC handlers MUST implement the functions defined below.Identifier validation
Clients are stored under a uniqueIdentifier prefix.
This ICS does not require that client identifiers be generated in a particular manner, only that they be unique.
However, it is possible to restrict the space of Identifiers if required.
The validation function validateClientIdentifier MAY be provided.
validateClientIdentifier will always return true.
Utilising past roots
To avoid race conditions between client updates (which change the state root) and proof-carrying transactions in handshakes or packet receipt, many IBC handler functions allow the caller to specify a particular past root to reference, which is looked up by height. IBC handler functions which do this must ensure that they also perform any requisite checks on the height passed in by the caller to ensure logical correctness.CreateClient
CallingcreateClient with the client state and initial consensus state creates a new client. The intiator of this client is responsible for setting all of the initial parameters of the ClientState and the initial root-of-trust ConsensusState. The client implementation is then responsible for executing the light client ValidityPredicate against these initial parameters. Thus, once a root-of-trust is instantiated; the light client guarantees to preserve that trust within the confines of the security model as parameterized by the ClientState. If a user verifies that a client is a valid client of the counterparty chain once, they can be guaranteed that it will remain a valid client into the future so long as the MisbehaviourPredicate is not triggered. If the MisbehaviourPredicate is triggered however, this can be submitted as misbehaviour to freeze the IBC light client operations.
CreateClient Inputs:
clientType: string: This is the client-type that references a particular light client implementation on the chain. The CreateClient message will create a new instance of the given client-type.
ClientState: bytes: This is the opaque client state as defined for the given client type. It will contain any parameters needed for verifying client updates and proof verification against a ConsensusState. The ClientState parameterizes the security model as implemented by the client type.
ConsensusState: bytes: This is the opaque consensus state as defined for the given client type. It is the initial consensus state provided and MUST be capable of being used by the ValidityPredicate to add new ConsensusStates to the client. The initial ConsensusState MAY also be used for proof verification but it is not necessary.
Height: Number: This is the height that is associated with the initial consensus state.
CreateClient Preconditions:
- The provided
clientTypeis supported by the chain and can be routed to by the IBC handler.
- A unique identifier
clientIdis generated for the client - The provided
ClientStateis persisted to state and retrievable given theclientId. - The provided
ConsensusStateis persisted to state and retrievable given theclientIdandheight.
- The provided
ClientStateis invalid given the client type. - The provided
ConsensusStateis invalid given the client type. - The
Heightis not a positive number.
RegisterCounterparty
IBC Version 2 introduces aregisterCounterparty procedure. Calling registerCounterparty with the clientId will register the counterparty clientId
that the counterparty will use to write packet messages intended for our chain. All ICS24 provable paths to our chain will be keyed on the counterparty clientId, so each client must be aware of the counterparty’s identifier in order to construct the path for key verification and ensure there is an authenticated stream of packet data between the clients that do not get written to by other clients.
The registerCounterparty also includes the CommitmentPrefix to use for the counterparty chain. Most chains will not store the ICS24 directly under the root of a MerkleTree and will instead store the standardized paths under a custom prefix, thus the counterparty client must be given this information to verify proofs correctly. The CommitmentPrefix is defined as an array of byte arrays to support nested Merkle trees. In this case, each element of the outer array is a key for each tree in the nested structure ordered from the top-most tree to the lowest level tree. In this case, the ICS24 path is appended to the key of the lowest-level tree (i.e. the last element of the commitment prefix) in order to get the full CommitmentPath for proof verification.
RegisterCounterparty Inputs:
clientId: bytes: The clientId on the executing chain.
counterpartyClientId: bytes: The identifier of the client used by the counterparty chain to verify the executing chain.
counterpartyCommitmentPrefix: []bytes: The prefix used by the counterparty chain.
RegisterCounterparty Preconditions:
- A client has already been created for the
clientId
- The
counterpartyClientIdis retrievable given theclientId. - The
counterpartyCommitmentPrefixis retrievable given theclientId.
- There does not exist a client for the given
clientId RegisterCounterpartyhas already been called for the givenclientId
RegisterCounterparty is setting information that will be crucial for proper proof verification of IBC messages using our client. Thus, it must be authenticated properly. The RegisterCounterparty message can be permissionless in which case the fields must be authenticated against the counterparty chain using the client which may prove difficult and cumbersome. It is RECOMMENDED to simply ensure that the client creator address is the same as the one that registers the counterparty. Once the client and counterparty are set by the same creator, users can decide if the configuration is secure out-of-band.
Update
Updating a client is done by submitting a newClientMessage. The Identifier is used to point to the
stored ClientState that the logic will update. When a new ClientMessage is verified using the ValidityPredicate with
the stored ClientState and a previously stored ConsensusState, the client MUST then add a new ConsensusState with a new Height.
If a client can no longer be updated (if, for example, the trusting period has passed),
then new packet flow will not be able to be processed. Manual intervention must take place to
reset the client state or migrate the client. This
cannot safely be done completely automatically, but chains implementing IBC could elect
to allow governance mechanisms to perform these actions
(perhaps even per-client/connection/channel in a multi-sig or contract).
UpdateClient Inputs:
clientId: bytes: The identifier of the client being updated.
clientMessage: bytes: The opaque clientMessage to update the client as defined by the given clientType. It MUST include the trustedHeight we wish to update from. This trustedHeight will be used to retrieve a trusted ConsensusState which we will use to update to a new consensus state using the ValidityPredicate.
UpdateClient Preconditions:
- A client has already been created for the
clientId
- A new
ConsensusStateis added to the client and persisted with a newHeight - Implementations MAY automatically detect misbehaviour in
UpdateClientif the update itself is proof of misbehaviour (e.g. There is already a differentConsensusStatefor the given height, or time monotonicity is broken). It is recommended to automatically freeze the client in this case to avoid having to send a redundantsubmitMisbehaviourmessage.
- The trusted
ConsensusStatereferenced in theClientMessagedoes not exist in state ValidityPredicate(clientState, trustedConsensusState, trustedHeight)returns an error
Misbehaviour
IfConsensus of the counterparty chain is violated, then the relayer can submit proof of this as misbehaviour. Once the client is frozen, no updates may take place and all proof verification will fail. The client may be unfrozen by an out-of-band protocol once trust in the counterparty Consensus is restored and any invalid state caused by the break in Consensus is reverted on the executing chain.
SubmitMisbehaviour Inputs:
clientId: bytes: The identifier of the client being frozen.
clientMessage: bytes: The opaque clientMessage to freeze the client as defined by the given clientType. It MUST include the trustedHeight we wish to verify misbehaviour from. This trustedHeight will be used to retrieve a trusted ConsensusState which we will use to freeze the client given the MisbehaviourPredicate. It MUST also include the misbehaviour being submitted.
SubmitMisbehaviour Preconditions:
- A client has already been created for the
clientId.
- The client is frozen, update and proof verification will fail until client is unfrozen again.
- The trusted
ConsensusStatereferenced in theClientMessagedoes not exist in state. MisbehaviourPredicate(clientState, trustedConsensusState, trustedHeight, misbehaviour)returnsfalse.
VerifyMembership and VerifyNonmembership
The IBC core packet handler uses the consensus states created inUpdateClient to verify ICS-4 standardized paths to authenticate packet messages. In order to do this, the IBC packet handler constructs the expected key/value for the given packet flow message and sends the expected path and value to the client along with the relayer-provided proof to the client for verification. Note that the proof is relayer provided, but the path and value are constructed by the IBC packet handler for the given packet. Thus, the relayer cannot forge proofs for packets that did not get sent. IBC Packet handler must also have the ability to prove nonmembership of a given path in order to enable timeout processing. Thus, clients must expose the following verifyMembership and verifyNonMembership methods:
clientId: bytes: The identifier of the client that will verify the proof.Height: Number: The height for the consensus state that the proof will be verified against.Path: CommitmentPath: The path of the key being proven. In the IBC protocol, this will be an ICS24 standardized path prefixed by theCommitmentPrefixregistered on the counterparty. ThePathMUST be constructed by the IBC handler given the IBC message, it MUST NOT be provided by the relayer as the relayer is untrusted.Value: Optional<bytes>: The value being proven. If it is non-empty this is a membership proof. If the value is nil, this is a non-membership proof.
- A client has already been created for the
clientId. - A
ConsensusStateis stored for the givenHeight.
- Proof verification should be stateless in most cases. In the case that the proof verification is a signature check, we may wish to increment a nonce to prevent replay attacks.
CommitmentProofdoes not successfully verify with the providedCommitmentPathandValuewith the retrievedConsensusStatefor the providedHeight.
Properties & Invariants
- Client identifiers are immutable & first-come-first-serve. Clients cannot be deleted (allowing deletion would potentially allow future replay of past packets if identifiers were re-used).
Backwards Compatibility
Not applicable.Forwards Compatibility
New client types can be added by IBC implementations at-will as long as they conform to this interface.Example Implementations
Please see the ibc-go implementations of light clients for examples of how to implement your own: (https://github.com/cosmos/ibc-go/blob/main/modules/light-clients).History
Mar 5, 2019 - Initial draft finished and submitted as a PR May 29, 2019 - Various revisions, notably multiple commitment-roots Aug 15, 2019 - Major rework for clarity around client interface Jan 13, 2020 - Revisions for client type separation & path alterations Jan 26, 2020 - Addition of query interface Jul 27, 2022 - Addition ofverifyClientState function, and move ClientState to the provableStore
August 4, 2022 - Changes to ClientState interface and associated handler to align with changes in 02-client-refactor ADR: (https://github.com/cosmos/ibc-go/pull/1871)
August 22, 2024 - Changes for IBC/TAO V2