hotstuff源码分析(一)

目录

1.前言

2.源代码分析

1)config文件

2)util文件

3)task.h文件 

4)type.h文件

5)crypto文件


1.前言

这一章笔者将对hotstuff底层源码进行分析和记录。先简单回顾一下hotstuff中的理论概念。1)hotstuff是使用了门限签名的一种算法,由领导者发送信息,然后跟随者验证信息签名后由领导者统一验证签名的合法性,简单来说就是只有领导者和跟随者之间的通信,相比于pbft减少了很多的通信。2)hotstuff中可选择三段式共识或者两段式共识。3)hotstuff中分为basic hotstuff 和链式 hotstuff,链式hotstuff 相较于前者可支持流水化作业。4)hotstuff中由pacemaker来推动协议的进行以及一些视图轮换的机制(有文章说在链式hotstuff中在每个 prepare phase 都切换视图及领导者,暂时笔者也是认同的,因为能力有限,笔者也是希望通过写下这些记录来增加自己对代码的认识)

2.源代码分析

左边是头文件,右图是cpp文件,也就是源代码中相关函数的实现。在一份协议中,我们除了处理协议的逻辑关系外,我们还需要定义一些核心结构。在以下文件中,client.h是与客户端有关的命令等结构的定义;crypto.h是关于一些签名、证书的定义;entity.h是关于协议中命令和区块的定义;liveness.h是关于pacemaker的定义;task.h是关于验证任务与任务池的定义(用来辅助并发异步执行);type.h用于引入库、错误基类的文件(起辅助作用);util.h与协议的日志记录有关。promise.hpp定义了一个C++的Promise库,类似于JavaScript中的Promise/A+,用于创建、链式调用和处理异步操作。最后是consensus.h和hotstuff.h,用于实现协议的核心逻辑(基础核心逻辑➡网络实现的核心逻辑➡加密实现的核心逻辑)。我们将根据文件引用关系来逐步分析

1)config文件

config.h.in文件是有关于一些宏的定义(通常是用在 CMake 构建系统中的一种模板文件。它的作用是提供一个基础的头文件模板,在配置和编译过程中,CMake 会根据这个模板文件生成一个实际的 .h 文件。这个生成的 .h 文件会根据构建环境和配置选项进行适当的修改,以满足不同的编译需求。)

//config.h.in文件

#ifndef _HOTSTUFF_CONFIG_H
#define _HOTSTUFF_CONFIG_H

#cmakedefine HOTSTUFF_DEBUG_LOG//*若定义HOTSTUFF_DEBUG_LOG 宏,表示调试日志*/
#cmakedefine HOTSTUFF_NORMAL_LOG/*若定义 HOTSTUFF_NORMAL_LOG 宏,启用正常日志记录。*/
#cmakedefine HOTSTUFF_PROTO_LOG/*若定义 HOTSTUFF_PROTO_LOG 宏,启用协议日志记录。*/
#cmakedefine HOTSTUFF_MSG_STAT/*若定义 HOTSTUFF_MSG_STAT 宏,启用消息统计功能。*/
#cmakedefine HOTSTUFF_BLK_PROFILE/*若定义 HOTSTUFF_BLK_PROFILE 宏,表示使用区块分析功能。*/
#cmakedefine HOTSTUFF_TWO_STEP/*若静止 HOTSTUFF_TWO_STEP 宏,表示使用三步共识协议(默认行为)。*/


#endif
  • #cmakedefine 是 CMake 的特殊指令,表示这个地方会根据 CMake 的配置选项来进行替换。具体来说,CMake 会检查相应的配置选项(例如 HOTSTUFF_DEBUG_LOG 是否被启用),然后在生成的 config.h 文件中将其替换为 #define HOTSTUFF_DEBUG_LOG 或者完全删除这一行。
  • 换句话说,如果在 CMake 配置过程中设置了 HOTSTUFF_DEBUG_LOG,那么生成的 config.h 文件中会有 #define HOTSTUFF_DEBUG_LOG,否则这个宏定义将不会存在。

2)util文件

util文件包括日志功能和条件编译的配置。通过定义和取消定义不同的宏,可以启用或禁用特定的日志功能,还包括一个块分析器的实现,用于在启用块分析时记录块的各种时间点。

Logger类

class Logger: public salticidae::Logger {
    public:
    // 使用基类的构造函数
    using salticidae::Logger::Logger;

    // 新增的 proto 日志函数,用于记录协议相关的日志
    void proto(const char *fmt, ...) {
        va_list ap;  // 声明一个 va_list 类型的变量 ap,用于处理可变参数列表
        va_start(ap, fmt);  // 初始化 va_list 变量 ap,使其指向第一个可变参数
        // 调用基类的 write 函数记录日志
        // 参数 "proto" 是日志的标签,用于标识日志类型
        // is_tty() 函数用于检查当前输出是否是终端,若是终端则使用 TTY_COLOR_MAGENTA 颜色,否则不使用颜色
        write("proto", is_tty() ? salticidae::TTY_COLOR_MAGENTA : nullptr, fmt, ap);
        va_end(ap);  // 使用 va_end 宏清理 va_list 变量 ap
    }
};

//extern Logger logger; 声明一个外部的Logger实例
extern Logger logger;

首先是Logger类,该类继承自salticidae::Logger(如下图),增加了proto方法,用于记录协议相关的日志。简单来说就是增加一种proto日志类别用于日志输出。

//saltcidae:Logger
class Logger {
    const char *color_info;//保存信息级别日志的颜色。
    const char *color_debug;//保存调试级别日志的颜色。
    const char *color_warning;//保存警告级别日志的颜色。
    const char *color_error;//保存错误级别日志的颜色。
    protected:
    int output;//文件描述符,用于确定日志输出的位置(如标准输出或文件)。
    bool opened;//标记日志是否打开了文件。
    const char *prefix;//日志前缀,用于标识日志来源或类别
    void write(const char *tag, const char *color, //私有函数,用于实际写日志内容。
                const char *fmt, va_list ap);
    void set_color();//私有函数,用于设置颜色。

    public:
    //构造函数 Logger(const char *prefix, int fd = 2):初始化日志对象,默认输出到文件描述符 fd(默认为标准错误输出 stderr,即 2),并设置日志前缀和颜色。
    Logger(const char *prefix, int fd = 2):
        output(fd), opened(false), prefix(prefix) { set_color(); }
    //构造函数 Logger(const char *prefix, const char *filename):初始化日志对象,将日志输出到指定文件。
    Logger(const char *prefix, const char *filename):
        opened(true), prefix(prefix) {
        if ((output = open(filename, O_CREAT | O_WRONLY)) == -1)
            throw SalticidaeError("logger cannot open file %s", filename);//尝试打开文件,如果失败则抛出异常。
        set_color();
    }

    ~Logger() { if (opened) close(output); }

    void info(const char *fmt, ...);//记录信息级别日志。
    void debug(const char *fmt, ...);//记录调试级别日志。
    void warning(const char *fmt, ...);//记录警告级别日志
    void error(const char *fmt, ...);//记录错误级别日志。
    bool is_tty() { return isatty(output); }//检查输出是否是一个终端。如果是终端,可以启用彩色输出,否则禁用
};

