概述
本规范文档描述了一个用于采用 Tendermint 共识的区块链的客户端(验证算法)。动机
使用 Tendermint 共识算法复制的各类状态机,可能希望通过 IBC 与其他复制状态机或单机状态机进行交互。定义
函数和术语的定义见 ICS 2。currentTimestamp 的定义见 ICS 24。
Tendermint 轻客户端使用 ICS 23 中定义的广义 Merkle 证明格式。
hash 是一种通用的抗碰撞哈希函数,并且可以方便地进行配置。
期望性质
本规范必须满足 ICS 2 中定义的客户端接口。关于“本会被欺骗”逻辑的说明
“本会被欺骗”检测的基本思想是:它使我们能够采取更保守的策略。当我们知道网络中其他某处、采用略有不同更新模式的另一轻客户端本可能被欺骗时,即使我们自己没有被欺骗,也可以冻结本轻客户端。 考虑一个由三条链A、B 和 C 组成的拓扑,以及两条针对链 A 的客户端 A_1 和 A_2,它们分别运行在链 B 和 C 上。会发生如下事件序列:
- 链
A在高度h_0生成一个区块(正确)。 - 客户端
A_1和A_2都被更新到高度h_0的区块。 - 链
A在高度h_0 + n生成一个区块(正确)。 - 客户端
A_1被更新到高度h_0 + n的区块(客户端A_2尚未更新)。 - 链
A在高度h_0 + k生成第二个区块(即存在双重签名/自相矛盾的区块),其中k <= n。
A_2(因为在高度 h_0 + k 处存在两个有效区块,且它们都比 A_2 已知的最新标头更新),但无法冻结 A_1,因为 A_1 已经推进到了 h_0 + k 之后。
可以说,这是不利的,因为 A_1 只是“运气好”,恰好在 A_2 尚未更新时完成了更新,而显然已经发生了某种拜占庭故障,这种情况很可能应当由人工或治理系统介入处理。“本会被欺骗”的思路,就是允许 A_1 从一个可配置的过去标头开始检测不当行为(因此在这个例子中,A_1 可以从 h_0 开始,也会被冻结)。
这里有一个自由参数,即 A_1 愿意回溯多远(当 A_1 已更新到 h_0 + n 时,n 可以大到什么程度,而 A_1 仍愿意去查找 h_0)?与此同时,还存在一个相反方向的顾虑:在解绑期过去之后,默认认为双重签名不再有成本,因此我们不希望为 IBC 客户端引入拒绝服务攻击向量。
因此,必要条件是:A_1 应当愿意回查到它仍然存储着的旧标头,但同时也应当对该不当行为执行“解绑期”检查;如果该不当行为相对于客户端本地时间已经早于解绑期,则应避免冻结客户端。如果担心时钟偏差,可以加入一个小的时间裕量。
技术规范
本规范依赖于 Tendermint 共识算法 和 轻客户端算法 的正确实例化。客户端状态
Tendermint 客户端状态会跟踪当前修订版本、当前验证者集合、信任期、解绑期、最新高度、最新时间戳(区块时间),以及可能存在的冻结高度。共识状态
Tendermint 客户端会为所有先前已验证的共识状态跟踪时间戳(区块时间)、下一验证者集合的哈希,以及承诺根(这些状态可以在解绑期过后被清理,但在此之前不应被清理)。高度
Tendermint 客户端的高度由两个uint64 组成:修订号,以及该修订中的高度。
0 的同时,将修订号加一,从而在零高度升级过程中保留超时语义。
标头
Tendermint 标头包含高度、时间戳、承诺根、验证者集合的哈希、下一验证者集合的哈希,以及提交该区块的验证者签名。提交给链上客户端的标头还包括完整的验证者集合,以及一个用于更新的受信任高度和验证者集合。这样可以减少链上客户端需要维护的状态量,并防止中继器更新中的竞争条件。ClientMessage 接口。
Misbehaviour
Misbehaviour 类型用于检测不当行为,并在适用时冻结客户端,以阻止后续数据包流转。
Tendermint 客户端的 Misbehaviour 由两个处于同一高度、且都会被轻客户端视为有效的标头组成。
ClientMessage 接口。
客户端初始化
Tendermint 客户端初始化需要一个(主观选择的)最新共识状态,其中包括完整的验证者集合。latestClientHeight 函数会返回最新存储的高度,并且每当一个新的(更近期的)标头被验证后都会更新该值。
有效性判定
Tendermint 客户端的有效性检查使用 Tendermint 规范 中描述的二分算法。如果提供的标头有效,则会更新客户端状态,并将新验证的承诺写入存储。恶意行为判定
函数checkForMisbehaviour 将检查某次更新是否包含恶意行为的证据。如果 ClientMessage 是一个头部,则通过检查存储中是否已存在冲突的共识状态,或该头部是否破坏时间单调性,来判断是否存在隐式的恶意行为证据。
更新状态
函数updateState 将对 Tendermint 客户端执行一次常规更新。它会向客户端存储中添加一个共识状态。如果该头部高于 clientState 上的最新高度,则会更新 clientState。
发生恶意行为时更新状态
函数updateStateOnMisbehaviour 会将冻结高度设置为一个非零的哨兵高度,以冻结整个客户端。
升级
该轻客户端所跟踪的链可以选择在状态中写入一个预先确定的特殊键,以便轻客户端更新其客户端状态(例如使用新的链 ID 或修订号),为升级做准备。 由于客户端状态变更会立即执行,一旦新的客户端状态信息被写入该预定键,客户端将无法继续跟随旧链上的区块,因此必须及时完成升级。状态验证函数
Tendermint 客户端的状态验证函数会针对先前已验证的承诺根检查 Merkle 证明。 这些函数使用客户端初始化时所采用的proofSpecs。
属性与不变量
由 Tendermint 轻客户端算法提供的正确性保证。向后兼容性
不适用。向前兼容性
不适用。对客户端验证算法的修改将需要新的客户端标准。示例实现
- Go 语言中的 ICS 07 实现可见 ibc-go repository。
- Rust 语言中的 ICS 07 实现可见 ibc-rs repository。
历史
2019 年 12 月 10 日 - 初始版本 2019 年 12 月 19 日 - 第一版最终草稿版权
此处所有内容均基于 Apache 2.0 许可。Synopsis
This specification document describes a client (verification algorithm) for a blockchain using Tendermint consensus.Motivation
State machines of various sorts replicated using the Tendermint consensus algorithm might like to interface with other replicated state machines or solo machines over IBC.Definitions
Functions & terms are as defined in ICS 2.currentTimestamp is as defined in ICS 24.
The Tendermint light client uses the generalised Merkle proof format as defined in ICS 23.
hash is a generic collision-resistant hash function, and can easily be configured.
Desired Properties
This specification must satisfy the client interface defined in ICS 2.Note on “would-have-been-fooled logic
The basic idea of “would-have-been-fooled” detection is that it allows us to be a bit more conservative, and freeze our light client when we know that another light client somewhere else on the network with a slightly different update pattern could have been fooled, even though we weren’t. Consider a topology of three chains -A, B, and C, and two clients for chain A, A_1 and A_2, running on chains B and C respectively. The following sequence of events occurs:
- Chain
Aproduces a block at heighth_0(correctly). - Clients
A_1andA_2are updated to the block at heighth_0. - Chain
Aproduces a block at heighth_0 + n(correctly). - Client
A_1is updated to the block at heighth_0 + n(clientA_2is not yet updated). - Chain
Aproduces a second (equivocating) block at heighth_0 + k, wherek <= n.
A_2 (since there are two valid blocks at height h_0 + k which are newer than the latest header A_2 knows), but it will not be possible to freeze A_1, since A_1 has already progressed beyond h_0 + k.
Arguably, this is disadvantageous, since A_1 was just “lucky” in having been updated when A_2 was not, and clearly some Byzantine fault has happened that should probably be dealt with by human or governance system intervention. The idea of “would-have-been-fooled” is to allow this to be detected by having A_1 start from a configurable past header to detect misbehaviour (so in this case, A_1 would be able to start from h_0 and would also be frozen).
There is a free parameter here - namely, how far back is A_1 willing to go (how big can n be where A_1 will still be willing to look up h_0, having been updated to h_0 + n)? There is also a countervailing concern, in and of that double-signing is presumed to be costless after the unbonding period has passed, and we don’t want to open up a denial-of-service vector for IBC clients.
The necessary condition is thus that A_1 should be willing to look up headers as old as it has stored, but should also enforce the “unbonding period” check on the misbehaviour, and avoid freezing the client if the misbehaviour is older than the unbonding period (relative to the client’s local timestamp). If there are concerns about clock skew a slight delta could be added.
Technical Specification
This specification depends on correct instantiation of the Tendermint consensus algorithm and light client algorithm.Client state
The Tendermint client state tracks the current revision, current validator set, trusting period, unbonding period, latest height, latest timestamp (block time), and a possible frozen height.Consensus state
The Tendermint client tracks the timestamp (block time), the hash of the next validator set, and commitment root for all previously verified consensus states (these can be pruned after the unbonding period has passed, but should not be pruned beforehand).Height
The height of a Tendermint client consists of twouint64s: the revision number, and the height in the revision.
0 while the revision number increases by one in order to preserve timeouts through zero-height upgrades.
Headers
The Tendermint headers include the height, the timestamp, the commitment root, the hash of the validator set, the hash of the next validator set, and the signatures by the validators who committed the block. The header submitted to the on-chain client also includes the entire validator set, and a trusted height and validator set to update from. This reduces the amount of state maintained by the on-chain client and prevents race conditions on relayer updates.ClientMessage interface.
Misbehaviour
The Misbehaviour type is used for detecting misbehaviour and freezing the client - to prevent further packet flow - if applicable.
Tendermint client Misbehaviour consists of two headers at the same height both of which the light client would have considered valid.
ClientMessage interface.
Client initialisation
Tendermint client initialisation requires a (subjectively chosen) latest consensus state, including the full validator set.latestClientHeight function returns the latest stored height, which is updated every time a new (more recent) header is validated.
Validity predicate
Tendermint client validity checking uses the bisection algorithm described in the Tendermint spec. If the provided header is valid, the client state is updated & the newly verified commitment written to the store.Misbehaviour predicate
FunctioncheckForMisbehaviour will check if an update contains evidence of Misbehaviour. If the ClientMessage is a header we check for implicit evidence of misbehaviour by checking if there already exists a conflicting consensus state in the store or if the header breaks time monotonicity.
Update state
FunctionupdateState will perform a regular update for the Tendermint client. It will add a consensus state to the client store. If the header is higher than the latest height on the clientState, then the clientState will be updated.
Update state on misbehaviour
FunctionupdateStateOnMisbehaviour will set the frozen height to a non-zero sentinel height to freeze the entire client.
Upgrades
The chain which this light client is tracking can elect to write a special pre-determined key in state to allow the light client to update its client state (e.g. with a new chain ID or revision) in preparation for an upgrade. As the client state change will be performed immediately, once the new client state information is written to the predetermined key, the client will no longer be able to follow blocks on the old chain, so it must upgrade promptly.State verification functions
Tendermint client state verification functions check a Merkle proof against a previously validated commitment root. These functions utilise theproofSpecs with which the client was initialised.
Properties & Invariants
Correctness guarantees as provided by the Tendermint light client algorithm.Backwards Compatibility
Not applicable.Forwards Compatibility
Not applicable. Alterations to the client verification algorithm will require a new client standard.Example Implementations
- Implementation of ICS 07 in Go can be found in ibc-go repository.
- Implementation of ICS 07 in Rust can be found in ibc-rs repository.