欢迎光临
我们一直在努力

Diem 区块链中的状态机复制:DiemBFT 共识算法核心解析与源码实现

Diem 区块链中的状态机复制:DiemBFT 共识算法核心解析与源码实现

【免费下载链接】diem Diem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world. 【免费下载链接】diem 项目地址: https://gitcode.com/gh_mirrors/di/diem

导读

本文基于 Diem(原 Libra)项目官方技术论文《State Machine Replication in the Diem Blockchain》整理而成,全面讲解 Diem 共识协议 DiemBFT(Diem Byzantine Fault Tolerance)的算法核心:它如何在可配置的验证者(validator)集合上,对交易的排序与最终确认达成一致,并保证在网络异步、参与者拜占庭故障下的安全性与活性。阅读本文后,你将掌握 DiemBFT 相对于 HotStuff 的三个核心创新(轮次同步机制、NIL 块投票、TCB 安全封装)、共识中的核心数据结构(Block、QuorumCert、Vote、TimeoutCertificate 等)及其校验规则,并能结合当前仓库 consensus/ 与 specifications/consensus/README.md 中的实现细节,理解协议从论文到代码的落地过程。

注:论文发布于 2020 年 4 月(为配合 Libra 白皮书 v2.0 更新),其描述的 Libra 协会已于 2020 年 12 月 1 日更名为 Diem 协会。项目实现的功能可能因监管审批等因素与论文存在差异,并会持续演进,阅读源码时请以当前仓库实际代码为准。

背景:为什么 Diem 需要 BFT 共识

状态机复制(State Machine Replication, SMR)是分布式系统中最经典的问题之一:一组副本必须对外部输入(在本场景中是交易)达成一致的顺序,并以相同的顺序执行,从而在每一个诚实节点上产生相同的状态。Diem 区块链的共识协议正是承担这一职责的组件——它在一组可配置的验证者之间,就交易的排序与最终确认达成一致。

DiemBFT 的安全属性包含两个维度:

  • 安全性(Safety):即使网络完全异步,且在任意一个配置纪元(epoch)中,有不超过阈值比例的参与者是拜占庭节点(恶意或有故障),协议仍然不会产生冲突的确认结果。
  • 活性(Liveness):在部分同步(partial synchrony)条件下,网络最终会恢复同步,协议能够持续取得进展。

从当前仓库的规范文档可以看到同样明确的表述(见 specifications/consensus/README.md):

DiemBFT 是一种 BFT 共识协议,在部分同步条件下保证活性与安全性,最多允许 f 个验证者出错(其中 f < 全部验证者数/3)。

从 HotStuff 到 DiemBFT:三个关键创新

DiemBFT 基于 HotStuff——一个借鉴了数十年 BFT 科学进展、面向互联网规模场景设计的现代 BFT 协议。HotStuff 采用"线性视图切换 + 聚合签名"的设计,把协议复杂度从传统 PBFT 的 O(n³) 通信降到 O(n),因此在广域网环境下具有更好的可扩展性。