BlockProfiler类

blockprofiler类用于区块分析,可以看做一个区块分析的机器。在代码中可以看到区块有三种状态,BLK_SEEN、BLK_FETCH、BLK_CC以及对应的四种时间,第一次看到区块,第一次获取到区块、轮询、提交的时间,这里先记住,有助于后续核心逻辑的分析。

#ifdef HOTSTUFF_BLK_PROFILE//如果启用了块分析功能(HOTSTUFF_BLK_PROFILE),则定义BlockProfiler类
//BlockProfiler类用于记录区块的各种时间点,以分析区块的处理过程。
class BlockProfiler {
    // BlockState 枚举定义了区块的三种状态:见到、获取和CC。
    enum BlockState {
        BLK_SEEN,  // 区块哈希第一次被看到
        BLK_FETCH, // 完整区块内容第一次被获取
        BLK_CC     // CC (例如,某种共识机制) 状态
    };

    // BlockProfile 结构体记录了区块的各种时间点和状态。
    struct BlockProfile {
        bool is_local;             // 该区块是否由本地副本提议?
        BlockState state;          // 区块的当前状态
        double hash_seen_time;     // 第一次看到区块哈希的时间
        double full_fetch_time;    // 第一次获取完整区块内容的时间
        double polling_start_time; // 开始轮询的时间
        double commit_time;        // 提交的时间
    };

    // 用于存储每个区块的哈希和其对应的 BlockProfile 的映射
    std::unordered_map<const uint256, BlockProfile> blocks;
    // 用于测量时间的计时器
    ElapsedTime timer;

    public:
    // 构造函数,初始化计时器
    BlockProfiler() { timer.start(); }

    // rec_blk 方法记录见到区块哈希的时间。
    auto rec_blk(const uint256 &blk_hash, bool is_local) {
        auto it = blocks.find(blk_hash);
        // 确保区块哈希之前未被记录
        assert(it == blocks.end());
        timer.stop(false); // 停止计时器,获取当前时间
        // 插入新的 BlockProfile 并返回迭代器
        return blocks.insert(std::make_pair(blk_hash,
            BlockProfile{is_local, BLK_SEEN, timer.elapsed_sec, 0, 0, 0})).first;
    }

    // get_blk 方法记录获取完整区块内容的时间。
    void get_blk(const uint256 &blk_hash) {
        auto it = blocks.find(blk_hash);
        // 如果区块哈希未被记录,则调用 rec_blk 方法记录
        if (it == blocks.end())
            it = rec_blk(blk_hash, false);
        BlockProfile &blkp = it->second;
        // 确保区块当前状态是 BLK_SEEN
        assert(blkp.state == BLK_SEEN);
        timer.stop(false); // 停止计时器,获取当前时间
        blkp.full_fetch_time = timer.elapsed_sec; // 记录获取完整区块内容的时间
        blkp.state = BLK_FETCH; // 更新区块状态为 BLK_FETCH
    }

    // have_cc 方法记录 CC 的时间。
    void have_cc(const uint256 &blk_hash) {
        auto it = blocks.find(blk_hash);
        // 确保区块哈希已被记录
        assert(it != blocks.end());
        BlockProfile &blkp = it->second;
        // 确保区块当前状态是 BLK_FETCH
        assert(blkp.state == BLK_FETCH);
        timer.stop(false); // 停止计时器,获取当前时间
        blkp.polling_start_time = timer.elapsed_sec; // 记录开始轮询的时间
        blkp.state = BLK_CC; // 更新区块状态为 BLK_CC
    }

    // decide_blk 方法记录提交时间,并返回一个包含所有时间点的字符串。
    const char *decide_blk(const uint256 &blk_hash) {
        static char buff[1024]; // 静态缓冲区用于存储结果字符串
        auto it = blocks.find(blk_hash);
        // 确保区块哈希已被记录
        assert(it != blocks.end());
        BlockProfile &blkp = it->second;
        // 确保区块当前状态是 BLK_CC
        assert(blkp.state == BLK_CC);
        timer.stop(false); // 停止计时器,获取当前时间
        blkp.commit_time = timer.elapsed_sec; // 记录提交时间
        // 格式化所有时间点和状态信息到 buff 中
        snprintf(buff, sizeof buff, "(%d,%.4f,%.4f,%.4f,%.4f,%.4f)",
                blkp.is_local,
                blkp.hash_seen_time, blkp.full_fetch_time,
                blkp.polling_start_time, blkp.polling_start_time, // 修正: polling_start_time 应该是 polling_end_time
                blkp.commit_time);
        blocks.erase(it); // 从映射中移除区块
        return buff; // 返回结果字符串
    }
};


#endif

其余的一些宏的定义

#ifdef HOTSTUFF_PROTO_LOG
#define HOTSTUFF_ENABLE_LOG_PROTO
#endif

#ifdef HOTSTUFF_DEBUG_LOG
#define HOTSTUFF_NORMAL_LOG
#define HOTSTUFF_ENABLE_LOG_DEBUG
#define HOTSTUFF_ENABLE_LOG_PROTO
#endif

#ifdef HOTSTUFF_NORMAL_LOG
#define HOTSTUFF_ENABLE_LOG_INFO
#define HOTSTUFF_ENABLE_LOG_WARN
#endif

#ifdef HOTSTUFF_ENABLE_LOG_INFO
#define HOTSTUFF_LOG_INFO(...) hotstuff::logger.info(__VA_ARGS__)
#else
#define HOTSTUFF_LOG_INFO(...) ((void)0)
#endif

#ifdef HOTSTUFF_ENABLE_LOG_DEBUG
#define HOTSTUFF_LOG_DEBUG(...) hotstuff::logger.debug(__VA_ARGS__)
#else
#define HOTSTUFF_LOG_DEBUG(...) ((void)0)
#endif

#ifdef HOTSTUFF_ENABLE_LOG_WARN
#define HOTSTUFF_LOG_WARN(...) hotstuff::logger.warning(__VA_ARGS__)
#else
#define HOTSTUFF_LOG_WARN(...) ((void)0)
#endif

#ifdef HOTSTUFF_ENABLE_LOG_PROTO
#define HOTSTUFF_LOG_PROTO(...) hotstuff::logger.proto(__VA_ARGS__)
#else
#define HOTSTUFF_LOG_PROTO(...) ((void)0)
#endif

