概述

简介

了解 07-tendermint 轻客户端模块。 Tendermint 客户端是 IBC 中第一个、也是部署最广泛的轻客户端。它实现了 IBC 的轻客户端模块接口,用于跟踪运行 CometBFT 共识的对手链。
Tendermint 是 CometBFT 的旧名称。为了避免高昂的迁移成本,IBC 中仍然保留了这一命名。
Tendermint 客户端由两个重要结构体组成,它们用于跟踪对手链的状态并支持后续更新。ClientState 结构体包含 CometBFT 区块头验证所需的全部参数。另一方面,ConsensusState 是对手链某个特定区块头的压缩视图。与链下轻客户端不同,IBC 不会存储完整区块头,而是只保存证明对手链状态中键值对验证所需的信息(即区块头中的 AppHash),以及将该共识状态作为下一信任根、向客户端添加新共识状态所必需的信息(即区块头中的 NextValidatorsHash 和 Timestamp)。在 UpdateClient 时,中继器会提供完整的已信任区块头,系统会将其与压缩后的信任根共识状态进行校验。如果该已信任区块头与先前某个共识状态匹配,并且该已信任区块头与新区块头通过了 CometBFT 轻客户端更新算法的检查,那么新区块头就会被压缩为一个共识状态并添加到 IBC 客户端中。 每个 Tendermint Client 都由一个以客户端 ID 为键的 ClientState 和多个共识状态组成,这些共识状态同时以 clientID 和区块头高度为键。中继器可以使用这些共识状态,针对对手链的 AppHash 验证数据包承诺、确认和回执的默克尔证明,从而实现可验证的数据包流转。 如果对手链以一种可被链下轻客户端检测到的方式违反了 CometBFT 协议,任何链下参与者也可以将这种恶意行为提交给 IBC 客户端。验证该恶意行为后,Tendermint IBC Client 将被冻结,阻止这条恶意链继续产生任何数据包流转。随后可以通过治理或其他带外协议来回滚已经造成的影响。

初始化

Tendermint 轻客户端初始化时需要提供一个 ClientState,其中包含 CometBFT 区块头验证所需的参数,以及一个最新高度和 ConsensusState。该 ConsensusState 封装了一个已信任区块头的应用状态根,并将作为未来验证来自对手方新区块头的基础。
message ClientState {
  / human readable chain-id that will be included in header
  / and signed over by the validator set
  string   chain_id    = 1;
  / trust level is the fraction of the trusted validator set
  / that must sign over a new untrusted header before it is accepted
  / it can be a minimum of 1/3 and a maximum of 2/3
  / Note these are the bounds of liveness. 1/3 is the minimum
  / honest stake needed to maintain liveness on a chain,
  / requiring more than 2/3 to sign over the new header would
  / break the BFT threshold of allowing 1/3 malicious validators
  Fraction trust_level = 2;
  / duration of the period since the LatestTimestamp during which the
  / submitted headers are valid for update
  google.protobuf.Duration trusting_period = 3;
  / duration of the staking unbonding period
  google.protobuf.Duration unbonding_period = 4;
  / defines how much new (untrusted) header's Time can drift
  / into the future relative to our local clock.
  google.protobuf.Duration max_clock_drift = 5;

  / Block height when the client was frozen due to a misbehaviour
  ibc.core.client.v1.Height frozen_height = 6;
  / Latest height the client was updated to
  ibc.core.client.v1.Height latest_height = 7;

  / Proof specifications used in verifying counterparty state
  repeated cosmos.ics23.v1.ProofSpec proof_specs = 8;

  / Path at which next upgraded client will be committed.
  / Each element corresponds to the key for a single CommitmentProof in the
  / chained proof. NOTE: ClientState must stored under
  / `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored
  / under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using
  / the default upgrade module, upgrade_path should be []string{"upgrade",
  / "upgradedIBCState"}`
  repeated string upgrade_path = 9;
}
message ConsensusState {
  / timestamp that corresponds to the block height in which the ConsensusState
  / was stored.
  google.protobuf.Timestamp timestamp = 1;
  / commitment root (i.e app hash) that will be used
  / to verify proofs of packet flow messages
  ibc.core.commitment.v1.MerkleRoot root = 2;
  / hash of the next validator set that will be used as
  / a new updated source of trust to verify future updates
  bytes next_validators_hash = 3;
}

更新