DiemBFT 在 HotStuff 基础上引入三个重要改进:

  • 新颖的轮次同步机制(round synchronization):在同步(synchrony)条件下提供有界提交延迟(bounded commit latency)。验证者通过本地计时器与超时证书(TimeoutCertificate)主动推进轮次,而不是被动等待领导者提案。
  • NIL 块投票(nil-block vote):当验证者在某个轮次内没有收到提案时,它可以投票支持一个"NIL 块",从而即使领导者有故障,提案仍然能够被提交,避免活性停滞。
  • TCB 化("tcb"-able)模块:将参与者的正确行为封装进一个可信计算基(Trusted Computing Base)模块中,使其可以在安全硬件飞地中运行,缩小参与者的攻击面。这一设计在当前仓库的 consensus/safety-rules/ 目录中有完整实现(SafetyRules 即可独立部署运行)。
  • 此外,DiemBFT 支持在线重配置(reconfiguration):它把配置变更命令嵌入区块序列中,一个"纪元(epoch)"结束后,新纪元可能改变从验证者集合到协议本身的一切。

    核心数据结构与校验规则

    理解 DiemBFT 的起点是它的一套核心数据结构。以下定义与校验规则同时出现在 specifications/consensus/README.md 的 "Data structures" 章节,并在 consensus/consensus-types/ 中有对应实现。

    Block、BlockData 与 BlockType

    Block 在 block_data 基础上增加了唯一标识符与提案签名(见 consensus/consensus-types/src/block.rs):

    struct Block {
    id: HashValue,
    block_data: BlockData,
    signature: Option<Ed25519Signature>,
    }

    • id:块的唯一标识,由 block_data 的 BCS 序列化哈希计算得到,不参与序列化,反序列化时重新计算;
    • signature:若该块由验证者提案,则为提案者对 block_data.hash() 的 Ed25519 签名;创世块(genesis)与 NIL 块没有签名。

    BlockData 承载投票所需的全部信息(见 consensus/consensus-types/src/block_data.rs):

    struct BlockData {
    epoch: u64,
    round: u64,
    timestamp_usecs: u64,
    quorum_cert: QuorumCert,
    block_type: BlockType,
    }

    其中 timestamp_usecs 提供四条时间保证:时间单调递增;被确认块的时间戳 T 必须已被至少 f+1 个诚实验证者认为"已过去";携带 QC 的块不得在本地时钟未到达 T 时转发给其他节点;验证者不会签发"过于未来"的块(当前实现认为超过未来 5 分钟的块是恶意的)。

    BlockType 枚举了三种块(同样定义在 consensus/consensus-types/src/block_data.rs):

    pub enum BlockType {
    Proposal {
    payload: Vec<SignedTransaction>,
    author: AccountAddress,
    },
    NilBlock,
    Genesis,
    }

    • Proposal:由某个验证者按轮次与已提交历史提出的块,包含零个或多个交易与作者身份;
    • NilBlock:验证者在某轮超时且尚未投票时生成,无作者、无签名。若有 >2f 个验证者投票给 NIL 块,它可以进入 QuorumCert 链,缩小轮次差距、改善提交延迟;
    • Genesis:每个纪元第一个已提交块,由所有验证者根据上一纪元的 end-epoch LedgerInfo 本地确定性构造,作为该纪元 BlockTree 的根。

    VoteData、Vote 与 QuorumCert

    VoteData 记录被投票块及其父块信息,其 BCS 哈希用于 LedgerInfo 中的 consensus_data_hash:

    pub struct VoteData {
    proposed: BlockInfo,
    parent: BlockInfo,
    }

    Vote 是网络上传回的投票(见 consensus/consensus-types/src/vote.rs):

    struct Vote {
    vote_data: VoteData,
    author: AccountAddress,
    ledger_info: LedgerInfo,
    signature: Ed25519Signature,
    timeout_signature: Option<Ed25519Signature>,
    }

    投票者把 vote_data 的 BCS 哈希写入 ledger_info.consensus_data_hash,然后对 ledger_info 签名——这一设计使一张签名同时覆盖投票与提交两个意图。timeout_signature 可选,是对 (epoch, round) 超时结构的签名,聚合后可形成超时证书。在源码实现中,Vote 还支持 2-chain 超时(two_chain_timeout 字段,见 vote.rs),这是仓库当前实现相较规范文档的更细粒度扩展。

    QuorumCert(QC) 是同一块上 >2f 个投票的聚合,可作为推进验证者进入下一轮的证据(见 consensus/consensus-types/src/quorum_cert.rs):

    struct QuorumCert {
    vote_data: VoteData,
    signed_ledger_info: LedgerInfoWithSignatures,
    }

    派生的关键概念:

    • certified_block = vote_data.proposed(被认证的块);
    • parent_block = vote_data.parent;
    • ends_epoch:signed_ledger_info.ledger_info.commit_info.next_epoch_state 是否被设置。

    其核心校验包括:vote_data.hash() == signed_ledger_info.ledger_info.consensus_data_hash;每个签名用链上验证者公钥验签;且 len(signatures) >= 2/3 * |validators| + 1。创世 QC 特殊:certified_block 与 parent 相同、签名集合为空(创世块被隐式认可),且必须由 end-epoch LedgerInfo 确定性生成(对应源码 QuorumCert::certificate_for_genesis_from_ledger_info,见 quorum_cert.rs)。

    Timeout 与 TimeoutCertificate

    当验证者在某轮未收到提案时,会签发对 (epoch, round) 的超时声明:

    struct Timeout {
    epoch: u64,
    round: Round,
    }

    2f 个验证者的超时签名聚合为 TimeoutCertificate(TC),可作为推进所有验证者进入下一轮的证据。规范明确:同一轮同时存在 QC 与 TC 时,QC 优先(对应实现见 timeout_certificate.rs)。

    SyncInfo

    SyncInfo 携带可验证的证书,让落后节点能追赶到他人进度:

    pub struct SyncInfo {
    highest_quorum_cert: QuorumCert,
    highest_commit_cert: Option<QuorumCert>,
    highest_timeout_cert: Option<TimeoutCertificate>,
    }

    校验要求三个证书处于同一纪元、QC 的认证轮次高于 commit cert、且 commit cert 确实携带非空 commit。若收到更新的 SyncInfo,节点应通过 BlockRetrievalRequest、EpochRetrievalRequest 或状态同步追赶上(对应实现见 sync_info.rs)。

    网络消息与状态转移

    共识相关的所有网络消息都包裹在 ConsensusMsg 枚举中(规范见 specifications/consensus/README.md 的 "Network messages" 章节,实现见 consensus/src/network_interface.rs):

    enum ConsensusMsg {
    BlockRetrievalRequest(BlockRetrievalRequest),
    BlockRetrievalResponse(BlockRetrievalResponse),
    EpochRetrievalRequest(EpochRetrievalRequest),
    ProposalMsg(ProposalMsg),
    SyncInfo(SyncInfo),
    EpochChangeProof(EpochChangeProof),
    VoteMsg(VoteMsg),
    }

    关键消息与语义:

    • ProposalMsg:领导者向所有验证者广播的提案,含 proposal 块与 sync_info(证明该块应被投票);校验要求 proposal.block_data.quorum_cert == sync_info.highest_quorum_cert 且 proposal.round == highest_round + 1;
    • VoteMsg:投票消息,由两种事件触发——收到提案并执行后投票,或本地轮次超时(此时 vote.timeout_signature 被设置);校验要求 vote.vote_data.proposed.round == sync_info.highest_round + 1;
    • BlockRetrievalRequest/Response:节点缺失某块祖先时向对端发起的 RPC 拉取,按 block_id 起、沿父链回溯最多 num_blocks 个块;响应状态有 Succeeded、IdNotFound、NotEnoughBlocks;
    • EpochRetrievalRequest / EpochChangeProof:用于纪元变更证明的请求与响应,EpochChangeProof 是一串 epoch 连续递增的 LedgerInfoWithSignatures。

    三阶段处理流程(源码级)

    规范用伪代码描述了状态机处理各类消息的流程,仓库中对应实现在 consensus/src/round_manager.rs 与 consensus/src/epoch_manager.rs。

    处理 ProposalMsg:

    ensure_epoch(proposal.epoch())?;
    ensure_round(proposal.round(), sync_info)?;
    executed_block = block_store.execute_and_insert(proposal)?;
    if let Some(vote) = safety_rules.vote(executed_block) {
    round_state.save(vote);
    network.direct_send(next_proposer, VoteMsg { vote, sync_info });
    }

    处理 VoteMsg:先验签与检查纪元/轮次,再确认发送者是否为下一轮提案者(或是否为超时票);随后把投票存入 pending_votes;若同一 VoteData + LedgerInfo 上的投票达到 2f+1,就构建 QuorumCert 并 insert_quorum_cert;若未能成 QC 但超时票达到 2f+1,则构建 TimeoutCertificate 并插入;最后调用 update() 尝试进入新轮次。

    本地超时:计时器触发后,若无缓存投票则生成 NIL 块、执行并投票;若已有投票则附加 timeout_signature;随后广播 VoteMsg。这一机制正是 DiemBFT "NIL 块投票" 的运行时体现。

    共识状态机:RoundState 与 EpochState

    轮次状态与指数退避计时

    共识以 epoch 为单位运行:每个纪元有固定的、链上一致的验证者配置(验证者集合、VM 版本等),纪元内以递增的 round 推进。节点通过收到 r-1 轮的 QC 或 TC 进入第 r 轮。

    规范给出了 RoundState 的抽象与超时计算公式:

    let timeout = BASE_INTERVAL * EXPONENT_BASE ^ min(MAX_EXPONENT, max(0, round – committed_round – 3));

    建议取值:BASE_INTERVAL = 1 sec、EXPONENT_BASE = 1.2、MAX_EXPONENT = 6。即:距离上次提交越远,单轮等待时间越长(指数退避),从而在部分同步恢复后自动加大等待窗口、减少无效超时。

    该公式在仓库中有精确对应实现——ExponentialTimeInterval(见 consensus/src/liveness/round_state.rs):

    impl RoundTimeInterval for ExponentialTimeInterval {
    fn get_round_duration(&self, round_index_after_committed_qc: usize) -> Duration {
    let pow = round_index_after_committed_qc.min(self.max_exponent) as u32;
    let base_multiplier = self.exponent_base.powf(f64::from(pow));
    let duration_ms = ((self.base_ms as f64) * base_multiplier).ceil() as u64;
    Duration::from_millis(duration_ms)
    }
    }

    源码中的轮次索引规则与规范一致:创世块不遵循 3-chain 提交规则,因此 round 1 的索引为 0;current_round < highest_committed_round + 3 时索引为 0,否则为 current_round – highest_committed_round – 3(见 round_state.rs)。RoundState::process_certificates 在收到更新的 QC/TC 时切换轮次,并区分 NewRoundReason::QCReady 与 Timeout 两种进入新轮的原因(见 round_state.rs)。

    纪元状态与重配置

    EpochState 组合了该纪元的所有运行时组件:

    struct EpochState {
    epoch: u64,
    round_state: RoundState,
    block_store: BlockStore,
    proposer_election: ProposerElection,
    proposal_generator: ProposalGenerator,
    safety_rules: SafetyRules,
    }

    进入新纪元的唯一方式是收到来自 StateComputer 的重配置通知(携带非空 next_epoch_state 的 end-epoch LedgerInfo)。此时节点本地确定性生成新纪元的创世块(round 0)及它的 QC/commit cert,并把轮次状态重置到 round 1。创世块由上一纪元 end-epoch LedgerInfo 的确定性字段生成,因此所有节点构造出完全相同的创世块;网络本身则通过创世交易(genesis transaction,区别于创世块)引导启动。

    Safety Rules:两条投票规则与 3-chain 提交

    安全由独立的 SafetyRules 模块保证(实现位于 consensus/safety-rules/src/safety_rules.rs,可通过独立进程或安全硬件运行,对应论文中的 TCB 化设计)。

    持久化状态

    struct SafetyState {
    preferred_round: Round, // 收到的 QC 中 parent_block().round() 的最大值
    last_vote_round: Round, // 发出的投票中 proposed_block().round() 的最大值
    }

    初始值均为 0。源码中的 SafetyData 还额外记录了 one_chain_round 与 epoch(见 safety_data.rs),并随 QC 观察持续更新(observe_qc,见 safety_rules.rs)。

    两条投票规则

  • 规则一:只有当提案块轮次 > last_vote_round 时才能投票(防重复投票)。
  • 规则二:只有当提案块所扩展的 QC 轮次 >= preferred_round 时才能投票(防旧链投票破坏安全)。
  • 两条规则在源码中有直接对应:verify_and_update_last_vote_round(见 safety_rules.rs)与 verify_and_update_preferred_round(见 safety_rules.rs)。

    3-chain 提交规则

    对提案块 block 的投票,同时隐含着对祖父块 block.quorum_cert().parent_block() 的提交——只要存在 3 个连续轮次被认证:

    block.round() == block.quorum_cert().proposed_block().round() + 1
    == block.quorum_cert().parent_block().round() + 2

    源码 construct_ledger_info 中的实现(见 safety_rules.rs):

    let block2 = proposed_block.round();
    let block1 = proposed_block.quorum_cert().certified_block().round();
    let block0 = proposed_block.quorum_cert().parent_block().round();
    let commit = next_round(block0)? == block1 && next_round(block1)? == block2;
    let commit_info = if commit {
    proposed_block.quorum_cert().parent_block().clone()
    } else {
    BlockInfo::empty()
    };

    投票构造过程

    通过投票规则后,按如下方式构造投票:

    let vote_data = VoteData {
    proposed: block.block_info(), // 执行结果
    parent: block.quorum_cert().certified_block(),
    };
    let commit_info = if commit_rule() {
    block.quorum_cert().parent_block()
    } else {
    BlockInfo::empty()
    };
    let ledger_info = LedgerInfo {
    commit_info,
    consensus_data_hash: vote_data.hash(),
    };
    let signature = ed25519_sign(ledger_info.hash());

    注意这里"签在 ledger_info 上"而非直接签在块上,配合 consensus_data_hash 字段,一张签名同时认证了投票与(可能的)提交意图。对应实现见 vote.rs 的 Vote::new。

    提案者选举:LeaderReputation

    DiemBFT 的提案者选举基于已提交历史,倾向于成功的领导者,以提升交易吞吐量并降低提交延迟(实现见 consensus/src/liveness/leader_reputation.rs)。

    算法要点(来自规范):

    • 候选人 = 当前纪元所有验证者,按账户地址排序的向量;
    • 对轮次 r,取 target_round = min(max(r – 4, 0), latest_committed_round),向前拉取至多 window_size 个块;
    • 对每个候选人:若其是窗口中任一块的提案者、或任一块 QC 的投票者,赋予 active_weight,否则赋予 inactive_weight;
    • 取 sha3-256(round) 前 8 字节(小端)作为随机 u64,chosen = random % 总权重;
    • 从候选人向量头部开始累加权重,首个使累计权重 >= chosen 的候选人即本轮提案者。

    源码中 LeaderReputation::get_valid_proposer 实现了上述逻辑(见 leader_reputation.rs):target_round 的 round – 4 偏移、ActiveInactiveHeuristic 的活跃/不活跃权重分配、以及用 round.to_le_bytes() 驱动确定性随机选择。窗口元数据来自链上的 NewBlockEvent(DiemDBBackend 从数据库读取),使选举结果对每个验证者一致且可验证。

    Proposal Generator:提案与 NIL 块生成

    ProposalGenerator 负责两类生成(实现见 consensus/src/liveness/proposal_generator.rs):

    生成提案块(当本节点是提案者时):

    • 要求目标轮次高于 block store 中最高 QC(hqc)的轮次,且 hqc 不在纪元末尾;
    • 若 hqc.certified_block().has_reconfiguration():payload 为空,时间戳沿用 hqc 块的;否则从 mempool 拉取交易(排除已在 path_from_root(hqc) 中使用的待定交易),时间戳取当前 UNIX 微秒时间;
    • 构造 BlockData { epoch, round, timestamp, quorum_cert: hqc, block_type: Proposal { payload, author } },并用共识私钥对 BCS 序列化哈希签名。

    生成 NIL 块(当本轮超时且未投票时):

    • 构造 BlockData { epoch, round, timestamp: hqc 的时间戳, quorum_cert: hqc, block_type: NilBlock },不带签名。

    NIL 块复用提案路径进行执行与投票,但因为没有作者与签名,天然避免了伪造领导者的问题——这正是论文所述"提案在领导者故障下仍可提交"的实现基础。

    抽象模块与状态转移全景

    规范还定义了共识依赖的五类抽象模块,帮助读者把协议与代码模块对应起来:

    抽象模块职责仓库对应实现
    Network 向对端验证者收发消息(direct_send / rpc / broadcast) consensus/src/network.rs
    StateComputer 执行交易、持久化账本状态、提交与同步 consensus/src/state_computer.rs
    PersistentStorage 持久化共识内部数据 consensus/src/persistent_liveness_storage.rs
    OnChainConfig 从账本状态读取链上配置(验证者集合等) types/src/on_chain_config/
    BlockStore 内存中的块树存储 consensus/src/block_storage/block_store.rs

    其中 BlockStore 是协议的"内存视图":块以树状组织,根为最新已提交块。它实现 execute_and_insert(执行并插入新块)、insert_single_quorum_cert、insert_timeout_certificate、commit(沿根到目标块的路径提交并剪枝)以及 sync_to_highest_commit_cert(通过 BlockRetrievalRequest {num_blocks: 3} 追赶并重建块树)等操作。

    完整的消息驱动状态转移可归纳为三类事件(规范 "Running Consensus" 章节):

  • Reconfiguration(来自 StateComputer):切换到新纪元,生成创世块,重置轮次;
  • ConsensusMsg(来自网络):ensure_epoch → ensure_round → 处理具体消息(提案、投票、同步、纪元证明、块拉取);
  • Timeout(来自本地计时器):生成/复用投票、附加超时签名、广播 VoteMsg。
  • ensure_epoch 与 ensure_round 两个辅助函数负责纪元/轮次对齐:本节点落后时向发送方拉取证据,超前时把证据回发给发送方,确保任何消息都只在正确的纪元与轮次上下文中被处理。

    总结与延伸阅读

    DiemBFT 以 HotStuff 为基石,通过有界延迟的轮次同步、NIL 块投票与 TCB 化安全模块三个创新,把 BFT 共识推进到了适合互联网规模部署的形态;同时借助内嵌于区块序列的重配置机制,让验证者集合乃至协议本身都能随链上治理平滑演进。它的安全性建立在两条投票规则与 3-chain 提交规则之上,活性则依赖指数退避的超时机制与 QC/TC 双通道的轮次推进。

    若想深入实践,建议继续阅读以下仓库资料:

    • 协议规范全文:specifications/consensus/README.md
    • 共识数据结构实现:consensus/consensus-types/
    • 共识主循环与状态机:consensus/src/round_manager.rs、consensus/src/epoch_manager.rs
    • 安全投票模块(TCB 设计):consensus/safety-rules/
    • 块树存储:consensus/src/block_storage/block_store.rs
    • 选举与提案:consensus/src/liveness/leader_reputation.rs、consensus/src/liveness/proposal_generator.rs

    【免费下载链接】diem Diem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world. 【免费下载链接】diem 项目地址: https://gitcode.com/gh_mirrors/di/diem

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:171主机测评 » Diem 区块链中的状态机复制:DiemBFT 共识算法核心解析与源码实现
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址