#define HOTSTUFF_LOG_ERROR(...) hotstuff::logger.error(__VA_ARGS__)
  • HOTSTUFF_PROTO_LOG

    • 如果定义了 HOTSTUFF_PROTO_LOG,则会启用 HOTSTUFF_ENABLE_LOG_PROTO,这表示与协议相关的日志功能将被启用。
  • HOTSTUFF_DEBUG_LOG

    • 如果定义了 HOTSTUFF_DEBUG_LOG,则自动启用 HOTSTUFF_NORMAL_LOGHOTSTUFF_ENABLE_LOG_DEBUGHOTSTUFF_ENABLE_LOG_PROTO。这表示在调试模式下,普通日志、调试日志和协议日志都将被启用。
  • HOTSTUFF_NORMAL_LOG

    • 如果定义了 HOTSTUFF_NORMAL_LOG,则会启用 HOTSTUFF_ENABLE_LOG_INFOHOTSTUFF_ENABLE_LOG_WARN。这表示普通日志模式下,信息日志和警告日志将被启用。
  • 具体的日志输出宏

    • 如果启用了某个日志功能宏(如 HOTSTUFF_ENABLE_LOG_INFO),相应的日志输出宏(如 HOTSTUFF_LOG_INFO)将真正执行日志操作。
    • 如果没有启用相应的日志功能宏,那么日志输出宏将被定义为空操作(((void)0)),这意味着在代码中调用这些日志宏不会产生任何实际效果。
  • HOTSTUFF_LOG_ERROR

    • HOTSTUFF_LOG_ERROR 是始终定义的,并且总是会调用 hotstuff::logger.error 方法。这表示错误日志不会受到上述任何条件的限制,总是启用的。

3)task.h文件 

该文件继承了util.h和event.h文件。event.h是一个复杂的 C++ 头文件,主要涉及基于 libuv的事件循环管理。libuv 是一个跨平台的异步 I/O 库,用于处理事件循环、文件描述符、定时器、信号处理等。这个文件定义了一系列用于事件处理的类和函数,帮助用户更方便地使用 libuv 提供的功能(笔者理解就是创建一个机器,这个机器中可以定义任何形式的处理任何形式信息的工具,只要打开这个机器,所有工具就都在运行中,触发就会执行相应的步骤,简单来说就是创建了个自动循环的东西)

#include "salticidae/event.h"
#include "hotstuff/util.h"

VeriTask类

 VeriTask 类表示一个验证任务,包含一个布尔值 `result` 表示验证结果。

class VeriTask {
    friend class VeriPool; // 允许 VeriPool 类访问 VeriTask 的私有成员
    bool result; // 验证结果

    public:
    // 纯虚函数,要求派生类实现实际的验证逻辑
    virtual bool verify() = 0;
2
    // 虚析构函数,提供了虚析构函数以确保派生类的析构函数被正确调用。
    virtual ~VeriTask() = default;
};

VeriPool类 

VeriPool 类表示一个验证任务的工作池。包含处理任务的队列和工作线程的管理。逻辑结构是:有待处理队列in_quene和已完成队列out_quene,有线程worker以及对应的线程向量workers,有任务与promise的映射pms(每完成一个任务就会解决一个promise,然后就会触发某些行为)。veripool的逻辑是:verify函数将收到的验证任务加入到in_quene队列。构建函数创建对象后会启动out_quene中的处理函数(和事件那些函数有关),然后初始化工作线程,最后启动工作线程。

class VeriPool {
    // 多生产者多消费者队列,用于接收待处理任务
    mpmc_queue_t in_queue;
    // 多生产者单消费者队列,用于存储处理完成的任务
    mpsc_queue_t out_queue;

    // Worker 结构体表示一个工作线程
    // 包含线程句柄 handle、事件上下文 ec 和线程调用对象 tcall
    struct Worker {
        std::thread handle; // 线程句柄
        EventContext ec;    // 事件上下文,用于调度事件
        BoxObj<ThreadCall> tcall; // 线程调用对象,用于调度线程调用
    };

    std::vector<Worker> workers; // 工作线程的向量
    std::unordered_map<VeriTask *, std::pair<veritask_ut, promise_t>> pms; // 任务与 promise 的映射

    public:
    // 构造函数接收事件上下文 ec、工作线程数量 nworker 和每次处理任务的批量大小 burst_size
    // 注册 out_queue 的处理函数,处理完成的任务
    // 初始化工作线程,注册 in_queue 的处理函数,处理待验证的任务
    VeriPool(EventContext ec, size_t nworker, size_t burst_size = 128) {
        // 注册 out_queue 的处理函数
        out_queue.reg_handler(ec, [this, burst_size](mpsc_queue_t &q) {
            size_t cnt = burst_size;
            VeriTask *task;
            while (q.try_dequeue(task)) {
                auto it = pms.find(task);
                it->second.second.resolve(task->result); // 解决 promise
                pms.erase(it); // 删除映射
                if (!--cnt) return true; // 处理的任务达到批量大小,返回
            }
            return false; // 继续处理队列中的任务
        });

        // 初始化工作线程
        workers.resize(nworker);
        for (size_t i = 0; i < nworker; i++) {
            // 注册 in_queue 的处理函数
            in_queue.reg_handler(workers[i].ec, [this, burst_size](mpmc_queue_t &q) {
                size_t cnt = burst_size;
                VeriTask *task;
                while (q.try_dequeue(task)) {
                    HOTSTUFF_LOG_DEBUG("%lx working on %u",
                                        std::this_thread::get_id(), (uintptr_t)task);
                    task->result = task->verify(); // 执行验证
                    out_queue.enqueue(task); // 将处理完成的任务加入 out_queue
                    if (!--cnt) return true; // 处理的任务达到批量大小,返回
                }
                return false; // 继续处理队列中的任务
            });
        }

        // 启动工作线程
        for (auto &w: workers) {
            w.tcall = new ThreadCall(w.ec); // 创建线程调用对象
            w.handle = std::thread([ec=w.ec]() { ec.dispatch(); }); // 启动线程
        }
    }

    // 析构函数停止所有工作线程
    ~VeriPool() {
        for (auto &w: workers)
            w.tcall->async_call([ec=w.ec](ThreadCall::Handle &) {
                ec.stop(); // 停止事件上下文
            });
        for (auto &w: workers)
            w.handle.join(); // 等待线程结束
    }

    // verify 函数接收一个验证任务,并返回一个 promise
    // 将任务插入 pms 映射,将任务指针加入 in_queue 队列
    promise_t verify(veritask_ut &&task) {
        auto ptr = task.get();
        auto ret = pms.insert(std::make_pair(ptr,
                std::make_pair(std::move(task), promise_t([](promise_t &){}))));
        assert(ret.second); // 确保插入成功
        in_queue.enqueue(ptr); // 将任务加入 in_queue
        return ret.first->second.second; // 返回对应的 promise
    }
};

其余定义

BoxObj可以看成是管理指针的工具

// 从 Salticidae 库中引入 ThreadCall 类型
using salticidae::ThreadCall;
// VeriTask 的智能指针类型
using veritask_ut = BoxObj<VeriTask>;
// 多生产者多消费者事件驱动队列,用于存储待处理的 VeriTask*
using mpmc_queue_t = salticidae::MPMCQueueEventDriven<VeriTask *>;
// 多生产者单消费者事件驱动队列,用于存储处理完成的 VeriTask*
using mpsc_queue_t = salticidae::MPSCQueueEventDriven<VeriTask *>;

4)type.h文件

该文件引入 salticidae 库中的类型和函数,简化了代码编写,并且通过定义接口类和类型别名,提高了代码的可读性和可维护性,负责管理数据类型、错误处理和对象克隆等功能。(辅助的一些工具类)

