1.前言
上一章我们分析了hotstuff的网络实现和加密实现,这一章笔者将分析hotstuff中的事件驱动,包括event(salticidae库)和pacemaker(liveness.h文件)。
2.源码分析
Event
因为源代码较多,笔者将从如何使用来分析这个库的逻辑
(1) EventContext 事件上下文
EventContext
是事件循环的核心部分。它负责管理和调度各种事件,包括定时器事件、文件描述符事件和信号事件。
-
基本概念:
- 事件循环:程序会不断检查是否有事件需要处理,如网络连接的读写、定时器超时等。
EventContext
是事件循环的封装,用于管理和调度这些事件。
-
使用:
- 你首先需要创建一个
EventContext
对象,表示你的事件循环环境。 - 然后通过调用
dispatch()
方法来启动事件循环
- 你首先需要创建一个
#include "salticidae/event.h"
int main() {
salticidae::EventContext ec; // 创建事件上下文
// 事件循环启动,处理所有事件
ec.dispatch();
return 0;
}
(2) FdEvent 文件描述符事件
FdEvent
用于监视文件描述符的事件,比如是否有数据可读,或者是否可以写入数据。
-
基本概念:
- 文件描述符:在 UNIX 系统中,所有东西都被当作文件,文件描述符是访问这些文件(如标准输入、输出、网络连接)的句柄。
FdEvent
允许你监视这些文件描述符的事件(可读、可写)。
-
使用:
- 创建
FdEvent
对象时,传入文件描述符和回调函数。 - 当事件发生时,回调函数会被调用,处理相应的事件。
- 创建
#include "salticidae/event.h"
#include <unistd.h>
#include <fcntl.h>
int main() {
salticidae::EventContext ec;
// 打开一个文件描述符(例如从标准输入)
int fd = fileno(stdin);
// 创建 FdEvent 对象,监听可读事件
salticidae::FdEvent ev(ec, fd, [](int fd, int events) {
if (events & salticidae::FdEvent::READ) {
char buf[1024];
ssize_t n = read(fd, buf, sizeof(buf));
if (n > 0) {
buf[n] = '\0';
printf("Read: %s\n", buf);
}
}
});
ev.add(salticidae::FdEvent::READ); // 添加可读事件监听
ec.dispatch(); // 启动事件循环
return 0;
}
(3) TimerEvent 定时器事件
TimerEvent
用于在指定时间后执行某些操作。
-
基本概念:
- 定时器:设置一个时间,当时间到达时触发某个操作。
TimerEvent
允许你设置这样的定时器,并指定时间到时要执行的操作。
-
使用:
- 创建
TimerEvent
对象时,传入事件上下文和回调函数。 - 使用
add()
方法启动定时器,当定时器到达时,回调函数会被调用。
- 创建
#include "salticidae/event.h"
int main() {
salticidae::EventContext ec;
// 创建一个定时器事件,每隔2秒触发一次
salticidae::TimerEvent timer(ev, [&](salticidae::TimerEvent &ev) {
printf("Timer fired!\n");
ev.add(2.0); // 重新设置定时器,2秒后再次触发
});
timer.add(2.0); // 2秒后触发定时器
ec.dispatch(); // 启动事件循环
return 0;
}
(4)信号处理事件 SigEvent
SigEvent
用于处理系统信号,比如 SIGINT
(通常表示用户按下 Ctrl+C
)。
-
基本概念:
- 信号:操作系统向进程发送的一种通知,表示发生了某种事件(如用户中断、非法操作等)。
SigEvent
允许你捕获这些信号,并在信号发生时执行某些操作。
-
使用:
- 创建
SigEvent
对象时,传入事件上下文和回调函数。 - 使用
add()
方法添加要监听的信号,当信号发生时,回调函数会被调用。
- 创建
#include "salticidae/event.h"
int main() {
salticidae::EventContext ec;
// 创建一个信号事件,处理 SIGINT 信号
salticidae::SigEvent sig(ev, [](int signum) {
printf("Received signal: %d\n", signum);
exit(0); // 退出程序
});
sig.add(SIGINT); // 监听 SIGINT 信号
ec.dispatch(); // 启动事件循环
return 0;
}
(5) MPSCQueueEventDriven 队列事件
MPSCQueueEventDriven
实现了一个多生产者-单消费者的事件驱动队列。
-
基本概念:
- 多生产者-单消费者:多个线程可以向队列中添加任务,但只有一个线程可以从队列中取出任务并处理。
- 事件驱动:队列中有数据时触发处理操作。
-
使用:
- 创建队列对象并注册处理程序。
- 当队列中有数据时,处理程序会被触发,处理相应的数据。
#include "salticidae/event.h"
int main() {
salticidae::EventContext ec;
// 创建队列
salticidae::MPSCQueueEventDriven<int> queue;
// 注册队列处理程序
queue.reg_handler(ec, [](auto &q) {
int value;
while (q.try_dequeue(value)) {
printf("Dequeued: %d\n", value);
}
return false; // 队列空时返回 false
});
// 向队列中添加元素
queue.enqueue(42);
ec.dispatch(); // 启动事件循环
return 0;
}
Pacemaker
PaceMaker
类:
- 这是一个抽象基类,定义了 HotStuff 协议中控制时间节奏的基本接口。
- 它提供了多个纯虚函数供子类实现,比如:
init()
:初始化 PaceMaker,通常与 HotStuff 核心进行绑定。beat()
:返回一个promise_t
,当系统认为是时候发出新命令时,该 promise 被解决。get_proposer()
:返回当前提议者的 ID。get_parents()
:返回当前区块的父块列表。beat_resp()
:用于在某个区块被投票后响应(笔者理解就是响应提议者的命令)。impeach()
:用于罢免当前提议者。on_consensus()
:当区块达成共识时被调用。get_pending_size()
:返回当前待处理的节奏请求的数量。
/*PaceMaker是一个抽象基类,定义了Liveness机制的基本接口。
提供初始化方法init,心跳方法beat,获取提议者方法get_proposer,获取父块方法get_parents,响应心跳方法beat_resp,
弹劾提议者方法impeach,共识达成后的处理方法on_consensus,获取挂起的心跳请求数量方法get_pending_size。
pacemaker_bt是PaceMaker的智能指针类型。*/
// PaceMaker 类定义了一个协议中的时间控制逻辑接口,具体细节由继承类实现。
class PaceMaker {
protected:
HotStuffCore *hsc; // 指向 HotStuff 核心的指针,用于与共识核心交互
public:
virtual ~PaceMaker() = default; // 虚拟析构函数,允许派生类正确清理
/** 初始化 PaceMaker。派生类应调用默认实现来设置 `hsc`。 */
virtual void init(HotStuffCore *_hsc) { hsc = _hsc; }
/** 获取一个 promise,当 PaceMaker 认为是 *合适* 的时间去发出新命令时解决该 promise。
* 当 promise 被解决时,副本应提出该命令。 */
virtual promise_t beat() = 0;
/** 获取当前的提议者。 */
virtual ReplicaID get_proposer() = 0;
/** 选择新块的父块。
* @return 父块。索引为 0 的块是直接父块,其它的是叔块/姑块。返回的向量应为非空。 */
virtual std::vector<block_t> get_parents() = 0;
/** 获取一个 promise,当 PaceMaker 认为是 *合适* 的时间去为块投票时解决该 promise。
* promise 解决时的值是上一个提议者的 ID。 */
virtual promise_t beat_resp(ReplicaID last_proposer) = 0;
/** 罢免当前提议者。 */
virtual void impeach() {}
/** 当达成共识时调用此函数。 */
virtual void on_consensus(const block_t &) {}
/** 获取待处理的 beat 请求的数量。 */
virtual size_t get_pending_size() = 0;
};
using pacemaker_bt = BoxObj<PaceMaker>;
PMHighTail
类:
PMHighTail
继承自PaceMaker
,并实现了父块的选择逻辑。- 它通过监控最高质量共识(HQC)块来确定新区块的父块。
- 它注册了多个回调函数来更新 HQC 块,并选择新的父块。
using pacemaker_bt = BoxObj<PaceMaker>;
/** Parent selection implementation for PaceMaker: select the highest tail that
* follows the current hqc block. */
/*PMHighTail继承自PaceMaker,用于选择当前最高的hqc区块作为父区块。
提供了init初始化方法,注册hqc更新、提议和接收提议的事件。
提供了get_parents方法,返回当前hqc区块作为父区块*/
// PMHighTail 是 PaceMaker 的一个实现,它根据共识协议中的最新块来选择新块的父块。
class PMHighTail : public virtual PaceMaker {
block_t hqc_tail; // 高质量共识(HQ)尾块
const int32_t parent_limit; // 最大父块数量
/** 检查块 `_b` 是否是块 `_a` 的祖先。 */
bool check_ancestry(const block_t &_a, const block_t &_b) {
block_t b;
for (b = _b;
b->get_height() > _a->get_height();
b = b->get_parents()[0]);
return b == _a;
}
/** 注册 HQ 块更新的回调函数。 */
void reg_hqc_update() {
hsc->async_hqc_update().then([this](const block_t &hqc) {
hqc_tail = hqc;
// 更新 hqc_tail 为最高的尾块
for (const auto &tail: hsc->get_tails())
if (check_ancestry(hqc, tail) && tail->get_height() > hqc_tail->get_height())
hqc_tail = tail;
reg_hqc_update(); // 重新注册回调
});
}
/** 注册提议回调函数。 */
void reg_proposal() {
hsc->async_wait_proposal().then([this](const Proposal &prop) {
hqc_tail = prop.blk; // 更新 hqc_tail 为新提议的块
reg_proposal(); // 重新注册回调
});
}
/** 注册接收提议的回调函数。 */
void reg_receive_proposal() {
hsc->async_wait_receive_proposal().then([this](const Proposal &prop) {
const auto &hqc = hsc->get_hqc();
const auto &blk = prop.blk;
if (check_ancestry(hqc, blk) && blk->get_height() > hqc_tail->get_height())
hqc_tail = blk; // 更新 hqc_tail 为接收到的块
reg_receive_proposal(); // 重新注册回调
});
}
public:
PMHighTail(int32_t parent_limit) : parent_limit(parent_limit) {}
void init() override {
hqc_tail = hsc->get_genesis(); // 初始化 hqc_tail 为创世块
reg_hqc_update(); // 注册 HQ 块更新的回调
reg_proposal(); // 注册提议的回调
reg_receive_proposal(); // 注册接收提议的回调
}
/** 获取新块的父块。返回 hqc_tail 以及其它尾块(如果有的话)。 */
std::vector<block_t> get_parents() override {
const auto &tails = hsc->get_tails(); // 获取所有尾块
std::vector<block_t> parents{hqc_tail}; // 初始化父块列表
// TODO: 包含区块链逻辑
// 计算需要的父块数量,限制在 parent_limit 内
// auto nparents = tails.size();
// if (parent_limit > 0)
// nparents = std::min(nparents, (size_t)parent_limit);
// nparents--;
// /* 将其余尾块作为“叔块/姑块”添加 */
// for (const auto &blk: tails) {
// if (blk != hqc_tail) {
// parents.push_back(blk);
// if (!--nparents) break;
// }
// }
return parents; // 返回父块列表
}
};
PMWaitQC
类:
PMWaitQC
继承自PaceMaker
,实现了在上一个提议的区块获得 QC(Quorum Certificate)时触发节奏的逻辑。- 它通过维护一个等待队列来管理节奏请求,并在条件满足时解决这些请求。
/** Beat implementation for PaceMaker: simply wait for the QC of last proposed
* block. PaceMakers derived from this class will beat only when the last
* block proposed by itself gets its QC. */
/*PMWaitQC继承自PaceMaker,用于在上一个提议的区块获得QC时触发心跳。
提供了init初始化方法,更新最后提议的区块,调度下一个心跳。
提供了get_proposer方法,返回当前提议者的ID。
提供了beat方法,添加心跳请求到队列,并调度下一个心跳。
提供了beat_resp方法,返回上一个提议者的ID。*/
// PMWaitQC 是 PaceMaker 的另一个实现,它根据提议的块状态来决定何时发出命令。
class PMWaitQC : public virtual PaceMaker {
std::queue<promise_t> pending_beats; // 等待处理的 beat 请求队列
block_t last_proposed; // 最近提议的块
bool locked; // 标记是否处于锁定状态
promise_t pm_qc_finish; // 用于 QC 完成的 promise
promise_t pm_wait_propose; // 用于等待提议的 promise
protected:
/** 调度下一个 beat 请求,如果队列非空且当前未被锁定。 */
void schedule_next() {
// 检查是否有待处理的 beats 以及当前是否未锁定
if (!pending_beats.empty() && !locked) {
// 从待处理的 beats 队列中取出第一个 promise 对象
auto pm = pending_beats.front();
pending_beats.pop(); // 从队列中移除该 promise 对象
// 拒绝当前的 pm_qc_finish promise,表示目前块的 QC 完成的状态尚不可用
pm_qc_finish.reject();
// 使用 HotStuffCore 类中的 async_qc_finish 方法获取块 QC 完成的 promise 对象
// 传入的参数是当前最后提议的块(last_proposed)
// async_qc_finish 方法返回的 promise 对象将会在块的 QC 完成时解决
(pm_qc_finish = hsc->async_qc_finish(last_proposed))
.then([this, pm]() {
// 当 pm_qc_finish promise 对象解决时,执行回调函数
// 解决待处理的 promise 对象 pm,并返回当前提议者的 ID
pm.resolve(get_proposer());
});
// 设置锁定状态,防止在当前 QC 完成前处理其他 beats
locked = true;
}
}
/** 更新最近提议的块。 */
void update_last_proposed() {
pm_wait_propose.reject();
(pm_wait_propose = hsc->async_wait_proposal()).then(
[this](const Proposal &prop) {
last_proposed = prop.blk; // 更新最近提议的块
locked = false; // 解除锁定状态
schedule_next(); // 调度下一个 beat 请求
update_last_proposed(); // 继续更新提议
});
}
public:
/** 获取待处理的 beat 请求的数量。 */
size_t get_pending_size() override { return pending_beats.size(); }
void init() override {
last_proposed = hsc->get_genesis(); // 初始化最近提议的块为创世块
locked = false; // 设置锁定状态
update_last_proposed(); // 更新最近提议的块
}
/** 获取当前提议者的 ID。 */
ReplicaID get_proposer() override {
return hsc->get_id(); // 返回当前提议者 ID
}
/** 获取一个 promise,当 PaceMaker 认为是 *合适* 的时间去发出新命令时解决该 promise。 */
promise_t beat() override {
promise_t pm;
pending_beats.push(pm); // 将新的 beat 请求加入队列
schedule_next(); // 调度下一个 beat 请求
return pm;
}
/** 获取一个 promise,当 PaceMaker 认为是 *合适* 的时间去为块投票时解决该 promise。
* promise 解决时的值是下一个提议者的 ID。 */
promise_t beat_resp(ReplicaID last_proposer) override {
return promise_t([last_proposer](promise_t &pm) {
pm.resolve(last_proposer); // 解决 promise,返回下一个提议者的 ID
});
}
};
PaceMakerDummy
和 PaceMakerDummyFixed
类:
- 这两个类是
PaceMaker
的简单实现。 PaceMakerDummy
是前两个类的集合类。PaceMakerDummyFixed
在PaceMakerDummy基础上
固定了一个提议者 ID,该提议者始终负责发起提议
/** Naive PaceMaker where everyone can be a proposer at any moment. */
/*PaceMakerDummy继承自PMHighTail和PMWaitQC,结合了两者的功能。
提供了init初始化方法,初始化PMHighTail和PMWaitQC*/
struct PaceMakerDummy: public PMHighTail, public PMWaitQC {
PaceMakerDummy(int32_t parent_limit):
PMHighTail(parent_limit), PMWaitQC() {}
void init(HotStuffCore *hsc) override {
PaceMaker::init(hsc);
PMHighTail::init();
PMWaitQC::init();
}
};
/** PaceMakerDummy with a fixed proposer. */
/*PaceMakerDummyFixed继承自PaceMakerDummy,具有固定的提议者。
提供了get_proposer方法,返回固定的提议者ID。
提供了beat_resp方法,返回固定的提议者ID。*/
class PaceMakerDummyFixed: public PaceMakerDummy {
ReplicaID proposer;
public:
PaceMakerDummyFixed(ReplicaID proposer,
int32_t parent_limit):
PaceMakerDummy(parent_limit),
proposer(proposer) {}
ReplicaID get_proposer() override {
return proposer;
}
promise_t beat_resp(ReplicaID) override {
return promise_t([this](promise_t &pm) {
pm.resolve(proposer);
});
}
};
PMRoundRobinProposer
类:
- 这是一个实现了轮流提议机制的
PaceMaker
。 - 提议者按照轮流的方式依次进行轮换。
- 该类实现了复杂的定时器逻辑,用于在提议者没有及时发出提议时进行轮换
/**
* Simple long-standing round-robin style proposer liveness gadget.
*/
/*PMRoundRobinProposer继承自PaceMaker,实现了轮流提议的机制。
提供了注册提议和接收提议的方法,调度下一个提议,更新最后提议的区块,处理新共识的方法和轮转方法。
提供了初始化方法init,获取提议者方法get_proposer,心跳方法beat,响应心跳方法beat_resp,共识达成后的处理方法on_consensus和弹劾提议者方法impeach。*/
class PMRoundRobinProposer: virtual public PaceMaker {
double base_timeout; // 基础超时值,初始值
double exp_timeout; // 指数增长的超时值,用于定时器
double prop_delay; // 提议延迟
EventContext ec; // 事件上下文,用于定时器和事件处理
// 定时器事件,用于超时处理
TimerEvent timer;
// 当前认为的提议者 ID
ReplicaID proposer;
// 存储各个提议者的提议区块
std::unordered_map<ReplicaID, block_t> prop_blk;
// 轮换状态标志
bool rotating;
// 提议相关的状态变量和 promise
std::queue<promise_t> pending_beats; // 待处理的节奏 promise
block_t last_proposed; // 上一个提议的区块
bool locked; // 锁定状态标志
promise_t pm_qc_finish; // QC 完成的 promise
promise_t pm_wait_propose; // 等待提议的 promise
promise_t pm_qc_manual; // 手动 QC 的 promise
// 注册区块提议的回调函数
void reg_proposal() {
// 异步等待提议,并在提议到达时调用回调函数
hsc->async_wait_proposal().then([this](const Proposal &prop) {
// 获取当前提议者的提议区块(如果还没有存储)
auto &pblk = prop_blk[hsc->get_id()];
if (!pblk) pblk = prop.blk;
// 如果当前处于轮换状态,重新注册提议回调
if (rotating) reg_proposal();
});
}
// 注册接收到提议的回调函数
void reg_receive_proposal() {
// 异步等待接收到提议,并在接收到提议时调用回调函数
hsc->async_wait_receive_proposal().then([this](const Proposal &prop) {
// 获取提议者的提议区块(如果还没有存储)
auto &pblk = prop_blk[prop.proposer];
if (!pblk) pblk = prop.blk;
// 如果当前处于轮换状态,重新注册接收到提议的回调
if (rotating) reg_receive_proposal();
});
}
// 处理提议者的调度
void proposer_schedule_next() {
// 检查是否有待处理的节奏,并且当前没有锁定
if (!pending_beats.empty() && !locked)
{
// 获取并移除待处理的节奏 promise
auto pm = pending_beats.front();
pending_beats.pop();
// 拒绝之前的 QC 完成 promise
pm_qc_finish.reject();
// 异步等待 QC 完成,并在 QC 完成时调用回调函数
(pm_qc_finish = hsc->async_qc_finish(last_proposed))
.then([this, pm]() {
HOTSTUFF_LOG_PROTO("got QC, propose a new block");
pm.resolve(proposer); // 解决节奏 promise,返回当前提议者 ID
});
// 设置锁定状态,防止重复处理
locked = true;
}
}
// 更新上一个提议的区块
void proposer_update_last_proposed() {
// 拒绝之前的等待提议 promise
pm_wait_propose.reject();
// 异步等待新的提议,并在提议到达时调用回调函数
(pm_wait_propose = hsc->async_wait_proposal()).then(
[this](const Proposal &prop) {
// 更新上一个提议的区块
last_proposed = prop.blk;
// 解除锁定状态
locked = false;
// 调度下一个提议
proposer_schedule_next();
// 继续等待下一个提议
proposer_update_last_proposed();
});
}
// 发起新的共识流程
void do_new_consensus(int x, const std::vector<uint256_t> &cmds) {
// 使用给定的命令(cmds)和父区块(get_parents())创建一个新的区块
// bytearray_t() 表示附加的额外数据,这里为空
auto blk = hsc->on_propose(cmds, get_parents(), bytearray_t());
// 拒绝之前的 QC 完成 promise
pm_qc_manual.reject();
// 异步等待新创建的区块的 QC 完成
(pm_qc_manual = hsc->async_qc_finish(blk))
.then([this, x]() {
// 打印调试信息,表示收到了区块的 QC
HOTSTUFF_LOG_PROTO("Pacemaker: got QC for block %d", x);
#ifdef HOTSTUFF_TWO_STEP
// 如果宏定义 HOTSTUFF_TWO_STEP 被定义,并且 x >= 2,则返回
if (x >= 2) return;
#else
// 否则,如果 x >= 3,则返回
if (x >= 3) return;
#endif
// 递归调用 do_new_consensus 方法,发起下一步的共识流程
// 传递递增的步骤参数 x 和一个空的命令列表
do_new_consensus(x + 1, std::vector<uint256_t>{});
});
}
// 处理超时事件
void on_exp_timeout(TimerEvent &) {
// 检查当前副本是否是提议者
if (proposer == hsc->get_id()) {
// 如果当前副本是提议者,发起新的共识流程
do_new_consensus(0, std::vector<uint256_t>{});
}
// 创建新的定时器事件以轮换提议者
timer = TimerEvent(ec, [this](TimerEvent &){ rotate(); });
// 设置提议的延迟时间
timer.add(prop_delay);
}
// 轮换提议者
void rotate() {
// 注册提议回调函数以处理新的提议
reg_proposal();
// 注册接收提议回调函数以处理接收到的提议
reg_receive_proposal();
// 清除提议区块缓存,以便在轮换后重新缓存
prop_blk.clear();
// 将状态标记为轮换中,表示当前正在进行提议者的轮换
rotating = true;
// 计算下一个提议者的 ID,轮换至下一个副本
proposer = (proposer + 1) % hsc->get_config().nreplicas;
// 记录提议者轮换的日志信息
HOTSTUFF_LOG_PROTO("Pacemaker: rotate to %d", proposer);
// 取消当前的确认完成、提议等待、以及手动确认的 promise
pm_qc_finish.reject();
pm_wait_propose.reject();
pm_qc_manual.reject();
// 启动定时器以处理超时事件
timer = TimerEvent(ec, salticidae::generic_bind(&PMRoundRobinProposer::on_exp_timeout, this, _1));
// 设置初始超时时间,并准备在超时后触发事件
timer.add(exp_timeout);
// 将超时时间设置为之前的两倍,以实现指数增长的超时机制
exp_timeout *= 2;
}
// 停止轮换
void stop_rotate() {
// 删除当前定时器,停止超时事件的触发
timer.del();
// 记录停止轮换的日志信息
HOTSTUFF_LOG_PROTO("Pacemaker: stop rotation at %d", proposer);
// 取消当前的确认完成、提议等待、以及手动确认的 promise
pm_qc_finish.reject();
pm_wait_propose.reject();
pm_qc_manual.reject();
// 将状态标记为非轮换中,表示轮换过程已结束
rotating = false;
// 将锁定状态设置为非锁定
locked = false;
// 将最后提议的块重置为创世块
last_proposed = hsc->get_genesis();
// 更新上一个提议的区块信息
proposer_update_last_proposed();
// 如果当前副本是提议者
if (proposer == hsc->get_id()) {
// 将当前的 HotStuffCore 对象转换为 HotStuffBase 类型,以调用 do_elected 方法
auto hs = static_cast<hotstuff::HotStuffBase *>(hsc);
// 通知系统当前副本已被当选为提议者
hs->do_elected();
// 异步调用方法以处理决策等待中的命令
hs->get_tcall().async_call([this, hs](salticidae::ThreadCall::Handle &) {
// 获取所有待决策的命令
auto &pending = hs->get_decision_waiting();
// 如果没有待决策的命令,则无需处理
if (!pending.size()) return;
// 记录重新提议待决策命令的日志信息
HOTSTUFF_LOG_PROTO("reproposing pending commands");
// 创建一个命令列表,用于存储待决策的命令
std::vector<uint256_t> cmds;
for (auto &p: pending)
cmds.push_back(p.first);
// 发起新的共识流程以处理这些待决策的命令
do_new_consensus(0, cmds);
});
}
}
protected:
// 处理共识完成
void on_consensus(const block_t &blk) override {
timer.del(); // 删除定时器
exp_timeout = base_timeout; // 重置超时
if (prop_blk[proposer] == blk)
stop_rotate(); // 如果提议块匹配,停止轮换
}
// 处理提议者的罢免
void impeach() override {
if (rotating) return; // 如果正在轮换,直接返回
rotate(); // 否则轮换提议者
HOTSTUFF_LOG_INFO("schedule to impeach the proposer");
}
public:
// 构造函数
PMRoundRobinProposer(const EventContext &ec,
double base_timeout, double prop_delay):
base_timeout(base_timeout),
prop_delay(prop_delay),
ec(ec), proposer(0), rotating(false) {}
// 获取待处理的节奏 promise 数量
size_t get_pending_size() override { return pending_beats.size(); }
// 初始化方法
void init() {
exp_timeout = base_timeout; // 初始化超时值
stop_rotate(); // 停止轮换
}
// 获取当前提议者 ID
ReplicaID get_proposer() override {
return proposer;
}
// 处理节奏的 promise
promise_t beat() override {
if (!rotating && proposer == hsc->get_id())
{
promise_t pm;
pending_beats.push(pm);
proposer_schedule_next(); // 调度下一个提议
return pm;
}
else
return promise_t([proposer=proposer](promise_t &pm) {
pm.resolve(proposer); // 如果不是当前提议者,直接解决 promise
});
}
// 处理节奏响应的 promise
promise_t beat_resp(ReplicaID last_proposer) override {
return promise_t([last_proposer](promise_t &pm) {
pm.resolve(last_proposer); // 解决 promise,返回最后提议者的 ID
});
}
};
在 PMRoundRobinProposer
类中,提议者的轮换主要发生在以下几种情况
1. 定时器超时事件触发时 (on_exp_timeout
)
-
当一个提议者的提议在规定的时间内没有得到足够的响应(例如没有达成共识),会触发一个超时事件。在
on_exp_timeout
方法中,如果当前提议者是自己(当前节点),系统会发起一个新的共识流程do_new_consensus
。之后,系统会重新设置定时器,并调用rotate()
方法来轮换到下一个提议者。
2. 提议者被弹劾时 (impeach
)
-
当系统检测到当前的提议者无法继续履行职责(例如提议者没有按时提出提议,或者提议失败等),可以通过调用
impeach()
方法来弹劾当前的提议者。这个方法会直接调用rotate()
方法,进行提议者的轮换。
PaceMakerRR
类:
- 这是一个结合了
PMHighTail
和PMRoundRobinProposer
的类,继承了两者的功能。 - 它既具备父块选择的逻辑,也具备轮流提议者的机制
/*PaceMakerRR继承自PMHighTail和PMRoundRobinProposer,结合了两者的功能。
提供了初始化方法init,初始化PMHighTail和PMRoundRobinProposer。*/
struct PaceMakerRR: public PMHighTail, public PMRoundRobinProposer {
// 构造函数,初始化基类
PaceMakerRR(EventContext ec, int32_t parent_limit,
double base_timeout = 1, double prop_delay = 1):
PMHighTail(parent_limit),
PMRoundRobinProposer(ec, base_timeout, prop_delay) {}
// 初始化方法
void init(HotStuffCore *hsc) override {
PaceMaker::init(hsc); // 初始化 PaceMaker 基类
PMHighTail::init(); // 初始化 PMHighTail 基类
PMRoundRobinProposer::init(); // 初始化 PMRoundRobinProposer 基类
}
};