提交初始客户端状态和共识状态之后,可以通过提交 IBC区块头向客户端添加后续的共识状态。这些区块头包含运行 CometBFT 轻客户端协议所需的全部信息。
message Header {
  / this is the new signed header that we want to add
  / as a new consensus state to the ibc client.
  / the signed header contains the commit signatures of the `validator_set` below
  .tendermint.types.SignedHeader signed_header = 1;

  / the validator set which signed the new header
  .tendermint.types.ValidatorSet validator_set      = 2;
  / the trusted height of the consensus state which we are updating from
  ibc.core.client.v1.Height      trusted_height     = 3;
  / the trusted validator set, the hash of the trusted validators must be equal to
  / `next_validators_hash` of the current consensus state
  .tendermint.types.ValidatorSet trusted_validators = 4;
}
关于 CometBFT 轻客户端协议及其安全属性的详细信息,请参阅原始 Tendermint 白皮书。

证明

随着共识状态被不断添加到客户端中,希望针对对手链某个特定高度上的数据包流转消息进行证明的中继器,就可以使用这些共识状态来完成证明验证。这一过程使用 Tendermint 客户端上的 VerifyMembership 和 VerifyNonMembership 方法。
/ 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. The caller
/ is expected to construct the full CommitmentPath from a
/ CommitmentPrefix and a standardized path (as defined in ICS 24).
VerifyMembership(
    ctx sdk.Context,
    clientID string,
    height Height,
    delayTimePeriod uint64,
    delayBlockPeriod uint64,
    proof []byte,
    path Path,
    value []byte,
)

error

/ VerifyNonMembership is a generic proof verification method
/ which verifies the absence of a given CommitmentPath at a
/ specified height. The caller is expected to construct the
/ full CommitmentPath from a CommitmentPrefix and a standardized
/ path (as defined in ICS 24).
VerifyNonMembership(
    ctx sdk.Context,
    clientID string,
    height Height,
    delayTimePeriod uint64,
    delayBlockPeriod uint64,
    proof []byte,
    path Path,
)

error
Tendermint 客户端初始化时会使用 ICS23 proof spec。这使得 Tendermint 实现能够支持多种不同的默克尔树结构,只要它们可以表示为 ics23.ProofSpec。

恶意行为

Tendermint 轻客户端会直接跟踪 CometBFT 对手链的共识。只要对手方保持拜占庭容错,也就是说,质押验证者中的恶意子集不超过客户端的信任级别,那么该客户端就是安全的。 如果验证者中的恶意子集超过了客户端的信任级别,客户端就可能被诱导接受无效区块,此时连接将不再安全。 Tendermint 客户端提供了一些缓解措施来防止这种情况发生。如果在同一高度上,存在两个都由对手方验证者集合签名的有效区块 [例如,一个由诚实子集签名的有效区块,以及一个由恶意子集签名的无效区块],那么这些冲突的区块头可以作为恶意行为提交给客户端。客户端会验证这些区块头并冻结客户端,从而阻止未来的更新和证明验证成功执行。这实际上会中止与已被攻陷对手方的通信,同时允许通过带外社会共识来回滚已经造成的损害。 类似地,如果区块头的时间戳没有单调递增,这也可以作为恶意行为的证据,并导致客户端被冻结。 因此,凡是轻客户端能够检测到的共识故障,都属于恶意行为协议的一部分,并可用于尽可能减少对手链被攻陷所带来的损害。

安全模型

需要特别指出的是,IBC 并不是一个完全无需信任的协议;它是一个最小化信任的协议。这意味着,两条链之间双向 IBC 通信的安全性,取决于这两条链各自的安全属性。如果其中一条链被完全攻陷,那么与另一条链之间的 IBC 连接就有可能接收到来自恶意链的无效数据包。举例来说,如果某条链上的恶意验证者集合已经控制了超过 2/3 的验证者投票权,那么这组恶意验证者就可以构造一条包含任意承诺根和任意下一验证者集合承诺的单一区块链。这将使其完全控制该链,并阻止诚实子集甚至创建一条竞争性的诚实区块。 在这种情况下,仅跟踪 CometBFT 共识的 IBC Tendermint 客户端无法检测这种恶意行为,也无法冻结客户端。IBC 协议将需要依赖带外机制来检测并修复对手链上这类严重的安全故障。由于 Tendermint 轻客户端只跟踪共识,而不验证状态转换的有效性,因此,来自超出 BFT 故障阈值的验证者集合的恶意行为,是这一轻客户端实现所接受的风险。 IBC 协议通过故障隔离原则(例如,所有代币都带有其所属通道的前缀,因此来自不同链的代币彼此不具备互换性)和故障缓解原则(例如,如果能在完全恶意接管发生前检测到恶意行为,就可以冻结客户端),将这种风险尽可能降到最低。