namespace hotstuff {
//使用 using 关键字将 salticidae 命名空间中的一些常用类型和函数引入到 hotstuff 命名空间中,简化代码编写。
// 引入 salticidae 命名空间中的 RcObj 类型
// 可能是一个引用计数对象类,类似于 std::shared_ptr
using salticidae::RcObj;

// 引入 salticidae 命名空间中的 ArcObj 类型
// 可能是另一个引用计数对象类,可能实现方式不同于 RcObj
using salticidae::ArcObj;

// 引入 salticidae 命名空间中的 BoxObj 类型
// 可能是一个对象包装类,用于动态内存管理,类似于 std::unique_ptr 或 std::shared_ptr
using salticidae::BoxObj;

// 引入 salticidae 命名空间中的 uint256_t 类型
// 这是一个 256 位无符号整数类型,通常用于表示大数或哈希值
using salticidae::uint256_t;

// 引入 salticidae 命名空间中的 DataStream 类型
// 可能用于处理数据的序列化和反序列化
using salticidae::DataStream;

// 引入 salticidae 命名空间中的 htole 函数模板
// 用于将主机字节序转换为小端字节序
using salticidae::htole;

// 引入 salticidae 命名空间中的 letoh 函数模板
// 用于将小端字节序转换为主机字节序
using salticidae::letoh;

// 引入 salticidae 命名空间中的 get_hex 函数
// 用于将数据转换为其十六进制表示
using salticidae::get_hex;

// 引入 salticidae 命名空间中的 from_hex 函数
// 用于将十六进制字符串转换为数据
using salticidae::from_hex;

// 引入 salticidae 命名空间中的 bytearray_t 类型
// 表示一个字节数组的类型,通常是 std::vector<uint8_t>
using salticidae::bytearray_t;

// 引入 salticidae 命名空间中的 get_hex10 函数
// 可能是 get_hex 的变体,支持特定格式或更长的十六进制字符串
using salticidae::get_hex10;

// 引入 salticidae 命名空间中的 get_hash 函数
// 用于计算哈希值,可能返回某种类型的哈希值
using salticidae::get_hash;

// 引入 salticidae 命名空间中的 NetAddr 类型
// 网络地址类,可能用于表示 IP 地址和端口
using salticidae::NetAddr;

// 引入 salticidae 命名空间中的 PeerId 类型
// 表示网络对等体标识的类
using salticidae::PeerId;

// 引入 salticidae 命名空间中的 TimerEvent 类型
// 定时器事件类,用于处理定时任务或事件
using salticidae::TimerEvent;

// 引入 salticidae 命名空间中的 FdEvent 类型
// 文件描述符事件类,用于处理 I/O 事件
using salticidae::FdEvent;

// 引入 salticidae 命名空间中的 EventContext 类型
// 事件上下文类,用于事件处理和管理
using salticidae::EventContext;

using promise::promise_t;//promise::promise_t 是Promise库中的一个类型,用于管理异步操作

//HotStuffError 类继承自 salticidae::SalticidaeError,是所有HotStuff错误的基类。
class HotStuffError: public salticidae::SalticidaeError {
    public:
    template<typename... Args>
    HotStuffError(Args... args): salticidae::SalticidaeError(args...) {}
};
//HotStuffInvalidEntity 类继承自 HotStuffError,用于表示无效实体的错误。
class HotStuffInvalidEntity: public HotStuffError {
    public:
    template<typename... Args>
    HotStuffInvalidEntity(Args... args): HotStuffError(args...) {}
};
//引入 Serializable 类型,用于表示可以序列化的对象。
using salticidae::Serializable;

//Cloneable 是一个接口类,提供一个纯虚函数 clone,要求实现该接口的类必须提供对象拷贝的方法。
class Cloneable {
    public:
    virtual ~Cloneable() = default;
    virtual Cloneable *clone() = 0;
};
/*ReplicaID:表示副本ID,类型为 uint16_t。
opcode_t:表示操作码,类型为 uint8_t。
tls_pkey_bt 和 tls_x509_bt:表示TLS密钥和证书的智能指针,使用 BoxObj 封装。*/
using ReplicaID = uint16_t;
using opcode_t = uint8_t;
using tls_pkey_bt = BoxObj<salticidae::PKey>;
using tls_x509_bt = BoxObj<salticidae::X509>;

}

5)crypto文件

该文件包括加密和签名的相关类和函数。该文件包括公钥、私钥、部分证书、法定证书(Quorum Certificate)等的实现。 这些类和函数主要使用了secp256k1库进行椭圆曲线加密(ECC)操作。

这个文件的主要逻辑架构中,定义了公钥、私钥、部分证书、法定证书的抽象类➡测试类(dummy)➡实现椭圆曲线加密的类。

代码分析

Secp256k1Context 类

该类封装了 secp256k1 库的上下文 (secp256k1_context *),用于签名和验证操作。它管理 secp256k1 上下文的创建、销毁,以及移动语义,以确保资源的正确管理。

/*Secp256k1Context 类 */
class Secp256k1Context {
    // 私有成员变量 ctx 是指向 secp256k1_context 的指针,表示 secp256k1 的上下文。
    secp256k1_context *ctx;

    // 友元类声明,允许 PubKeySecp256k1 和 SigSecp256k1 直接访问 Secp256k1Context 的私有成员 ctx。
    friend class PubKeySecp256k1;
    friend class SigSecp256k1;

    public:
    /*
     * 构造函数 Secp256k1Context,接受一个布尔值 sign 作为参数,默认为 false。
     * 如果 sign 为 true,创建一个用于签名操作的上下文;
     * 否则,创建一个用于验证操作的上下文。
     */
    Secp256k1Context(bool sign = false):
        ctx(secp256k1_context_create(
            sign ? SECP256K1_CONTEXT_SIGN :  // 如果 sign 为 true,则创建签名上下文
                    SECP256K1_CONTEXT_VERIFY)) {} // 否则,创建验证上下文

    // 删除拷贝构造函数,防止 Secp256k1Context 对象被拷贝。
    Secp256k1Context(const Secp256k1Context &) = delete;

    /*
     * 移动构造函数,将另一个 Secp256k1Context 对象的资源移动到当前对象。
     * 移动后,other.ctx 被置为 nullptr,以避免重复释放资源。
     */
    Secp256k1Context(Secp256k1Context &&other): ctx(other.ctx) {
        other.ctx = nullptr; // 将原对象的指针置为空,防止双重释放
    }

    /*
     * 析构函数,销毁 secp256k1_context 上下文。
     * 如果 ctx 非空,则调用 secp256k1_context_destroy 释放上下文资源。
     */
    ~Secp256k1Context() {
        if (ctx) secp256k1_context_destroy(ctx);
    }
};


//`secp256k1_context_t是Secp256k1Context`的智能指针类型。
using secp256k1_context_t = ArcObj<Secp256k1Context>;

//secp256k1_default_sign_ctx和secp256k1_default_verify_ctx是默认的签名和验证上下文
extern secp256k1_context_t secp256k1_default_sign_ctx;
extern secp256k1_context_t secp256k1_default_verify_ctx;

secp256k1_context_t secp256k1_default_sign_ctx = new Secp256k1Context(true);
secp256k1_context_t secp256k1_default_verify_ctx = new Secp256k1Context(false);

SigSecp256k1类

 表示使用 secp256k1 算法生成的签名,并实现了 Serializable 接口。

