美文网首页
tendermint共识解析

tendermint共识解析

作者: 牧码人爱跑马 | 来源:发表于2020-01-09 08:20 被阅读0次

本文关于tendermint的共识部分,主要参考如下:
1. Ethan Buchman:Tendermint: Byzantine Fault Tolerance in the Age of
Blockchains

2. tendermint中文文档
3.tendermint的一个论文
4. 万向区块链对参考0中论文的翻译
5. tendermint官方doc

本文着重介绍tendermint的共识方面的细节。


image.png

tendermint共识状态机解析

上图中每个step,其实都是共识状态机中的一个状态,状态转换周期如下:

NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->...

gossip 都广播哪些内容?

我们知道在驱动状态机最重要两个因素,一个是通信(tendermint中的P2P是以gossip协议和reactor模式为基础);另一个就是event(设计上任何异步通讯的过程都离不开event),这两部分的内容后面有机会的话会单独写文章解析,现在我们先来看看gossip都广播哪些内容,以使节点都能跟进当前的共识状态。
以下引用自官方文档,逐条说明本人结合源码对其的解析:

  • Nodes gossip PartSet parts of the current round's proposer's proposed block. A LibSwift inspired algorithm is used to quickly broadcast blocks across the gossip network.

说明:partset代表block的一些简要信息,其结构如下:

type PartSet struct {
  total int
  hash  []byte

  mtx           sync.Mutex
  parts         []*Part
  partsBitArray *bits.BitArray
  count         int
}
  • Nodes gossip prevote/precommit votes. A node NODE_A that is ahead of NODE_B can send NODE_Bprevotes or precommits for NODE_B's current (or future) round to enable it to progress forward.

说明:这条要结合下面状态机的prevote、precommit状态解析中的step ends一起理解。因为prevote,precommit都需要2/3+的票数通过,才能进行状态转换。

  • Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change) round if one is proposed.

说明:gossip prevotes的另一个用处是polka unlock.

  • Nodes gossip to nodes lagging in blockchain height with block commits for older blocks.

说明:gossip block,落后的节点同步新block。

  • Nodes opportunistically gossip HasVote messages to hint peers what votes it already has.

说明:节点偶尔gossip HasVote消息

  • Nodes broadcast their current state to all neighboring peers. (but is not gossiped further)

说明:节点gossip 当前所在状态(未来版本不再gossip此部分)

共识状态解析

接下来让我们逐条分析各个共识状态。

1. propose step(height:H,round:R)

客户端发起一笔交易gentx,通过rpc发到tendermint节点后进入mempool cache,tm调用checktx的abci接口验证交易,如果验证成功就进入mempool,proposer节点选择打包交易到区块中,并将区块附到proposalMsg中,对此msg进行签名,然后通过gossip把此msg广播到区块链网络中。

一个proposal包含一个(H,R)的block,和一个可选的最近的PoLC-Round < R,当然前提是proposer知道此可选参数(PoLC:proof of lock change,指的2/3+ prevote某个block),这样适当保住了共识网络的liveness。

Upon entering Propose:

指定的proposer 提议了一个block(H,R).

The Propose step ends:

  1. timeoutProposeR 超时,则进入-->Prevote(H,R)
  2. 当收到了proposal block,并且改validator的所有prevote step 都在PoLC-Round,则进入-->Prevote(H,R)。(要理解这点,重点要理解某个validator有可能involve在若干个round,相当于说你其他轮中都过了prevote的step,才能进入新一轮的prevote step?但如果有锁的情况下即使进入了prevote轮,你也不能prevote了)
  3. After common exit conditions

Note:
注意每轮共识开始时,validator节点在本地会有一个本地同步时钟,用来记时ProPose timeout, 如果在timeout时间周期内没有收到proposer节点的提议,对该提议的预投票为空(nil),validator节点会投票决定是否进入下一轮共识,validator也可以通过治理模块投票移出或者替换拜占庭验证者。而propose timeout也会随着每轮共识而增加。所以可以理解为此过程是同步过程。
但是在其后的prevote和precommit的两阶段共识是完全异步的,从而减少对时钟同步的依赖,或者网络延迟。但这也同样存在一个问题,如果这两阶段共识中就是得不到1/3以上validator的投票,整个区块链网络将瘫痪(此处其实有待看源码仔细研究,因为这两个阶段也是有timeout定时器的,重点关注step end的条件,以此来分析可能引起网络hang住的场景)。这个问题的本质其实就是FLP不可能定理。FLP 原理实际上说明对于允许节点失效情况下,纯粹异步系统无法确保一致性在有限时间内完成。
所以tendermint其实在一定程度是假定节点中大部分都是正常节点。

2. prevote step (height:H,round:R)

