tendermint 共识算法伪代码浅析
tendermint 共识算法的论文可以从 https://arxiv.org/pdf/1807.04938.pdf 下载。
如何表示一个节点的共识状态。
image下标p表示节点的identity。
hp
表示该节点的当前块高度,roundp
表示当前共识轮次,stepp
表示共识阶段(proposal
,prevote
,precommit
)
decisionp[]
暂且看作是节点p
的区块列表,按照高度递增。
lockedValuep
,lockedRoundp
表示该节点认可并且对外发起precommit
的块的值,和当时的round
。
validValuep
,validRoundp
表示收到了2/3+的prevote
,但是prevote来的晚了,已经投了nil票。
比较难理解的是validValue
和lockedValue
的区别: validValue
是用来提交proposal
用的,它是节点认为validValue
是其他节点的lockedValue
。而lockedValue
只要用来避免分叉的,只要lockedValue
不为空,这个节点不会接受其他的proposal
。
StartRound
image每次进入新的一轮共识时,会判断自身是否为proposer
,计算谁是proposer
是固定稳定的一个算法,任何节点执行proposer
函数, 对相同的hp
,roundp
,具有相同的输出。
如果经过计算发现自身是proposer
,则 proposal
应为validaValuep
,或者从自己的mempool
中选出入块的交易列表,同时根据上一个块的信息组装出proposal
。
实际算法中,会将proposal
拆分成两个部分,proposal(
只包含blockID
部分)和blockParts
,是由于一个块可能很大,将实际块的拆分成blockparts
去传输,而proposal
只包含block计算出的身份ID--blockId
,这个blockId
中会包含blockParts
的merkel hash root
,用于在blockparts收集完毕后,验证块的正确性和完整性。
同时启动一个超时器,过一段时间后,如果仍然是当前状态,则会投nil的prevote
票,进入prevote
的step。
消息处理
image收到proposal
-
如果该
proposal
声称是第一次提出,则校验proposal
,和v是否和本地的lockedvalue
相同或者lockedround
为-1,是的话就投prevote
票,同时进入prevote
的step。 -
如果该
proposal
声称是之前vr round出现过的proposal
,则检查在该round是否有2/3的的prevote
,如果是并且和本地的lockedValue
相同,则进行投票,同时进入prevote
的step。 -
如果收到
proposal
时,已经有2/3+的该proposal
的prevote
,则validValue
和validRound
设置为该proposal
和当前round
。并且如果目前是prevote
状态,也会设置lockedValue
和lockedRound
则广播precommit
投票。进入precommit
的step。
收到revote
- 一旦收到2/3+ 的
nil prevote
投票,并且自身的状态是prevote
,则启动一个超时器,过一段时间后,如果还是当前状态,则投nil的precommit
票,并且进入precommit
的step。
实际上一进入prevote step
的时候,就会启动一个超时器,过一段时间如果没有集齐足够的prevote
,则投nil precommit
票, 进入precommit
的step。
收到precommit
这里的伪代码有一点出入,实际上进入commit
step的时候,就会启动超时器,如果过一段时间状态没有变化,则会StartRound(hp+1)
进入新的一轮共识。
- 一旦收到2/3+ 的
precommit
投票,则共识成功,进入下一个高度的共识,reset各个状态。