// SigSecp256k1 类
class SigSecp256k1: public Serializable {
    // secp256k1_ecdsa_signature 结构体,用于存储 secp256k1 ECDSA 签名数据。
    secp256k1_ecdsa_signature data;

    // secp256k1_context_t 上下文,用于存储 secp256k1 操作的上下文信息(如签名和验证)。
    secp256k1_context_t ctx;

    // 静态方法,用于检查消息的长度是否为 32 字节(符合 secp256k1 的要求)。
    static void check_msg_length(const bytearray_t &msg) {
        if (msg.size() != 32)
            throw std::invalid_argument("the message should be 32-bytes");
    }

public:
    /*
     * 默认构造函数,接受一个 secp256k1_context_t 上下文作为参数,
     * 如果没有提供,则使用默认的签名上下文 secp256k1_default_sign_ctx。
     */
    SigSecp256k1(const secp256k1_context_t &ctx =
                        secp256k1_default_sign_ctx):
        Serializable(), ctx(ctx) {}

    /*
     * 构造函数,接受消息摘要、私钥和上下文作为参数,并立即生成签名。
     * 其中 digest 是消息摘要(长度为 32 字节),priv_key 是私钥,ctx 是上下文。
     */
    SigSecp256k1(const uint256_t &digest,
                const PrivKeySecp256k1 &priv_key,
                secp256k1_context_t &ctx =
                        secp256k1_default_sign_ctx):
        Serializable(), ctx(ctx) {
        sign(digest, priv_key);  // 立即生成签名
    }

    /*
     * 序列化方法,将签名数据序列化为紧凑的字节格式,并写入 DataStream。
     * 使用 secp256k1_ecdsa_signature_serialize_compact 函数将签名数据转换为 64 字节的格式。
     */
    void serialize(DataStream &s) const override {
        static uint8_t output[64];  // 存储紧凑格式的签名数据
        (void)secp256k1_ecdsa_signature_serialize_compact(
            ctx->ctx, (unsigned char *)output,
            &data);
        s.put_data(output, output + 64);  // 将签名数据写入 DataStream
    }

    /*
     * 反序列化方法,从 DataStream 中读取紧凑格式的签名数据,并解析为 secp256k1_ecdsa_signature 结构体。
     * 使用 secp256k1_ecdsa_signature_parse_compact 函数进行解析。
     */
    void unserialize(DataStream &s) override {
        static const auto _exc = std::invalid_argument("ill-formed signature");
        try {
            if (!secp256k1_ecdsa_signature_parse_compact(
                    ctx->ctx, &data, s.get_data_inplace(64)))
                throw _exc;  // 如果解析失败,抛出无效签名异常
        } catch (std::ios_base::failure &) {
            throw _exc;  // 捕获流操作失败异常并重新抛出为无效签名异常
        }
    }

    /*
     * sign 方法,用于使用给定的私钥对消息进行签名。
     * 消息 msg 必须是 32 字节的摘要(hash 值),priv_key 是私钥。
     * 该方法首先检查消息长度,然后使用 secp256k1_ecdsa_sign 生成签名。
     */
    void sign(const bytearray_t &msg, const PrivKeySecp256k1 &priv_key) {
        check_msg_length(msg);  // 检查消息长度是否为 32 字节
        if (!secp256k1_ecdsa_sign(
                ctx->ctx, &data,
                (unsigned char *)&*msg.begin(),
                (unsigned char *)priv_key.data,
                NULL, // 默认的 nonce 生成函数
                NULL))  // 默认的数据参数
            throw std::invalid_argument("failed to create secp256k1 signature");  // 签名失败时抛出异常
    }

    /*
     * verify 方法,使用给定的公钥和消息验证签名是否有效。
     * 消息 msg 必须是 32 字节的摘要,pub_key 是公钥,_ctx 是上下文。
     * 返回值为 true 表示签名验证成功,false 表示失败。
     */
    bool verify(const bytearray_t &msg, const PubKeySecp256k1 &pub_key,
                const secp256k1_context_t &_ctx) const {
        check_msg_length(msg);  // 检查消息长度是否为 32 字节
        return secp256k1_ecdsa_verify(
                _ctx->ctx, &data,
                (unsigned char *)&*msg.begin(),
                &pub_key.data) == 1;  // 使用 secp256k1 库进行签名验证
    }

    /*
     * 重载的 verify 方法,使用类的默认上下文进行签名验证。
     * 消息 msg 和公钥 pub_key 是必需的参数。
     */
    bool verify(const bytearray_t &msg, const PubKeySecp256k1 &pub_key) {
        return verify(msg, pub_key, ctx);
    }
};

公钥Pubkey

/ * PubKey 类表示公钥,继承自 Serializable 和 Cloneable。
Serializable 和 Cloneable 是接口(或者抽象类),它们要求子类实现序列化/反序列化和克隆功能 */
class PubKey: public Serializable, Cloneable {
public:
    // 虚析构函数,确保通过基类指针删除派生类对象时,派生类的析构函数能够被正确调用。
    virtual ~PubKey() = default;

    // 纯虚函数,要求派生类实现自己的 clone 方法,
    // 该方法返回一个指向新创建的自身类型对象的指针(即深拷贝)。
    virtual PubKey *clone() override = 0;
};

/*pubkey_bt 是 PubKey 类的智能指针类型,使用 BoxObj 模板类进行管理。
 BoxObj 是一个假定的智能指针类型,负责自动管理 PubKey 对象的生命周期。/
using pubkey_bt = BoxObj<PubKey>;

/*PubKeyDummy 是一个虚拟的公钥类,用于测试或占位符,它实现了 PubKey 的所有虚函数。*/
class PubKeyDummy: public PubKey {
    // 实现 clone 函数,返回一个新的 PubKeyDummy 对象的拷贝。
    PubKeyDummy *clone() override { return new PubKeyDummy(*this); }

    // 实现序列化函数,将对象数据写入 DataStream(假定的流类型)。
    void serialize(DataStream &) const override {}

    // 实现反序列化函数,从 DataStream 中读取数据并设置对象状态。
    void unserialize(DataStream &) override {}
};

// PubKeySecp256k1 类表示使用 secp256k1 算法的公钥
class PubKeySecp256k1: public PubKey {
    // _olen 是公钥的输出长度,设置为 33 字节(压缩形式的 secp256k1 公钥长度)。
    static const auto _olen = 33;

    // SigSecp256k1 是友元类,允许它访问 PubKeySecp256k1 的私有成员。
    friend class SigSecp256k1;

    // secp256k1_pubkey 是一个结构体,用于保存 secp256k1 公钥数据。
    secp256k1_pubkey data;

    // secp256k1_context_t 是一个上下文对象,包含 secp256k1 库的上下文信息。
    secp256k1_context_t ctx;

public:
    // 构造函数,接受一个 secp256k1_context_t 上下文作为参数,并初始化上下文。
    PubKeySecp256k1(const secp256k1_context_t &ctx =
                            secp256k1_default_sign_ctx):
        PubKey(), ctx(ctx) {}