每个节点收到propose消息后,会先对区块进行验证,如果验证通过才开始prevote签名,然后广播到网络中。

Upon entering Prevote, each validator broadcasts its prevote vote.

  1. First, if the validator is locked on a block since LastLockRound but now has a PoLC for something else at round PoLC-Round where LastLockRound < PoLC-Round < R, then it unlocks.(这条好理解,就是解锁条件,重点还是要理解异步)。
  2. If the validator is still locked on a block, it prevotes that. (这条是因为锁的规则,他只能prevote自己lock住的block,但是会引起重复prevote?这个还得结合源码研究)。
  3. Else, if the proposed block from Propose(H,R) is good, it prevotes that.(没啥好解释的,就是验证了proposal后正常prevote)
  4. Else, if the proposal is invalid or wasn't received on time, it prevotes <nil>.(proposal无效,或者定时器超时,就prevote<nil>)

The Prevote step ends:

  • After +2/3 prevotes for a particular block or <nil>. -->; goto Precommit(H,R)
  • After timeoutPrevote after receiving any +2/3 prevotes. --> goto Precommit(H,R)
  • After common exit conditions

3. Precommit Step (height:H,round:R)

Upon entering Precommit, each validator broadcasts its precommit vote.

  1. If the validator has a PoLC at (H,R) for a particular block B, it (re)locks (or changes lock to) and precommits B and sets LastLockRound = R.(说明了什么时候上锁)
  2. Else, if the validator has a PoLC at (H,R) for <nil>, it unlocks and precommits <nil>.(说明了什么时候precommit nil,简言之,就是获得了POLC的<nil>)
    3.Else, it keeps the lock unchanged and precommits <nil>.(最后剩下的情况也就是超时了吧?)

A precommit for <nil> means "I didn’t see a PoLC for this round, but I did get +2/3 prevotes and waited a bit".(这句可以理解为没有获得非nil的POLC,而且等待了timeout时间段)

The Precommit step ends:

  1. After +2/3 precommits for <nil>. --> goto Propose(H,R+1)(注意,并没有commit nil。prevote nil、precommit nil进入下一轮,因此tendermint中没有类似PBFT中的viewchange )
  2. After timeoutPrecommit after receiving any +2/3 precommits. --> goto Propose(H,R+1)(我理解此处+2/3 precommits是笔误?应该是+2/3 prevotes?)
  3. After common exit conditions

Common exit conditions

  1. After +2/3 precommits for a particular block. --> goto Commit(H)
  2. After any +2/3 prevotes received at (H,R+x). --> goto Prevote(H,R+x)
  3. After any +2/3 precommits received at (H,R+x). --> goto Precommit(H,R+x)
    第一点,可理解为在R内要追赶最新状态,尽快commit。
    后两点可理解为要追赶更新的R。

4. Commit Step (height:H)

  1. Set CommitTime = now()
  2. Wait until block is received. --> goto NewHeight(H+1) //注意此处要等到本轮的块到了才会进入NewHeight step.

5. NewHeight Step (height:H)

Move Precommits to LastCommit and increment height.
Set StartTime = CommitTime+timeoutCommit
Wait until StartTime to receive straggler commits. --> goto Propose(H,0)

小结

在异步网络中,一个最大的问题就是同高度上,在不同的round提交了不同的block。tendermint引入了锁的机制。

什么时候锁?

每个validator锁定在他获得PoLC的block中,然后只能对该block precommit,在后面的R中,如果没有unlock也只能prevote此block。

什么时候解锁?

提交了一个当前锁定的块后
在下一轮(准确说应该是PoLC round)看到PoLC(proof of lock change,在新的一轮收到大于2/3 prevote的block)时获得解锁, 解锁后不会在PoLC round 投票了,因为在该R中已经获得了polka,也就是说已经结束了prevote state。

锁定规则

验证者只能预投票(pre-vote)他们被锁定的区块(因为他在某Round中prevote的block未必和precommit的block是相同的)。这样就阻止验证者在同一个高度,上一轮中预提交(pre-commit)一个区块,之后又预投票了下一轮的另一个区块。

Proof of Safety

假设有-1/3的Byzantine voting power(-1/3指不到1/3), 在R轮时,validator commit一个block B。这就意味着有1/3+的诚实节点lock在了R' > R, 他们直到获得在R`>R 的POLC才能unlock。但是这种unlock的情况不会发生,因为剩余的-2/3个节点能投票给B以外的block,无法形成POLC。(这不是会导致诚实节点一直锁吗?从而网络hang住?)

Proof of Liveness

相关文章

网友评论

      本文标题:tendermint共识解析

      本文链接:https://www.haomeiwen.com/subject/bnujgctx.html