hotstuff 的 proposer 轮换策略好像都是基于 HighQC、view number 这些,因为这些信息是所有 validator 达成共识的。那是否会有 proposer 轮换卡死的可能,举个例子:
比如 LibraBFT 的 hotstuff 方案中,当 validator 触发超时时,新 proposer 收集到 2f+1 的超时消息从而产生一个 Timeout Certificate(TC),正常情况下,此时新 proposer 会广播这个 TC 来对 validators 视图进行切换,所有 validators 的 view_number+1,然后广播一个新的提案,开启对新视图的共识。
但如果 proposer 恶意不广播 TC 也不广播新的提案呢?由于视图切换都是基于 QC 或者 TC 的,因此这种情况下其余 validator 的视图号 view number 并不会增加,HighQC 也不变,HighTC 也不变,那此时要基于什么轮换 Proposer 呢?不就一直卡在作恶的 proposer 上了吗?
Tendermint 就不会有这个问题,即使 proposer 故意不广播新提案,validators 也会对新的 Round 投 nil 票,超过 2f+1 的 nil 票会使得 Round+1,从而换掉之前的 Proposer。
那么 hotstuff 要如何解决这个 proposer 轮换卡死的问题,还是说 hotstuff 的 proposer 轮换有其他的变量在里面?
TC与QC不同,产生TC的超时消息是在全网广播,而不是只发送给下一个proposer。所以TC无需proposer来广播,而是由各个节点在本地根据接收到的超时消息来产生,然后这些节点的view会自动加1。所以文中的“此时新 proposer 会广播这个 TC 来对 validators 视图进行切换,所有 validators 的 view_number+1”是不正确的。
在安全假设下,会有超过2/3的节点能在本地产生TC,然后它们的view+1。如果下一个proposer在这些节点里头,那么就能正常出块。如果下一个proposer不在这些节点里头,这些节点会在view+1基础上再产生一个TC,然后view再加1。至于那些没能产生TC的节点,会在收到下一个view的相关信息时,从其它节点把本地没有的TC/QC同步过来。
你或许会问岂不是每个节点产生的TC的签名集合不一样?是的,确实不完全一样,这与QC不同,同一个view的QC的签名集合是相同的,因为是由proposer产生的。
那最终以哪个TC为准呢?看谁是TC之后第一个出块的,其它节点收到它的块之后,会从它那里同步它保存的TC。这样就全网最终对TC和QC达成一致了。
从Aptos源码看:(1)生成TC的函数可以参考Aptos的实现aptos-core/consensus/consensus-types/src/timeout_2chain.rs的函数aggregate_signatures,可以看到其生成TC时并未判断当前节点是否是proposer。(2)超时信息的处理,每个节点在生成超时事件时用的是广播,而非定向发送到proposer节点。见consensus\src\round_manager.rs的process_local_timeout函数,里面用的广播函数是broadcast_timeout_vote