    // 构造函数,接受原始字节数组和上下文作为参数,并通过 from_bytes 函数初始化公钥。
    PubKeySecp256k1(const bytearray_t &raw_bytes,
                    const secp256k1_context_t &ctx =
                            secp256k1_default_sign_ctx):
        PubKeySecp256k1(ctx) { from_bytes(raw_bytes); }

    // 使用私钥创建公钥的构造函数声明(定义在类外部)。
    inline PubKeySecp256k1(const PrivKeySecp256k1 &priv_key,
                            const secp256k1_context_t &ctx =
                                    secp256k1_default_sign_ctx);

    // 序列化函数,将 secp256k1 公钥数据序列化为压缩格式并写入 DataStream。
    void serialize(DataStream &s) const override {
        static uint8_t output[_olen]; // 用于存储压缩后的公钥数据
        size_t olen = _olen;          // 输出数据长度,初始化为 33 字节
        (void)secp256k1_ec_pubkey_serialize(
                ctx->ctx, (unsigned char *)output,
                &olen, &data, SECP256K1_EC_COMPRESSED); // 使用压缩形式序列化公钥
        s.put_data(output, output + _olen); // 将数据写入 DataStream
    }

    // 反序列化函数,从 DataStream 中读取公钥数据并解析为 secp256k1_pubkey。
    void unserialize(DataStream &s) override {
        static const auto _exc = std::invalid_argument("ill-formed public key");
        try {
            // 从 DataStream 中获取原始公钥字节,并使用 secp256k1 库进行解析。
            if (!secp256k1_ec_pubkey_parse(
                    ctx->ctx, &data, s.get_data_inplace(_olen), _olen))
                throw _exc; // 如果解析失败,抛出无效参数异常
        } catch (std::ios_base::failure &) {
            throw _exc; // 捕获流操作失败异常并重新抛出为无效参数异常
        }
    }

    // clone 函数,返回当前对象的一个深拷贝。
    PubKeySecp256k1 *clone() override {
        return new PubKeySecp256k1(*this);
    }
};

// PubKeySecp256k1 类的构造函数定义,使用私钥生成公钥。
PubKeySecp256k1::PubKeySecp256k1(
        const PrivKeySecp256k1 &priv_key,
        const secp256k1_context_t &ctx): PubKey(), ctx(ctx) {
    // 使用 secp256k1 库的私钥生成公钥,如果失败则抛出异常。
    if (!secp256k1_ec_pubkey_create(ctx->ctx, &data, priv_key.data))
        throw std::invalid_argument("invalid secp256k1 private key");
}

PrivKey类 

PrivKey类表示私钥,继承自Serializable。定义了虚析构函数和纯虚函数get_pubkey和from_rand。

class PrivKey: public Serializable {
    public:
    virtual ~PrivKey() = default;
    virtual pubkey_bt get_pubkey() const = 0;
    virtual void from_rand() = 0;
};

using privkey_bt = BoxObj<PrivKey>;

/*PrivKeyDummy是一个虚拟的私钥类,实现了PrivKey的虚函数*/
class PrivKeyDummy: public PrivKey {
    pubkey_bt get_pubkey() const override { return new PubKeyDummy(); }
    void serialize(DataStream &) const override {}
    void unserialize(DataStream &) override {}
    void from_rand() override {}
};

/*
 * PrivKeySecp256k1 类表示使用 secp256k1 算法的私钥。
 * 它继承自 PrivKey 类,并实现了私钥的序列化、反序列化、随机生成等功能。
 */
class PrivKeySecp256k1: public PrivKey {
    // nbytes 是私钥的字节长度,对于 secp256k1 算法来说,私钥长度是 32 字节。
    static const auto nbytes = 32;

    // 将 PubKeySecp256k1 和 SigSecp256k1 声明为友元类,
    // 使它们可以直接访问 PrivKeySecp256k1 的私有成员 data 和 ctx。
    friend class PubKeySecp256k1;
    friend class SigSecp256k1;

    // data 数组用于存储 32 字节长的私钥数据。
    uint8_t data[nbytes];

    // secp256k1_context_t 是 secp256k1 库的上下文,用于管理和配置私钥操作的环境。
    secp256k1_context_t ctx;

public:
    /*
     * 默认构造函数,接受一个 secp256k1_context_t 上下文作为参数,
     * 如果未提供,则使用默认的签名上下文 secp256k1_default_sign_ctx 进行初始化。
     */
    PrivKeySecp256k1(const secp256k1_context_t &ctx =
                            secp256k1_default_sign_ctx):
        PrivKey(), ctx(ctx) {}

    /*
     * 构造函数,接受原始字节数组和上下文作为参数,
     * 并通过 from_bytes 方法将原始字节数组转换为私钥数据。
     */
    PrivKeySecp256k1(const bytearray_t &raw_bytes,
                     const secp256k1_context_t &ctx =
                            secp256k1_default_sign_ctx):
        PrivKeySecp256k1(ctx) { from_bytes(raw_bytes); }

    /*
     * 序列化方法,将私钥数据序列化为字节数组,并写入 DataStream。
     * 这允许私钥数据被持久化或传输。
     */
    void serialize(DataStream &s) const override {
        s.put_data(data, data + nbytes);  // 将私钥数据写入 DataStream
    }

    /*
     * 反序列化方法,从 DataStream 中读取字节数据,并将其加载为私钥。
     * 如果读取失败,抛出异常。
     */
    void unserialize(DataStream &s) override {
        static const auto _exc = std::invalid_argument("ill-formed private key");
        try {
            // 将 nbytes 长度的数据从流中读取到 data 数组中。
            memmove(data, s.get_data_inplace(nbytes), nbytes);
        } catch (std::ios_base::failure &) {
            throw _exc;  // 如果读取失败,抛出无效私钥异常
        }
    }

    /*
     * from_rand 方法使用 OpenSSL 的 RAND_bytes 函数生成随机的私钥数据。
     * 如果随机数生成失败,抛出运行时异常。
     */
    void from_rand() override {
        if (!RAND_bytes(data, nbytes))  // 使用 OpenSSL 生成随机私钥
            throw std::runtime_error("cannot get rand bytes from openssl");  // 随机数生成失败时抛出异常
    }

    /*
     * get_pubkey 方法生成并返回与该私钥对应的公钥。
     * 公钥以 pubkey_bt 类型返回,该类型是一个智能指针,用于管理公钥对象的生命周期。
     */
    inline pubkey_bt get_pubkey() const override;
};

/*
 * get_pubkey 方法的实现。
 * 它使用当前的私钥对象生成一个新的 PubKeySecp256k1 对象,
 * 并返回一个指向该对象的智能指针。
 */
pubkey_bt PrivKeySecp256k1::get_pubkey() const {
    return new PubKeySecp256k1(*this, ctx);
}

Partcert和QuorumCert

PartCertQuorumCert 是一个从局部到整体的关系。PartCert 代表单个副本的部分签名,QuorumCert 代表多个副本的签名组合,通常要求达到某个法定多数以确认某个状态或块的有效性。

/* 
 * PartCert 类表示部分证书,继承自 Serializable 和 Cloneable。
 * 它定义了虚析构函数和几个纯虚函数,包括同步和异步验证函数 verify,获取对象哈希值函数 get_obj_hash 以及 clone 函数。
 * part_cert_bt 是 PartCert 的智能指针类型。
 */
