1.前言
在前几章笔者深入hotstuff源码进行分析,对底层逻辑、共识运作机制、签名算法等进行了详细的分析。在“前言”章节时笔者对hotsutff发出了一些疑问,因此在这一章,笔者将根据几天来对代码的分析对hotstuff算法进一步总结(不止包括算法逻辑,也涵盖代码实现逻辑)。
2.总结
- 三段式共识
在流水线模式中,三段式共识是依靠区块状态实现的,一个区块会经历【抓取、交付】(这里可以理解为获取区块信息,一般提议者先获取了再提议,追随者是先接受提议再获取)、提议、投票以及决策。而“投票”动作后协议会获取下一个提议区块开始新的流程。是否决策要看后面的区块是否有正确引用待决策的区块,只要待决策的区块在锁定区块的分支上(自己或引用的区块有引用锁定区块),并且其后面分支上有3代新的区块,则认为该区块可以决策(笔者之前一直以为要重复发送同一个区块的不同类型比如propose、precommit,commit信息,太笨了)。
/* three-step HotStuff */
const block_t &blk2 = nblk->qc_ref;
if (blk2 == nullptr) return; // 如果 blk2 为 null,返回
/* decided blk could possibly be incomplete due to pruning */
if (blk2->decision) return; // 如果 blk2 已经决策,返回
update_hqc(blk2, nblk->qc); // 更新 HQC
const block_t &blk1 = blk2->qc_ref;
if (blk1 == nullptr) return; // 如果 blk1 为 null,返回
if (blk1->decision) return; // 如果 blk1 已经决策,返回
if (blk1->height > b_lock->height) b_lock = blk1; // 更新 b_lock
const block_t &blk = blk1->qc_ref;
if (blk == nullptr) return; // 如果 blk 为 null,返回
if (blk->decision) return; // 如果 blk 已经决策,返回
/* commit requires direct parent */
if (blk2->parents[0] != blk1 || blk1->parents[0] != blk) return; // 确保父区块关系正确
/* otherwise commit */
std::vector<block_t> commit_queue; // 用于存储需要提交的区块
block_t b;
for (b = blk; b->height > b_exec->height; b = b->parents[0])
{ /* TODO: also commit the uncles/aunts */
commit_queue.push_back(b); // 将需要提交的区块添加到队列中
}
if (b != b_exec)
throw std::runtime_error("safety breached :( " +
std::string(*blk) + " " +
std::string(*b_exec)); // 确保安全性,如果不符合预期则抛出异常
for (auto it = commit_queue.rbegin(); it != commit_queue.rend(); it++)
{
const block_t &blk = *it;
blk->decision = 1; // 标记区块为已决策
do_consensus(blk); // 执行共识过程
LOG_PROTO("commit %s", std::string(*blk).c_str()); // 记录提交日志
for (size_t i = 0; i < blk->cmds.size(); i++)
do_decide(Finality(id, 1, i, blk->height,
blk->cmds[i], blk->get_hash())); // 执行决策操作
}
b_exec = blk; // 更新 b_exec
- 提议者轮换机制
在代码中,暂时没有实现在新一轮共识发起时对提议者进行轮换,只有在超时时才会对提议者进行轮换。
- 验证池机制
在代码实现中,代码在证书验证(QC证书的验证)、投票验证(part证书验证)时使用了验证池多线程机制,加快了验证速度。
- 两重网络
在代码中有客户端网络以及副本网络,客户端网络用于客户端与副本之间的通信,副本网络用于副本之间的通信。其中pacemaker是这两重网络与协议之间的桥梁。外部网络通过pacemaker将命令提交给内部网络,pacemaker辅助协议以及内部网络完成什么时候提议、什么时候轮换、怎么轮换等。
- 事件驱动机制
除了pacemaker,代码中会使用event库注册很多类型的事件循环驱动,比如副本网络会注册信息接受事件驱动程序、验证池会注册验证队列驱动机制(有新的验证信息进入队列会自动执行验证)、协议会注册超时机制等等。
- 异步交互机制
为了避免因异步多程序工作(接受新的提议、发起新的提议、投票、接受投票等等一起工作)引起的逻辑混乱。代码使用promiss库,会对某些任务赋予promiss,当任务完成时会解决promiss,然后自动调用对应于该promiss的回调函数,以实现逻辑连贯。