hotstuff源码分析(四)(event事件驱动)

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
        });
    }
};

 

PaceMakerDummyPaceMakerDummyFixed

  • 这两个类是 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

  • 这是一个结合了 PMHighTailPMRoundRobinProposer 的类,继承了两者的功能。
  • 它既具备父块选择的逻辑,也具备轮流提议者的机制
/*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 基类
    }
};

 

  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值