class PartCert: public Serializable, public Cloneable {
public:
    virtual ~PartCert() = default;

    // 异步验证部分证书,使用给定的公钥和验证池。
    virtual promise_t verify(const PubKey &pubkey, VeriPool &vpool) = 0;

    // 同步验证部分证书,使用给定的公钥。
    virtual bool verify(const PubKey &pubkey) = 0;

    // 获取部分证书对应的对象哈希值。
    virtual const uint256_t &get_obj_hash() const = 0;

    // 克隆当前对象,返回一个新的 PartCert 对象的深拷贝。
    virtual PartCert *clone() override = 0;
};

/* 
 * ReplicaConfig 类用于管理副本配置。包含一个副本信息的哈希表 replica_map,
 * 以及副本总数 nreplicas 和多数派数目 nmajority。提供了方法来添加副本、获取副本信息、获取公钥和获取对等 ID。
 */
class ReplicaConfig;

/* 
 * QuorumCert 类表示法定证书,继承自 Serializable 和 Cloneable。
 * 它定义了虚析构函数和几个纯虚函数,包括添加部分证书 add_part,计算法定证书 compute,
 * 同步和异步验证函数 verify,获取对象哈希值函数 get_obj_hash 以及 clone 函数。
 * quorum_cert_bt 是 QuorumCert 的智能指针类型。
 */
class QuorumCert: public Serializable, public Cloneable {
public:
    virtual ~QuorumCert() = default;

    // 添加一个部分证书到法定证书中。
    virtual void add_part(ReplicaID replica, const PartCert &pc) = 0;

    // 计算法定证书的最终结果。
    virtual void compute() = 0;

    // 异步验证法定证书,使用给定的副本配置和验证池。
    virtual promise_t verify(const ReplicaConfig &config, VeriPool &vpool) = 0;

    // 同步验证法定证书,使用给定的副本配置。
    virtual bool verify(const ReplicaConfig &config) = 0;

    // 获取法定证书对应的对象哈希值。
    virtual const uint256_t &get_obj_hash() const = 0;

    // 克隆当前对象,返回一个新的 QuorumCert 对象的深拷贝。
    virtual QuorumCert *clone() override = 0;
};

using part_cert_bt = BoxObj<PartCert>;
using quorum_cert_bt = BoxObj<QuorumCert>;

/* 
 * PartCertDummy 是一个虚拟的部分证书类,用于测试或占位符,
 * 实现了 PartCert 的所有纯虚函数。
 */
class PartCertDummy: public PartCert {
    uint256_t obj_hash;  // 用于存储对象哈希值

public:
    PartCertDummy() {}
    PartCertDummy(const uint256_t &obj_hash):
        obj_hash(obj_hash) {}

    // 序列化函数,将对象数据写入 DataStream。
    void serialize(DataStream &s) const override {
        s << (uint32_t)0 << obj_hash;
    }

    // 反序列化函数,从 DataStream 中读取数据并设置对象状态。
    void unserialize(DataStream &s) override {
        uint32_t tmp;
        s >> tmp >> obj_hash;
    }

    // 克隆当前对象,返回一个新的 PartCertDummy 对象。
    PartCert *clone() override {
        return new PartCertDummy(obj_hash);
    }

    // 同步验证函数,总是返回 true,因为这是一个虚拟类。
    bool verify(const PubKey &) override { return true; }

    // 异步验证函数,总是返回一个已解决的 promise,因为这是一个虚拟类。
    promise_t verify(const PubKey &, VeriPool &) override {
        return promise_t([](promise_t &pm){ pm.resolve(true); });
    }

    // 获取对象的哈希值。
    const uint256_t &get_obj_hash() const override { return obj_hash; }
};

/* 
 * QuorumCertDummy 是一个虚拟的法定证书类,用于测试或占位符,
 * 实现了 QuorumCert 的所有纯虚函数。
 */
class QuorumCertDummy: public QuorumCert {
    uint256_t obj_hash;  // 用于存储对象哈希值

public:
    QuorumCertDummy() {}
    QuorumCertDummy(const ReplicaConfig &, const uint256_t &obj_hash):
        obj_hash(obj_hash) {}

    // 序列化函数,将对象数据写入 DataStream。
    void serialize(DataStream &s) const override {
        s << (uint32_t)1 << obj_hash;
    }

    // 反序列化函数,从 DataStream 中读取数据并设置对象状态。
    void unserialize(DataStream &s) override {
        uint32_t tmp;
        s >> tmp >> obj_hash;
    }

    // 克隆当前对象,返回一个新的 QuorumCertDummy 对象。
    QuorumCert *clone() override {
        return new QuorumCertDummy(*this);
    }

    // 添加部分证书的虚拟实现(不执行实际操作)。
    void add_part(ReplicaID, const PartCert &) override {}

    // 计算法定证书的虚拟实现(不执行实际操作)。
    void compute() override {}

    // 同步验证函数,总是返回 true,因为这是一个虚拟类。
    bool verify(const ReplicaConfig &) override { return true; }

    // 异步验证函数,总是返回一个已解决的 promise,因为这是一个虚拟类。
    promise_t verify(const ReplicaConfig &, VeriPool &) override {
        return promise_t([](promise_t &pm) { pm.resolve(true); });
    }

    // 获取对象的哈希值。
    const uint256_t &get_obj_hash() const override { return obj_hash; }
};

/* 
 * PartCertSecp256k1 类表示使用 secp256k1 算法的部分证书,
 * 它继承了 SigSecp256k1 和 PartCert 类,实现了相关的功能。
 */
class PartCertSecp256k1: public SigSecp256k1, public PartCert {
    uint256_t obj_hash;  // 存储部分证书的对象哈希值

public:
    PartCertSecp256k1() = default;
    PartCertSecp256k1(const PrivKeySecp256k1 &priv_key, const uint256_t &obj_hash):
        SigSecp256k1(obj_hash, priv_key),
        PartCert(),
        obj_hash(obj_hash) {}

    // 使用给定的公钥同步验证部分证书。
    bool verify(const PubKey &pub_key) override {
        return SigSecp256k1::verify(obj_hash,
                                    static_cast<const PubKeySecp256k1 &>(pub_key),
                                    secp256k1_default_verify_ctx);
    }

    // 使用给定的公钥和验证池异步验证部分证书。
    promise_t verify(const PubKey &pub_key, VeriPool &vpool) override {
        return vpool.verify(new Secp256k1VeriTask(obj_hash,
                static_cast<const PubKeySecp256k1 &>(pub_key),
                static_cast<const SigSecp256k1 &>(*this)));
    }

    // 获取部分证书的对象哈希值。
    const uint256_t &get_obj_hash() const override { return obj_hash; }

    // 克隆当前对象,返回一个新的 PartCertSecp256k1 对象。
    PartCertSecp256k1 *clone() override {
        return new PartCertSecp256k1(*this);
    }

    // 序列化函数,将部分证书数据写入 DataStream。
    void serialize(DataStream &s) const override {
        s << obj_hash;
        this->SigSecp256k1::serialize(s);
    }