Overview

Synopsis

Learn about the 07-tendermint light client module. The Tendermint client is the first and most deployed light client in IBC. It implements the IBC light client module interface to track a counterparty running CometBFT consensus.
Tendermint is the old name of CometBFT which has been retained in IBC to avoid expensive migration costs.
The Tendermint client consists of two important structs that keep track of the state of the counterparty chain and allow for future updates. The ClientState struct contains all the parameters necessary for CometBFT header verification. The ConsensusState, on the other hand, is a compressed view of a particular header of the counterparty chain. Unlike off chain light clients, IBC does not store full header. Instead it stores only the information it needs to prove verification of key/value pairs in the counterparty state (i.e. the header AppHash), and the information necessary to use the consensus state as the next root of trust to add a new consensus state to the client (i.e. the header NextValidatorsHash and Timestamp). The relayer provides the full trusted header on UpdateClient, which will get checked against the compressed root-of-trust consensus state. If the trusted header matches a previous consensus state, and the trusted header and new header pass the CometBFT light client update algorithm, then the new header is compressed into a consensus state and added to the IBC client. Each Tendermint Client is composed of a single ClientState keyed on the client ID, and multiple consensus states which are keyed on both the clientID and header height. Relayers can use the consensus states to verify merkle proofs of packet commitments, acknowledgements, and receipts against the AppHash of the counterparty chain in order to enable verified packet flow. If a counterparty chain violates the CometBFT protocol in a way that is detectable to off-chain light clients, this misbehaviour can also be submitted to an IBC client by any off-chain actor. Upon verification of this misbehaviour, the Tendermint IBC Client will freeze, preventing any further packet flow from this malicious chain from occurring. Governance or some other out-of-band protocol may then be used to unwind any damage that has already occurred.

Initialization

