Tendermint 导读|乌托邦周报 #20

如果说 Cosmos SDK 是 Layer 1 的黄埔军校,那么 Tendermint 就是黄埔军校的奠基石。

自 2017 年问世以来,Cosmos SDK 是当今使用最为广泛的 L1 框架。从自家的 Cosmos Hub,到新晋金融协议 Injective,无不是出自 Cosmos SDK 之手。而 Tendermint,作为 Cosmos SDK 背后的核心模块,更是驱动了所有这些 L1 项目。

今天我们就来解构一下 Tendermint 的底层逻辑。

本文要点:

  • 什么是 Tendermint

  • 从头到尾走一遍 Tendermint


什么是 Tendermint

来源:https://v1.cosmos.network/intro
来源:https://v1.cosmos.network/intro

Tendermint 由 3 部分组成:

  1. 网络层:负责节点间的 P2P 通信。

  2. 共识层:拜占庭容错(BFT)的 PoS 共识机制。

  3. 应用层接口:暴露给上层应用的接口,所谓 ABCI(Application BlockChain Interface)。

其中,网络层和共识层整体上又被称为 Tendermint Core。

Cosmos SDK 做的,其实是在 Tendermint 的基础上,进一步为大家提供了应用层的逻辑模板,包括如何治理、如何质押、干坏事怎么 slash,以及如何跨链通信。

来源:https://blog.cosmos.network/tendermint-explained-bringing-bft-based-pos-to-the-public-blockchain-domain-f22e274a0fdb
来源:https://blog.cosmos.network/tendermint-explained-bringing-bft-based-pos-to-the-public-blockchain-domain-f22e274a0fdb

总而言之,Tendermint 把 L1 最共性的部分抽了出来,做成公共组件,让大家专心写应用逻辑就好。这就是 Tendermint 的价值所在。


从头到尾走一遍 Tendermint

理解 Tendermint,目前最好的资料便是官方文档里的这张流程图了。它从用户提交交易开始,到打包入块,到节点间达成共识,到执行交易,改变状态,完整地呈现了 Tendermint 的主线逻辑。

不过除此以外,似乎也没有其他补充性资料展开说明其中的逻辑细节。怎么办?我们不妨从头到尾扒代码看看。

用户提交 Tx

这一步骤对应流程图的左上区域。

Tendermint 在 Mempool Cache 介入,检查 Cache 中的 Tx 是否合法,合法的话才会将其加入 Mempool。

对应的逻辑在 Mempool 的 Reactor 中。Reactor 结构体用于响应 P2P 网络层的事件,最关键的是 Receive(),即收到其他节点发过来的消息后如何处理。

看看 Mempool Reactor 的实现。收到消息后首先反序列化,然后检查每一条 Tx 是否合法。

合法性主要看 Tx 的大小是否在配置的范围内,此处也可以加入自定义的检查逻辑。最终如果 Tx 合法,就加入到 Mempool 中。

Proposer 将 Tx 打包入块

Mempool 中的 Tx 打包由 CreateProposalBlock() 完成,后者会在 Mempool 中按 Tx 优先级从高到低,获取一组 Tx,放入区块中,并填充区块头。

节点间 3 阶段通信,达成共识

在区块链的语境下,共识机制讲的是所有节点按相同的顺序,执行同样的一组 Tx(状态转移),最终达到相同状态(State)的方法。

Tendermint 的共识机制和 PBFT 很相似,都有 3 个阶段,只不过命名不同。

来源:https://pmg.csail.mit.edu/papers/osdi99.pdf
来源:https://pmg.csail.mit.edu/papers/osdi99.pdf

PBFT 的三阶段为 Pre-prepare、Prepare、Commit。

其中,Pre-prepare 和 Prepare 阶段需要至少 2f+1f为拜占庭节点数)的节点投票,才能进入下一阶段,原因是最坏情况下,所有拜占庭节点(f个)同时广播假消息,那么合法节点数至少要超过它们(≥f+1),收到投票的节点才能判断哪边消息是真,哪边是假。拜占庭节点和合法节点数相加便得到2f+1

而在 Commit 后的 Reply 环节,客户端则只需要f+1 个确认来判断是否 OK,因为最坏情况下,所有拜占庭节点(f个)同时作恶,只要有 1 个合法节点与其反馈不一,就可以判断有问题。

最后,考虑所有拜占庭节点(f个)还可以选择不广播、不回应,因此要让这个机制能运行下去,总的节点数至少得是2f+1+f=3f+1

在 Tendermint 里,三阶段对应 Pre-vote、Pre-commit、Commit。

其中,Pre-vote 和 Pre-commit 阶段都需要至少 2f+1的节点投票,Commit 后需要至少f+1 个确认来开启下一轮。由于 Tendermint 基于 PoS,这里的节点看的不再是节点数量,而是节点的投票权(如按照质押额分配)。此外,由于节点多,公网环境复杂,且谁都可以加入,Tendermint 加入了超时机制,每个阶段只要超过 Δ 时间,就会跳过当前阶段。

来源:https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae
来源:https://blog.cosmos.network/consensus-compare-casper-vs-tendermint-6df154ad56ae
来源:https://arxiv.org/pdf/1807.04938.pdf
来源:https://arxiv.org/pdf/1807.04938.pdf

实现层面上,可以分步来看。

首先,Proposer 签名并广播 Proposal。

其他节点在收到 Proposer 广播的区块后,验证区块的合法性,包括区块的高度、时间戳、关联的上一个区块的 ID 等。

若区块没问题,则签名并广播 Pre-vote,其中包含区块的 ID,否则不包含(nil)。

投票结构体的定义也可以简单看一眼。

接下来,等待一段时间,接收其他节点的 Pre-vote,直到收到三分之二节点的 Pre-vote(这种情形又称作 Polka)或超时。

Pre-vote 阶段过后,进入到 Pre-commit 阶段。检查之前有没有收到三分之二的 Pre-vote,有且自己也见着 Pre-vote 对应的区块的话,就签名并广播 Pre-commit,其中包含该区块的 ID,否则不包含(nil)。

接下来,如前一样,进入 Pre-commit 阶段的等待期。这里不再赘述。

最后,终于走到 Commit 阶段,看大家有没有达成一致(Pre-commit 阶段达成 Polka),有的话就 Commit,没有的话从头再来。

执行 Tx

执行 Commit 的区块,包括其中的所有 Tx,并更新状态。

选举下一区块的新 Proposer

Tendermint 的 Leader Election 是基于 Weighted Round-Robin 算法进行的。

来源:https://medium.com/@ppio/tendermint-introduction-and-analysis-95bc15749921
来源:https://medium.com/@ppio/tendermint-introduction-and-analysis-95bc15749921

一轮走完后,先给所有节点增加优先级。然后,选择优先级最高的作为 Proposer,循环往复。

注意,被选为 Proposer 的节点在下一轮的 Priority 会排到队尾(被减去 VotingPower 之和)。

值得一提的是,上一轮的 Proposer 节点在下一轮的优先级会排到末尾。狡猾一点的话,他可以选择退出,再重新加入,这样又会按照他的质押额来赋予投票权,从而插到队伍中间。为了尽可能避免这种情况,新加入的节点的优先级会有一个惩罚系数。


以上便是 Tendermint 的全流程玩法了。后面我们将看看,构建在 Tendermint 之上的 Cosmos SDK 是如何对其进行扩展的。

Subscribe to 无环图
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.