    // 反序列化函数,从 DataStream 中读取数据并设置部分证书的状态。
    void unserialize(DataStream &s) override {
        s >> obj_hash;
        this->SigSecp256k1::unserialize(s);
    }
};

/* 
 * QuorumCertSecp256k1 类表示使用 secp256k1 算法的法定证书,
 * 它继承了 QuorumCert 类,实现了相关的功能。
 */
class QuorumCertSecp256k1: public QuorumCert {
    uint256_t obj_hash;               // 目标对象的哈希值
    salticidae::Bits rids;            // 记录哪些节点签署了 QC
    std::unordered_map<ReplicaID, SigSecp256k1> sigs; // 节点 ID 到其签名的映射

public:
    QuorumCertSecp256k1() = default; // 默认构造函数
    QuorumCertSecp256k1(const ReplicaConfig &config, const uint256_t &obj_hash); // 带配置和对象哈希的构造函数

    // 添加一个部分证书到法定证书中。
    void add_part(ReplicaID rid, const PartCert &pc) override {
        // 确保 PartCert 的对象哈希值与当前 QC 的对象哈希值匹配
        if (pc.get_obj_hash() != obj_hash)
            throw std::invalid_argument("PartCert does match the block hash");
        // 插入新的签名
        sigs.insert(std::make_pair(
            rid, static_cast<const PartCertSecp256k1 &>(pc)));
        // 记录下该节点已参与签名
        rids.set(rid);
    }

    // 计算法定证书的虚拟实现(不执行实际操作)。
    void compute() override {}

    // 使用给定的副本配置同步验证法定证书。
    bool verify(const ReplicaConfig &config) override;

    // 使用给定的副本配置和验证池异步验证法定证书。
    promise_t verify(const ReplicaConfig &config, VeriPool &vpool) override;

    // 获取法定证书的对象哈希值。
    const uint256_t &get_obj_hash() const override { return obj_hash; }

    // 克隆当前对象,返回一个新的 QuorumCertSecp256k1 对象。
    QuorumCertSecp256k1 *clone() override {
        return new QuorumCertSecp256k1(*this); // 克隆当前 QC 对象
    }

    // 序列化函数,将对象哈希值和节点 ID 集合序列化到数据流中。
    void serialize(DataStream &s) const override {
        s << obj_hash << rids;
        for (size_t i = 0; i < rids.size(); i++)
            if (rids.get(i)) s << sigs.at(i);
    }

    // 反序列化函数,从数据流中反序列化对象哈希值和节点 ID 集合。
    void unserialize(DataStream &s) override {
        s >> obj_hash >> rids;
        for (size_t i = 0; i < rids.size(); i++)
            if (rids.get(i)) s >> sigs[i];
    }
};

QuorumCertSecp256k1::QuorumCertSecp256k1(
        const ReplicaConfig &config, const uint256_t &obj_hash):
            QuorumCert(), obj_hash(obj_hash), rids(config.nreplicas) {
    rids.clear();
}
   
bool QuorumCertSecp256k1::verify(const ReplicaConfig &config) {
    // 检查签名数量是否满足所需的多数签名
    if (sigs.size() < config.nmajority) return false;

    // 遍历所有签名
    for (size_t i = 0; i < rids.size(); i++)
        // 检查每个签名是否有效
        if (rids.get(i))
        {
            HOTSTUFF_LOG_DEBUG("checking cert(%d), obj_hash=%s",
                                i, get_hex10(obj_hash).c_str());

            // 验证当前签名是否与相应公钥匹配
            if (!sigs[i].verify(obj_hash,
                            static_cast<const PubKeySecp256k1 &>(config.get_pubkey(i)),
                            secp256k1_default_verify_ctx))
                return false;
        }

    // 如果所有签名都有效,则返回 true
    return true;
}


promise_t QuorumCertSecp256k1::verify(const ReplicaConfig &config, VeriPool &vpool) {
    if (sigs.size() < config.nmajority)
        return promise_t([](promise_t &pm) { pm.resolve(false); });
    std::vector<promise_t> vpm;
    for (size_t i = 0; i < rids.size(); i++)
        if (rids.get(i))
        {
            HOTSTUFF_LOG_DEBUG("checking cert(%d), obj_hash=%s",
                                i, get_hex10(obj_hash).c_str());
            vpm.push_back(vpool.verify(new Secp256k1VeriTask(obj_hash,
                            static_cast<const PubKeySecp256k1 &>(config.get_pubkey(i)),
                            sigs[i])));
        }
    return promise::all(vpm).then([](const promise::values_t &values) {
        for (const auto &v: values)
            if (!promise::any_cast<bool>(v)) return false;
        return true;
    });
}

补充Secp256k1VeriTask类

继承VeriTask类用于椭圆曲线加密证书的异步验证

//Secp256k1VeriTask类表示一个验证任务,继承自VeriTask。
class Secp256k1VeriTask: public VeriTask {
    uint256_t msg;
    PubKeySecp256k1 pubkey;
    SigSecp256k1 sig;
    public:
    Secp256k1VeriTask(const uint256_t &msg,
                        const PubKeySecp256k1 &pubkey,
                        const SigSecp256k1 &sig):
        msg(msg), pubkey(pubkey), sig(sig) {}
    virtual ~Secp256k1VeriTask() = default;

    bool verify() override {
        return sig.verify(msg, pubkey, secp256k1_default_verify_ctx);
    }
};

总结

实现的逻辑

  1. 公钥和私钥管理:

    • 使用 PrivKeySecp256k1 生成私钥,并导出相应的 PubKeySecp256k1 对象。
    • 私钥可以用来签名数据,而公钥则用于验证签名。
  2. 部分证书的创建和验证:

    • 使用 PartCertSecp256k1 创建部分证书。副本在区块链系统中对某个数据(例如一个区块的哈希)进行签名,生成一个 PartCertSecp256k1 对象。
    • 其他副本可以使用相应的公钥来验证这个部分证书的有效性。
  3. 法定证书的创建和验证:

    • 多个部分证书可以组合成一个 QuorumCertSecp256k1 对象,表示该数据得到了足够多的签名支持,形成了法定证书。
    • 法定证书可以通过遍历所有的部分证书,验证每一个签名的有效性来进行验证。
  4. 异步任务和并行验证:

    • VeriPool 提供了并行验证的功能,可以同时验证多个签名,减少等待时间。
    • QuorumCertSecp256k1::verify 提供了异步验证功能,利用 promise_t 实现异步操作,当所有签名验证完成后再返回结果。

关键流程

  1. 签名创建

    • 创建私钥 PrivKeySecp256k1
    • 使用私钥对数据进行签名,生成 SigSecp256k1 对象。
  2. 签名验证

    • 使用对应的公钥 PubKeySecp256k1 和数据,对签名 SigSecp256k1 进行验证。
    • 如果签名和数据匹配,则返回验证通过。
  3. 部分证书生成和验证

    • 使用 PartCertSecp256k1 对数据进行签名,生成部分证书。
    • 使用公钥对部分证书进行验证。
  4. 法定证书生成和验证

    • 收集足够多的 PartCertSecp256k1,并将它们添加到 QuorumCertSecp256k1
    • 验证 QuorumCertSecp256k1 中所有部分证书的有效性,确保它们都签署了相同的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值