The Tendermint light client is initialized with a ClientState that contains parameters necessary for CometBFT header verification along with a latest height and ConsensusState that encapsulates the application state root of a trusted header that will serve to verify future incoming headers from the counterparty.
message ClientState {
  / human readable chain-id that will be included in header
  / and signed over by the validator set
  string   chain_id    = 1;
  / trust level is the fraction of the trusted validator set
  / that must sign over a new untrusted header before it is accepted
  / it can be a minimum of 1/3 and a maximum of 2/3
  / Note these are the bounds of liveness. 1/3 is the minimum
  / honest stake needed to maintain liveness on a chain,
  / requiring more than 2/3 to sign over the new header would
  / break the BFT threshold of allowing 1/3 malicious validators
  Fraction trust_level = 2;
  / duration of the period since the LatestTimestamp during which the
  / submitted headers are valid for update
  google.protobuf.Duration trusting_period = 3;
  / duration of the staking unbonding period
  google.protobuf.Duration unbonding_period = 4;
  / defines how much new (untrusted) header's Time can drift
  / into the future relative to our local clock.
  google.protobuf.Duration max_clock_drift = 5;

  / Block height when the client was frozen due to a misbehaviour
  ibc.core.client.v1.Height frozen_height = 6;
  / Latest height the client was updated to
  ibc.core.client.v1.Height latest_height = 7;

  / Proof specifications used in verifying counterparty state
  repeated cosmos.ics23.v1.ProofSpec proof_specs = 8;

  / Path at which next upgraded client will be committed.
  / Each element corresponds to the key for a single CommitmentProof in the
  / chained proof. NOTE: ClientState must stored under
  / `{upgradePath}/{upgradeHeight}/clientState` ConsensusState must be stored
  / under `{upgradepath}/{upgradeHeight}/consensusState` For SDK chains using
  / the default upgrade module, upgrade_path should be []string{"upgrade",
  / "upgradedIBCState"}`
  repeated string upgrade_path = 9;
}
message ConsensusState {
  / timestamp that corresponds to the block height in which the ConsensusState
  / was stored.
  google.protobuf.Timestamp timestamp = 1;
  / commitment root (i.e app hash) that will be used
  / to verify proofs of packet flow messages
  ibc.core.commitment.v1.MerkleRoot root = 2;
  / hash of the next validator set that will be used as
  / a new updated source of trust to verify future updates
  bytes next_validators_hash = 3;
}

Updates

Once the initial client state and consensus state are submitted, future consensus states can be added to the client by submitting IBC headers. These headers contain all necessary information to run the CometBFT light client protocol.
message Header {
  / this is the new signed header that we want to add
  / as a new consensus state to the ibc client.
  / the signed header contains the commit signatures of the `validator_set` below
  .tendermint.types.SignedHeader signed_header = 1;

  / the validator set which signed the new header
  .tendermint.types.ValidatorSet validator_set      = 2;
  / the trusted height of the consensus state which we are updating from
  ibc.core.client.v1.Height      trusted_height     = 3;
  / the trusted validator set, the hash of the trusted validators must be equal to
  / `next_validators_hash` of the current consensus state
  .tendermint.types.ValidatorSet trusted_validators = 4;
}
For detailed information on the CometBFT light client protocol and its safety properties please refer to the original Tendermint whitepaper.

Proofs

As consensus states are added to the client, they can be used for proof verification by relayers wishing to prove packet flow messages against a particular height on the counterparty. This uses the VerifyMembership and VerifyNonMembership methods on the Tendermint client.
/ 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. The caller
/ is expected to construct the full CommitmentPath from a
/ CommitmentPrefix and a standardized path (as defined in ICS 24).
VerifyMembership(
    ctx sdk.Context,
    clientID string,
    height Height,
    delayTimePeriod uint64,
    delayBlockPeriod uint64,
    proof []byte,
    path Path,
    value []byte,
)

error

/ VerifyNonMembership is a generic proof verification method
/ which verifies the absence of a given CommitmentPath at a
/ specified height. The caller is expected to construct the
/ full CommitmentPath from a CommitmentPrefix and a standardized
/ path (as defined in ICS 24).
VerifyNonMembership(
    ctx sdk.Context,
    clientID string,
    height Height,
    delayTimePeriod uint64,
    delayBlockPeriod uint64,
    proof []byte,
    path Path,
)

error
The Tendermint client is initialized with an ICS23 proof spec. This allows the Tendermint implementation to support many different merkle tree structures so long as they can be represented in an ics23.ProofSpec.

Misbehaviour

The Tendermint light client directly tracks consensus of a CometBFT counterparty chain. So long as the counterparty is Byzantine Fault Tolerant, that is to say, the malicious subset of the bonded validators does not exceed the trust level of the client, then the client is secure. In case the malicious subset of the validators exceeds the trust level of the client, then the client can be deceived into accepting invalid blocks and the connection is no longer secure. The Tendermint client has some mitigations in place to prevent this. If there are two valid blocks signed by the counterparty validator set at the same height [e.g. a valid block signed by an honest subset and an invalid block signed by a malicious one], then these conflicting headers can be submitted to the client as misbehaviour. The client will verify the headers and freeze the client; preventing any future updates and proof verification from succeeding. This effectively halts communication with the compromised counterparty while out-of-band social consensus can unwind any damage done. Similarly, if the timestamps of the headers are not monotonically increasing, this can also be evidence of malicious behaviour and cause the client to freeze. Thus, any consensus faults that are detectable by a light client are part of the misbehaviour protocol and can be used to minimize the damage caused by a compromised counterparty chain.

Security model

It is important to note that IBC is not a completely trustless protocol; it is trust-minimized. This means that the safety property of bilateral IBC communication between two chains is dependent on the safety properties of the two chains in question. If one of the chains is compromised completely, then the IBC connection to the other chain is liable to receive invalid packets from the malicious chain. For example, if a malicious validator set has taken over more than 2/3 of the validator power on a chain; that malicious validator set can create a single chain of blocks with arbitrary commitment roots and arbitrary commitments to the next validator set. This would seize complete control of the chain and prevent the honest subset from even being able to create a competing honest block. In this case, there is no ability for the IBC Tendermint client solely tracking CometBFT consensus to detect the misbehaviour and freeze the client. The IBC protocol would require out-of-band mechanisms to detect and fix such an egregious safety fault on the counterparty chain. Since the Tendermint light client is only tracking consensus and not also verifying the validity of state transitions, malicious behaviour from a validator set that is beyond the BFT fault threshold is an accepted risk of this light client implementation. The IBC protocol has principles of fault isolation (e.g. all tokens are prefixed by their channel, so tokens from different chains are not mutually fungible) and fault mitigation (e.g. ability to freeze the client if misbehaviour can be detected before complete malicious takeover) that make this risk as minimal as possible.