BFT 共识:从 PBFT、Tendermint 到 HotStuff 家族

本文是 dex_qa.md 第 2 章的独立版本,内容相同,按"理论基础、经典协议、HotStuff 家族演化、乐观路径、数据传播与 DAG、对比与工程"组织。文中"设计文档"指 Crustle-DEX交易引擎设计.md。涉及协议参数与性能的数字来自论文与公开资料,未在本项目环境复现。


BFT(拜占庭容错)共识是高性能链的核心。本文按"理论基础、经典协议、HotStuff 家族演化、乐观路径、数据传播与 DAG、对比与工程"的顺序组织:

部分 问题
理论基础 第 1 节系统模型;第 2 节为什么 n ≥ 3f+1
经典协议 第 3 节 PBFT;第 4 节 Tendermint;第 5 节 Malachite;第 6 节 HotStuff 与两链、三链规则;第 7 节 HotStuff 什么情况下换 leader;第 8 节 PBFT、Tendermint 与 HotStuff 的区别与选型
HotStuff 家族演化 第 9 节演化路线与起点;第 10 节 Fast HotStuff;第 11 节 Jolteon 与 Ditto;第 12 节 HotStuff-2 与 HotStuff-1;第 13 节 MonadBFT 与尾部分叉;第 14 节演化总表与规律
乐观路径 第 15 节 Optimistic BFT,以及 HotStuff 家族为什么不属于它
数据传播与 DAG 第 16 节 leader 带宽瓶颈、Narwhal 与 Quorum Store 的区别;第 17 节 DAG 类共识为什么提出、怎么排序(含例子)
对比与工程 第 18 节各协议对比总表;第 19 节如何做到 200 ms 以内;第 20 节从零实现一个 BFT 共识

1. BFT 共识的系统模型是什么?安全性、活性、响应性指什么?

讨论任何 BFT 协议,先说清三件事:节点会怎么出错、网络有多可靠、要保证什么。

故障模型:

类型 行为 需要的节点数
崩溃故障 节点只会停机,不会撒谎 n ≥ 2f+1(Raft、Paxos)
拜占庭故障 节点可以任意行为:撒谎、双签、选择性发消息 n ≥ 3f+1(第 2 节)

网络模型:

模型 假设 代表协议
同步 消息延迟有已知上界 Δ 理论上最强,现实网络难以保证
异步 消息最终会到,但延迟没有上界 HoneyBadger BFT、Tusk、Ditto 的回退路径
部分同步 存在一个未知的全局稳定时间 GST,之后延迟不超过 Δ PBFT、Tendermint、HotStuff 家族

FLP 不可能性(1985 年): 在异步网络里,只要可能有一个节点崩溃,任何确定性协议都无法同时保证总能达成一致(活性)与不出错(安全性)。实用协议的应对:

  • 部分同步协议保证安全性任何时候都成立,活性只在 GST 之后成立:网络差时可能停下来,但不会提交冲突的块。
  • 异步协议用随机数(公共随机币)绕开 FLP,以概率 1 最终达成一致。

三个性质:

性质 含义
安全性 诚实节点永远不会提交冲突的块;一旦提交就不可回滚(确定性最终性)
活性 诚实客户端的交易最终会被提交
乐观响应性 GST 之后且 leader 诚实时,推进速度只取决于实际网络延迟 δ,而不必等待保守的上界 Δ

衡量协议的三个维度:

  • 轮数(消息延迟):提交一个块要经过几次单向消息,决定延迟下限。
  • 通信复杂度:每个块全网要发多少条消息,O(n) 还是 O(n²)。
  • 认证复杂度:每个节点要验证多少个签名;聚合签名可以把 2f+1 个签名压成一个。

本文后面的协议对比(第 18 节)都按这几个维度展开。

2. 为什么 BFT 要求 n ≥ 3f+1,法定人数是 2f+1?

要让任意两个法定人数至少交在一个诚实节点上。

  • 最多 f 个节点作恶,而且可能有 f 个诚实节点消息迟到,所以只能等 n − f 个回复。
  • 两个大小为 n − f 的集合至少交于 n − 2f 个节点;要保证交集里至少有一个诚实节点,需要 n − 2f > f,即 n ≥ 3f+1。
  • n = 3f+1 时法定人数 n − f = 2f+1。例如 21 个验证者最多容忍 6 个拜占庭节点,法定人数 15。
  • 实际实现按投票权而不是人数计算:法定人数是总投票权的三分之二以上。

3. PBFT 的流程是怎样的?

PBFT(Castro 与 Liskov,1999)是第一个实用的拜占庭容错状态机复制协议,正常情况下三个阶段:pre-prepare、prepare、commit。

sequenceDiagram
  participant C as 客户端
  participant P as 主节点
  participant R as 副本
  C->>P: 请求
  P->>R: pre-prepare(视图 v, 序号 n, 请求摘要)
  R->>R: prepare 全员广播
  Note over R: 收到 2f 个一致的 prepare,加上 pre-prepare 共 2f+1:prepared
  R->>R: commit 全员广播
  Note over R: 收到 2f+1 个 commit:committed,执行
  R-->>C: 回复;客户端收到 f+1 个相同结果即确认
  • pre-prepare 由主节点给请求分配序号;prepare 让所有诚实副本对"视图 v 里序号 n 就是这个请求"达成一致;commit 保证这个一致在换主节点之后依然成立。
  • 通信量:prepare 与 commit 都是全员广播,正常路径 O(n²);视图切换要带上每个副本的 prepared 证书,朴素实现 O(n³)。
  • 检查点与回收:每隔若干序号做一次检查点,2f+1 个副本签名后即可删除更早的日志。
  • 不足:主节点固定到被怀疑才换,性能依赖主节点;平方级消息让它难以扩展到上百个节点;每个请求单独走三阶段,没有链式流水线。

4. Tendermint 的流程是怎样的?

Tendermint(现名 CometBFT)以"高度"为单位逐块共识,每个高度内按"轮"推进,每轮三步:propose、prevote、precommit。

flowchart LR
  P[propose:本轮提议者广播区块] --> PV[prevote:验证者对区块或空投票]
  PV --> PC{收到 2/3 以上的 prevote?}
  PC -- 是,形成 polka,锁定该块 --> PCV[precommit]
  PC -- 超时 --> PCN[precommit 空]
  PCV --> CM{收到 2/3 以上的 precommit?}
  CM -- 是 --> COMMIT[提交,进入下一高度]
  CM -- 否或超时 --> NR[进入下一轮,换提议者]
  PCN --> NR
  NR --> P
  • 锁机制:验证者对某块 precommit 后就锁定它,之后只在看到更高轮次对另一块的 2/3 prevote 时才解锁,这是安全性的关键。
  • 即时最终性:块一旦提交不可回滚,没有分叉,这是它适合应用链(Cosmos 生态、dYdX v4)的原因。
  • 延迟:正常情况一个块三次消息延迟;但每个高度要完整走完两轮投票才开始下一个高度,没有流水线,常见配置下出块在秒级。
  • 通信量:投票全员 gossip,O(n²)。
  • 活性:依赖超时,超时参数要按最坏网络延迟设置;新轮次要等超时才能推进,不具备乐观响应性(只要网络好,推进速度只取决于实际延迟)。

5. Malachite 是什么?和 Tendermint 什么关系?

Malachite 是 Rust 写的 BFT 共识引擎,核心是 Tendermint 算法的实现。它原由 Informal Systems 开发,2025 年随团队一起被 Circle 收购,现在是 Circle 的 Arc 链的共识。

  • 算法:Tendermint 的 propose、prevote、precommit 三步与锁机制(第 4 节),即时最终性。
  • 动机:解决 CometBFT(Tendermint 的 Go 实现)里共识与网络、mempool、执行耦合过紧的技术债,把共识做成可嵌入的库。
  • 分层:
层 内容
核心共识库 纯算法:状态机、投票统计、驱动器,不做任何 I/O
共识引擎 网络(点对点)、预写日志、同步、配置、指标,把核心库跑起来
应用接口 应用负责产生要共识的"值"(通常是区块)、执行与提交;Malachite 只负责对值达成一致
  • 形式化方法:部分实现与 Quint 形式化规约一起设计,并用基于模型的测试检查实现是否符合规约。
  • 公开数据:项目 README 称早期实验在 100 个验证者、1 MB 区块下平均最终确认约 780 ms,最多每秒 2.5 个块或 13.5 MB 数据,约 5 万笔交易。Arc 的公开资料称最终性低于 500 ms。均未在本项目环境复现。
  • 状态:README 自述为 alpha 阶段、尚未外部审计。
  • 典型组合:Malachite 作共识客户端,Reth 作执行客户端,两者之间用以太坊的 Engine API 衔接(dex_qa.md 12.10 节)。

6. HotStuff 是什么?两链与三链规则有什么区别?

HotStuff 是 leader 制的 BFT 协议,特点是线性通信(投票发给 leader)和链式流水线:每个块的投票同时推进前面块的提交。

原版 HotStuff 的设计要点(2018 年提出,2019 年发表于 PODC):

改进 做法
线性通信 投票只发给 leader;leader 用门限签名把 2f+1 票合成一个 QC 再广播。正常路径与换主都是 O(n)
乐观响应 新 leader 收到 n − f 个新视图消息就能推进,速度只取决于实际延迟
三阶段 prepare、pre-commit、commit 三次 QC 后 decide。多出一阶段,是为了让换主也保持线性:新 leader 只需带上最高 QC,不必收集并转发所有人的锁
链式流水线 每个块的 QC 同时充当前面块下一阶段的投票,三阶段摊到三个连续块上;每轮一个块
pacemaker 分离 活性(何时换轮、怎么同步轮次)与安全性(投什么票)分成独立模块

代价:提交一个块约 7 次消息延迟,两链为什么能少一轮、HotStuff 为什么不用两链,见第 11 节。

两链与三链规则:

  • 三链规则(原版 HotStuff):块 B 之后连续出现三个直接相连且都有 QC 的块,B 才提交,正常情况下需要三轮投票。
  • 两链规则(Jolteon,Aptos 现用的 AptosBFT):两个直接相连的已认证块即可提交,少一轮延迟;代价是换 leader 时要多带一个超时证书来证明安全,视图切换的通信量是平方级(第 11 节)。
  • Aptos 在此基础上加了顺序票:节点本地聚合出 QC 后广播顺序票,2f+1 张顺序票就确定顺序,再少等一轮。详见设计文档 3.8 节。

7. HotStuff 什么情况下会换 leader?

两种情况。一是正常轮换:每一轮按确定性规则换一个 leader,不需要额外消息,这就是正常出块路径本身。二是超时换主:本轮在超时时间内没有推进,节点广播超时消息,凑够 2f+1 条组成超时证书后进入下一轮,由下一轮的 leader 接手。

正常轮换。 链式 HotStuff 里,投票发给下一轮 leader,由它聚合 QC 并提出下一个块,所以每出一个块就换一次 leader。原始论文也允许"稳定 leader",出错时才换,类似 PBFT;LibraBFT、Aptos 等生产实现都是每轮轮换。下一轮由谁当 leader,所有节点按同一规则各自算出,结果一致:

方式 做法
轮流 按验证者列表或投票权依次轮换
按信誉 按最近一段历史里的出块成功率与投票参与率加权选择,近期出块失败的节点很少被选中

Aptos 的链上共识配置默认按信誉选举(types/src/on_chain_config/consensus_config.rs 的 LeaderReputation(ProposerAndVoterV2));超时轮次的 leader 会记进下一个块元数据的 failed_proposer_indices,作为信誉计算的输入。

超时换主的触发条件。 Aptos 在超时消息里记录超时原因(consensus/consensus-types/src/round_timeout.rs 的 RoundTimeoutReason,判断逻辑在 round_manager.rs 的 compute_timeout_reason):

情况 记录的原因 常见根因
没收到本轮提议 ProposalNotReceived leader 宕机、重启、网络断开,或者落后正在同步
收到提议,但批次数据取不齐 PayloadUnavailable 批次作者的数据没有传到,拉取超时
投了票,但没有形成 QC NoQC 票没汇齐:部分节点离线、网络分区,或者负责聚合的节点不聚合
收到提议且数据齐全,但没有投票 Unknown 提议没有通过安全规则校验,例如没有接在安全的 QC 之后

超时换主的流程(Jolteon 与 Aptos):

flowchart TD
  S[进入第 r 轮,启动本地计时器] --> Q{计时器到期前收到第 r 轮的 QC,<br/>或更高轮次的证书?}
  Q -- 是 --> N[进入第 r+1 轮,由第 r+1 轮的 leader 提议]
  Q -- 否 --> T[广播超时消息:轮次 r,加上自己见过的最高 QC]
  T --> TC{收齐 2f+1 条超时消息?}
  TC -- 是 --> C[组成超时证书 TC,进入第 r+1 轮]
  C --> P[第 r+1 轮的 leader 带上 TC 提议,<br/>必须接在 TC 里最高的 QC 之后]
  TC -- 否 --> W[继续等待,收到别人转发的 QC 或 TC 也能直接进入新轮次]
  • 轮次从哪来。 第 r 轮在两种情况下开始:收到第 r − 1 轮的 QC,或者收到第 r − 1 轮的超时证书。落后的节点从别人的消息里拿到更高轮次的证书,就直接跳到那一轮。
  • 超时时长。 Aptos 的轮次超时是"初始值 × 底数的 k 次方",k 是距离上一次成功排序的轮数,有上限(consensus/src/liveness/round_state.rs 的 ExponentialTimeInterval)。默认初始值 500 ms、底数 1.2、上限 10(config/src/config/consensus_config.rs,实现细节,可能随版本变化),连续失败时超时逐渐拉长,避免网络异常时轮次空转。
  • 与原版 HotStuff 的区别。 原版 HotStuff 超时后只把带最高 QC 的新视图消息发给下一轮 leader,换主 O(n);Jolteon 与 Aptos 把超时消息广播给所有节点,换主 O(n²),换来两链提交(第 11 节)。

其他会换 leader 的情况:

  • epoch 切换:验证者集合或共识配置变更后,按新的集合重新计算 leader 顺序。
  • 信誉降权:连续出块失败的节点,在信誉窗口内很少再被选中;它不必被"罢免",只是轮不到。

换主的代价主要是等超时:至少一个超时时长,再加一轮超时消息和一次提议。所以压低换主代价的手段是调小超时(Crustle 部署假设下把初始轮次超时从 500 ms 调到约 50 ms,见设计文档 3.8 节)、按信誉避开慢节点,以及让一次 leader 故障只损失一轮(见第 13 节的 MonadBFT)。

8. PBFT、Tendermint 与 HotStuff 有什么区别?各自优劣如何?

三者都工作在部分同步网络下,都要求 n ≥ 3f+1、用 2f+1 的法定人数,都有确定性最终性。区别在于 leader 怎么换、票怎么收、块怎么排。

维度 PBFT(1999) Tendermint(2014 起) HotStuff(2018)
设计目标 许可环境下的状态机复制 公链、应用链上的逐块共识 大规模验证者下的线性通信
leader 固定主节点,被怀疑作恶才换 每轮轮换提议者 每轮轮换 leader
正常路径阶段 pre-prepare、prepare、commit propose、prevote、precommit prepare、pre-commit、commit,再 decide
投票怎么收 全员广播,每个节点自己统计 全员 gossip,每个节点自己统计 只发给 leader,由它聚合成一个 QC 再广播
正常路径通信 O(n²) O(n²) O(n)
换主通信 O(n³)(朴素实现要转发各节点的证书) O(n²) O(n)
提交延迟(消息延迟 δ 的量级) 约 3δ 约 3δ,但每个高度都要完整走完 约 7δ(链式版本)
流水线 无,可并发处理多个序号 无,一个高度提交完才开始下一个 有,每个块的 QC 同时推进前面的块
乐观响应性 正常路径有;换主依赖超时 没有,新一轮要等超时 有,新 leader 收齐 n − f 条新视图消息即可推进
分叉 无分叉,提交即最终 无分叉,提交即最终 未提交的链尾可能被分叉掉,提交后即最终
签名要求 普通签名或消息认证码 普通签名 门限签名或聚合签名,把 2f+1 票压成一个 QC

PBFT

  • 优势:协议最成熟、证明最完整;正常路径约 3δ,延迟最低;固定主节点时吞吐稳定,适合节点少的许可联盟链。
  • 代价:平方级消息,实践中通常只到几十个节点;换主要带上所有节点的证书,朴素实现 O(n³),是最复杂、最容易写错的部分;主节点固定,恶意主节点可以刻意放慢速度而不被判定为故障。

Tendermint

  • 优势:以高度为单位、每轮三步,锁机制清楚,容易理解和实现;提交即最终、没有分叉,对应用链和跨链桥友好;轮换提议者,天然公平;生态成熟,CometBFT 是 Cosmos 生态的标准,Rust 实现 Malachite 用于 Arc(第 5 节)。
  • 代价:仍是平方级 gossip;不具备乐观响应性,超时要按最坏网络设置,出问题时恢复慢;没有流水线,每块都要走完两轮投票,常见配置下出块在秒级。

HotStuff

  • 优势:正常路径与换主都是 O(n),能扩到上百个验证者;乐观响应;链式流水线,每轮都能出一个块;换主与正常路径走同一套流程,实现与证明比 PBFT 简单;后续演化最活跃(第 9 节)。
  • 代价:基本版本提交约 7δ,为了换主线性多付了一整轮;所有投票汇到 leader,leader 是带宽与验签热点;流水线带来分叉攻击与尾部分叉两类新攻击面(第 10 节、第 13 节);依赖 BLS 一类聚合签名,计算与实现更复杂。

为什么现在主流是 HotStuff 的两链变体。 HotStuff 最大的短板是 7δ 的提交延迟。Jolteon 用平方级换主换来两链提交,把延迟降到约 5δ(原因与例子见第 11 节),再加上顺序票、乐观提议与推测最终性,定序延迟回到约 3δ 的量级,同时保留线性的正常路径与流水线。AptosBFT、Plasma 的 Fast HotStuff、MonadBFT 都属于这一支。

选型:

场景 选择 理由
十个左右节点的许可联盟链 PBFT 或其变种 平方通信可以接受,延迟最低,协议最成熟
应用链,需要即时最终性、成熟工具链、实现简单 Tendermint(CometBFT、Malachite) 没有分叉,生态成熟
验证者多,要高吞吐与低延迟 HotStuff 两链变体(Jolteon、Fast HotStuff、MonadBFT) 线性正常路径加流水线,定序接近 3δ
同机房的少量验证者(如 Crustle 的 21 个) 三者都可行;HotStuff 系的收益主要来自流水线与乐观提议 δ 很小,平方通信的代价被压缩,瓶颈转到签名、投票持久化与执行

协议优劣要结合部署前提来看:节点少、网络快时,O(n²) 与 O(n) 的差别不再决定性能,决定延迟的是轮数、签名聚合的计算量与投票落盘的耗时。

9. HotStuff 系列是如何演化的?

主线是三件事交替推进:通信量从平方降到线性,提交轮数从三轮降到两轮再到一轮推测,以及修补流水线带来的新问题(分叉、尾部分叉、leader 作恶)。数据传播与执行也逐步从共识里拆了出去。 本节给出演化路线和起点,各代协议的细节在第 10 节到第 13 节,总表与规律在第 14 节。

flowchart TD
  PBFT["PBFT 1999<br/>三阶段,O(n²),换主 O(n³),固定主节点"] --> TM["Tendermint / Casper FFG<br/>轮换提议者,锁机制,非乐观响应"]
  PBFT --> HS["HotStuff 2018<br/>三阶段,线性通信,乐观响应,链式流水线"]
  TM --> HS
  HS --> LB["LibraBFT / DiemBFT v1–v3<br/>工程化:pacemaker、安全规则模块、epoch 切换"]
  HS --> FHS["Fast HotStuff 2020<br/>两链提交,AggQC 防分叉"]
  LB --> JOL["Jolteon 2021 = DiemBFT v4<br/>两链提交,换主 O(n²),leader 信誉"]
  JOL --> DIT["Ditto 2021<br/>异步网络下回退到异步协议"]
  JOL --> APT["AptosBFT<br/>Quorum Store、顺序票、乐观提议,演进到 Raptr"]
  HS --> HS2["HotStuff-2 2023<br/>两阶段 + 乐观响应 + 乐观线性"]
  HS2 --> HS1["HotStuff-1 2024<br/>一阶段推测,slotting"]
  FHS --> MON["MonadBFT 2025<br/>抗尾部分叉,一轮推测最终"]
  JOL --> MON

9.1 起点:PBFT 的三个痛点

  1. 平方级通信:prepare、commit 都是全员广播,O(n²);换主要带上 prepared 证书,朴素实现 O(n³)。
  2. 固定主节点:主节点只在被怀疑时才换,性能与公平都依赖一个节点。
  3. 没有流水线:每个请求独立走三阶段。

Tendermint 与以太坊的 Casper FFG 引入了轮换提议者与锁的思想,但仍是全员投票、平方通信,且换轮要等超时,不具备乐观响应性(第 4 节)。

HotStuff 用线性通信、乐观响应与链式流水线回应这三点(第 6 节)。

9.2 LibraBFT 与 DiemBFT v1 到 v3(2019 到 2020 年)

Facebook 的 Libra(后改名 Diem)把 HotStuff 做成生产协议,仍是三链提交,主要是工程化:

  • pacemaker:超时后广播超时消息,2f+1 个组成超时证书(TC),据此进入下一轮。
  • 安全规则独立:记录最后投票的轮次与锁定轮次,投票前落盘;可以跑在单独进程或安全硬件里(第 20 节)。
  • epoch 切换:验证者集合变更通过特殊区块完成,新旧集合之间安全交接。
  • leader 选举:仍以轮换为主;按信誉选择 leader 是在 v4(Jolteon)中引入的(第 11 节)。

10. Fast HotStuff 是什么?解决了 HotStuff 的什么问题?

Fast HotStuff(Jalalzai、Niu、Feng,2020 年提出)是两链版的 HotStuff:正常路径两轮投票即可提交,提交约 5δ;换 leader 时,新 leader 必须附上 n − f 个节点的最高 QC 聚合成的 AggQC,证明自己的提议接在多数节点见过的最新块之后,从而消除分叉攻击。

正常路径(与链式 HotStuff 相同的流水线):

sequenceDiagram
  participant L1 as leader(v 轮)
  participant R as 副本
  participant L2 as leader(v+1 轮)
  L1->>R: 提议块 B_v,携带父块的 QC
  R->>L2: 对 B_v 投票(签名)
  L2->>L2: 聚合 n−f 票得到 QC(B_v)
  L2->>R: 提议块 B_v+1,携带 QC(B_v)
  Note over R: B_v 有了 QC,且 B_v+1 直接接在 B_v 之后
  R->>R: 当 B_v+1 也拿到 QC 时,B_v 满足两链规则,提交
  • 两链提交规则:块 B 有 QC,它的直接子块也有 QC,B 就提交。比基本 HotStuff 的三链少一轮。
  • 正常路径线性:投票只发给下一轮 leader,leader 聚合成一个 QC 广播,O(n)。

分叉攻击与 AggQC。 在两链规则下,如果新 leader 故意拿一个较旧的 QC 当父块来提议,就会把最近那个已认证、但尚未提交的块分叉掉。Fast HotStuff 的规则是:

  1. 超时后,每个副本把自己见过的最高 QC 签名发给新 leader。
  2. 新 leader 收齐 n − f 个,把它们聚合成 AggQC,放进提议里。
  3. 副本校验 AggQC,要求提议的父块就是 AggQC 里最高的那个 QC。

这样 leader 无法用旧 QC 替换多数节点已经见过的新 QC,分叉攻击做不成。代价是换 leader 时有 O(n²) 条消息,但每个副本只需验证两个聚合签名(AggQC 与其中最高的 QC),计算量比逐个验签低一个数量级。

和 Jolteon 的关系。 两者都是两链 HotStuff,思路接近:Jolteon 用超时证书证明安全,Fast HotStuff 用 AggQC 证明提议的父块是最新的。Plasma 的 PlasmaBFT 是流水线化的 Fast HotStuff,用 Rust 实现。

仍然存在的问题:尾部分叉。 Fast HotStuff 的超时证书能证明"上一个 leader 错过了自己的轮次",下一个 leader 可以合法地跳过前一个 leader 刚提议、还没拿到 QC 的块。恶意 leader 可以借此丢掉前一个块,抢走其中的 MEV。MonadBFT 专门解决了这一点(第 13 节、13.1 节)。

11. Jolteon 与 Ditto 是什么?为什么用平方级换主换两链提交?

Jolteon(2021 年提出,部署为 DiemBFT v4)保持正常路径线性,把换 leader 的通信改为 O(n²),换来两链提交,提交延迟从约 7δ 降到约 5δ。 理解这笔交易需要两个概念:

  • QC(法定人数证书):某个块收到 2f+1 张票后聚合成的证书,表示多数节点认可了这个块。
  • 锁:节点认为某个块可能已经被别人提交,就锁在它上面,之后只给延伸这个块的提议投票,防止在它旁边另起分叉。锁得早提交快,但换 leader 时容易出问题;锁得晚更安全,但要多等一轮。

延迟怎么数。 每一轮是"leader 广播提议"加"节点把票发给下一个 leader",共 2 次单向消息;每个新提议都带上一个块的 QC。

sequenceDiagram
  participant L as 各轮 leader
  participant V as 验证者
  L->>V: ① 提议块 B
  V->>L: ② 对 B 投票
  L->>V: ③ 提议 B1,带 QC(B)
  V->>L: ④ 对 B1 投票
  L->>V: ⑤ 提议 B2,带 QC(B1)
  Note over V: 两链规则:B 有 QC,直接子块 B1 也有 QC,此刻提交 B,共 5 次消息
  V->>L: ⑥ 对 B2 投票
  L->>V: ⑦ 提议 B3,带 QC(B2)
  Note over V: 三链规则:还要 B2 也有 QC,此刻才提交 B,共 7 次消息

少一代后代就少 2δ,而且每个块都省。HotStuff 不直接用两链,是因为两链加上线性换主会遇到隐藏锁。

线性换主怎么做。 超时后,每个节点只给新 leader 发一条新视图消息,里面是自己见过的最高 QC;新 leader 收齐 n − f 条就从中选最高的 QC 接着提议,提议里只带这一个 QC,全程 O(n) 条消息。新 leader 收齐 n − f 条就动手、不再多等,这是乐观响应性的来源;代价是它收到的 n − f 条里可能恰好没有某个诚实节点的那条。

隐藏锁的例子。 4 个验证者 A、B、C、D,f = 1,法定人数 3 张票,其中 A 是拜占庭节点。两链协议里,节点看到某块的 QC 就锁在它上面。

sequenceDiagram
  participant A as A(本轮 leader,拜占庭)
  participant B as B(下一轮 leader)
  participant C as C
  participant D as D
  A->>B: 提议块 X
  A->>C: 提议块 X
  A->>D: 提议块 X
  B-->>A: 对 X 投票
  C-->>A: 对 X 投票
  D-->>A: 对 X 投票
  A->>A: 聚合出 QC(X)
  A->>C: 下一个提议带着 QC(X),只发给 C
  Note over C: C 看到 QC(X),锁在 X 上
  Note over A,D: 本轮超时,换 B 当 leader
  A->>B: 新视图消息:谎称最高 QC 是 X 的父块
  D->>B: 新视图消息:最高 QC 是 X 的父块
  Note over B: 加上自己已收齐 3 条(n − f),不再等 C
  B->>C: 提议 Y,接在 X 的父块后面,与 X 冲突
  B->>D: 同上
  C-->>B: C 的新视图消息(带 QC(X))这时才到
  Note over C: C 锁在 X 上,拒绝给 Y 投票
  Note over A: A 也不投票
  Note over B,D: 只有 B、D 两张票,凑不够 3 张,这一轮失败

C 锁住了 X,新 leader B 却不知道,这就是隐藏锁。C 拒投是对的:Y 的提议里只有 X 父块的 QC,C 无从判断 B 是没收到 QC(X),还是故意藏起了它;如果 X 已经被别的节点提交,给 Y 投票就会造成分叉。所以隐藏锁不会破坏安全性,破坏的是活性。

"卡住活性"是什么意思。 不是永久停链,也不是分叉,而是诚实 leader 也可能出不了块:

  • 这一轮失败后超时,换下一个 leader。轮到知道 QC(X) 的节点当 leader(例子里是 C),或者 QC(X) 经由其他消息传到了 leader 手里,链就恢复。
  • 但协议给不出"哪一轮一定恢复"的保证。拜占庭节点每次当 leader 都可以重新制造一个隐藏锁(把新的 QC 只给一个诚实节点),再利用消息先后顺序让后面诚实 leader 的轮次失败。每失败一轮就白等一个超时,这段时间吞吐为零;能拖多久取决于拜占庭节点的数量和 leader 的轮换方式。
  • BFT 协议对活性的要求是:GST 之后,只要轮到诚实 leader,就一定能出块。两链加线性换主达不到这个要求,所以说它没有活性保证,而不是"一定会卡死"。

三种修法:

协议 修法 代价
Tendermint 新一轮开始前等一个足够长的超时,保证能听到所有诚实节点的锁 每次换轮都要等超时,失去乐观响应性
HotStuff 加一个阶段,让锁晚一轮落下 每个块多等 2δ,提交从 5δ 变成 7δ
Jolteon 保持两链;超时消息广播给所有节点,2f+1 条组成超时证书,证书本身就是"可以安全解锁"的证明 只在换 leader 时多一些消息,从 O(n) 变成 O(n²)
  • HotStuff 为什么加一阶段就够。 三链里,C 只拿到 QC(X) 时还不算锁,要等 X 的子块也有 QC 才锁。例子里 C 没锁,会给 Y 投票,B、C、D 凑够 3 票,链继续前进。一般地,多出来的阶段保证:任何诚实节点真正锁住一个块之前,已经有 2f+1 个节点见过相应的 QC,新 leader 从任意 n − f 个节点收集消息都能拿到它,所以提议里只需带一个最高 QC。这一轮等待加在了每一个块上,不管有没有换 leader。
  • Jolteon 怎么做。 超时后,每个节点把超时消息广播给所有人,里面签上自己最高 QC 的轮次;凑够 2f+1 条组成超时证书 TC。新 leader 的提议必须带上 TC,并接在轮次不低于 TC 里最高 QC 的块之后。节点投票时检查的是这个条件,而不是拿自己的锁去拒绝(Aptos 的实现见 consensus/safety-rules/src/safety_rules_2chain.rs 的 safe_to_vote)。
flowchart TD
  T[B 的这一轮超时] --> BC[每个节点广播超时消息<br/>签上自己最高 QC 的轮次]
  BC --> TC[凑够 3 条组成超时证书 TC]
  TC --> Q{TC 里有 C 的那条吗?}
  Q -- 有 --> H1["TC 里最高的是 QC(X)<br/>新 leader 必须接在 X 后面提议"]
  H1 --> OK1[C 的锁得到满足,B、C、D 投票,链继续前进]
  Q -- 没有,3 条来自 A、B、D --> H2["TC 里最高的是 X 父块的 QC<br/>新 leader 接在 X 的父块后面提议,附上 TC"]
  H2 --> OK2["C 看到 TC,确认 X 不可能已被提交,放心投票<br/>B、C、D 投票,链继续前进,X 被放弃"]
  • 为什么 TC 足以让 C 放心。 X 要被提交,X 的子块必须拿到 QC,也就是 2f+1 个节点都见过 QC(X),它们的最高 QC 轮次都不低于 X。TC 同样由 2f+1 个节点签名,两组至少交于 f+1 个节点,其中至少一个诚实节点会如实签上不低于 X 的轮次。所以只要 TC 里最高的轮次低于 X,X 就一定没有被提交,放弃它不会造成分叉。
  • 线性换主为什么做不到。 线性换主里新 leader 只转发一个最高 QC,C 拿不到"2f+1 个节点的最高 QC 都低于 X"的证据,只能拒投。要给出这个证据,就得让 2f+1 个节点各自签名并让所有节点都看到,这正是 TC,每个节点把超时消息发给所有人,换主就是 O(n²)。

为什么这笔交易划算。

  • HotStuff 每个块都多付 2δ,无论网络好坏;Jolteon 只在换 leader 时多付消息,而正常情况下 leader 诚实、网络良好,大多数块不需要换 leader。
  • 线性换主在实践中本来就省不下多少:换 leader 通常由超时触发,超时后要让所有节点进入同一轮,节点之间本来就要互相通知"我超时了",这一步几乎总是全员通信。HotStuff 为换主省下的消息,在同步轮次时又花掉了,却让每个块都多等了一轮。

Jolteon 部署为 DiemBFT v4 后,提交延迟从 7 次降到 5 次消息延迟,约快 30%。

  • leader 信誉:换主既然更贵,就按历史表现选 leader,尽量不让表现差的节点当 leader。
  • Ditto:网络进入异步时,从 leader 驱动的路径切换到不依赖超时的异步协议,恢复后再切回;解决的是"部分同步协议在长期异步下吞吐归零"的问题。

Aptos 的 AptosBFT 从 DiemBFT v4 继续演进:Quorum Store 把数据传播移出共识(第 16 节);顺序票让节点本地聚合 QC 后就广播顺序票,2f+1 张即确定顺序;乐观提议让下一轮 leader 不等 QC 就出块;执行与排序解耦,提交票单独确认执行结果(设计文档第 3 节);再往后是结合 DAG 思路的 Raptr。

12. HotStuff-2 与 HotStuff-1 改进了什么?

HotStuff-2 表明两阶段就能同时做到乐观响应与乐观线性通信;HotStuff-1 在此基础上加一阶段推测,让客户端更早得到确认。

12.1 HotStuff-2(Malkhi 与 Nayak,2023 年)

  • 结论:两阶段就够了。它同时做到:视图内两阶段提交、乐观响应、乐观情况下线性通信、最坏情况 O(n²)。此前普遍认为这几个性质不能兼得,HotStuff 为此用了三阶段。
  • 要点:新 leader 如果手里有上一个视图产生的证书,就说明没有人能锁在更高的块上,可以立即推进,保持响应性;只有在前一个视图失败时,才需要多等一段时间来收集各节点的锁,再安全地提议。正常情况不等,异常情况才付等待的代价。
  • 意义:在不牺牲响应性的前提下把 HotStuff 的三阶段降到两阶段,协议几乎没有增加复杂度。

12.2 HotStuff-1(Kang、Gupta、Malkhi、Sadoghi,2024 年)

  • 做法:在 HotStuff-2 基础上加一阶段推测:副本在第一阶段之后就推测执行,并提前向客户端发出"最终确认",客户端收到足够多一致的推测回复即可确认,比 HotStuff-2 少两跳网络延迟,同时保持线性通信。
  • 前缀推测困境:流水线协议里,前一个块的推测结果依赖更早的块,而流水线协议不能像固定主节点协议那样停下来回滚修复,推测一旦出错影响一整串前缀。HotStuff-1 是第一个在流水线协议里解决这个问题的协议。
  • slotting:每个 leader 在自己的任期内可以连续提议多个块(多个 slot),抵御两类 leader:出于利益故意拖延的理性 leader,以及故意破坏别人进度的恶意 leader。

13. MonadBFT 是什么?怎样防止尾部分叉?

MonadBFT 是 HotStuff 家族的流水线式协议,正常路径线性通信,一轮后给出推测最终性、两轮后最终确定,并且抵抗尾部分叉。 以下按 Monad 官方文档描述。

正常路径:

sequenceDiagram
  participant A as Alice(K 轮 leader)
  participant V as 验证者
  participant B as Bob(K+1 轮 leader)
  participant C as Charlie(K+2 轮 leader)
  A->>V: 提议块 A
  V->>B: 对块 A 投票,直接发给下一轮 leader
  B->>B: 聚合超多数票得到 QC(A)
  B->>V: 提议块 B,携带 QC(A)
  Note over V: 块 A 进入 Voted 状态:推测最终
  V->>C: 对块 B 投票
  C->>V: 提议块 C,携带 QC(B)
  Note over V: 块 A 进入 Finalized 状态:最终确定
  • 提议内容:轮次、区块(轮次、有序交易列表、QC)、可选的超时证书或无背书证书、leader 签名。
  • 流水线:每一轮同时带来一个新载荷和对上一个提议的 QC,于是父块推测最终、祖父块最终确定。
  • 推测最终性:只有在该块的提议者双签(同一高度签了两个不同的块)时才会回滚;双签有两块签名为证,可以问责。所以应用可以在一轮后就基于它执行交易。Monad 文档给出的是 1 个 slot 推测最终、2 个 slot 最终确定;slot 时长在公开资料里有 300 ms 与 400 ms 两种说法,属于公开数字,未在本项目环境复现。

超时与尾部分叉防护:

  1. leader 没有按时出块或下一个 leader 没聚合出 QC 时,验证者全员广播超时消息,每条消息带上自己的"tip",即自己见过的最新提议去掉载荷后的部分。
  2. 超多数超时消息组成超时证书,其中记录所有 tip 和轮次最高的"high tip"。
  3. 重新提议规则:新 leader 必须重新提议 high tip 指向的那个块,除非能证明它不可能拿到 QC。
  4. 无背书证书(NEC):要跳过那个块,新 leader 必须收集 2f+1 个"我没见过这个块"的签名声明。即使其中 f 个是拜占庭节点,仍有 f+1 个诚实节点没见过它,说明它不可能凑够法定人数。

这样,诚实 leader 提议的块不会因为下一个 leader 故意不聚合投票而被丢掉,MEV 抢夺式的尾部分叉被消除。

特点汇总: 正常路径消息数与验证数都线性,采用"leader 扇出、下一轮 leader 扇入"的通信模式;一个 leader 故障只造成一次超时延迟;具备乐观响应性。实现是 Rust 的 category-labs/monad-bft,与 C++ 的执行客户端 category-labs/monad 分开,均以 GPL-3.0 开源。

13.1 Fast HotStuff、Jolteon、MonadBFT 还会尾部分叉吗?

按论文描述的协议,Fast HotStuff 与 Jolteon 会尾部分叉,MonadBFT 不会。Aptos 实现的 Jolteon 默认把投票广播给所有验证者,"下一轮 leader 扣住投票"这条攻击路径不再成立。

尾部分叉是怎么发生的(以第 r 轮的块 B_r 为例):

  1. 诚实 leader 提议 B_r,2f+1 个验证者投票。
  2. 线性通信的流水线 HotStuff 里,投票只发给下一轮 leader L_{r+1},只有它能聚合出 QC(B_r)。
  3. L_{r+1} 故意不聚合、不提议,全网超时。
  4. 超时消息只携带各节点的最高 QC,没有人持有 QC(B_r),新 leader 可以合法地接在 B_{r−1} 之后提议,B_r 被丢掉。
  5. L_{r+1} 把 B_r 里的交易和 MEV 挪进自己后面的块。

成立的条件有两个:投票只汇到下一轮 leader,以及超时消息只记录 QC,不记录"投过票但还没形成 QC 的块"。去掉任意一个,攻击就做不成。

协议 会不会尾部分叉 原因
Fast HotStuff 会 AggQC 由 n − f 个节点的最高 QC 聚合而成,防的是新 leader 用旧 QC 分叉掉已经有 QC 的块(第 10 节)。QC(B_r) 从未形成,AggQC 里最高的是 QC(B_{r−1}),跳过 B_r 完全合规
Jolteon(论文) 会 投票同样只发给下一轮 leader;超时证书只记录各节点最高 QC 的轮次,B_r 没有 QC,不受保护
Jolteon(Aptos 实现) 这条攻击路径不成立 默认广播投票,收齐 2f+1 票的诚实节点都能自己聚合出 QC(B_r);它们的超时消息带上这个 QC,新 leader 必须接在 B_r 之后
MonadBFT 不会 超时消息携带 tip,新 leader 必须重新提议 high tip 指向的块,除非拿出无背书证书(第 13 节);重新提议的是原块,内容不能替换

Aptos 的依据(aptos-core 源码):

  • config/src/config/consensus_config.rs:broadcast_vote 默认为 true。
  • consensus/src/round_manager.rs:broadcast_vote 为真时调用 self.network.broadcast_vote(vote_msg),否则只 send_vote 给 get_valid_proposer(proposal_round + 1)。
  • consensus/consensus-types/src/timeout_2chain.rs:超时消息带 hqc_round,超时证书校验 hqc_round 等于其中签名轮次的最大值。
  • consensus/safety-rules/src/safety_rules_2chain.rs 的 safe_to_vote:块的轮次等于其 QC 轮次加一,或者等于超时证书轮次加一且 QC 轮次不低于证书里的最高 QC 轮次,才投票。新 leader 因此无法绕过证书里记录的 QC(B_r)。

投票广播之后,B_r 仍可能被放弃的情况只剩一种:投票在网络里延迟过久,组成超时证书的 2f+1 个节点都还没聚合出 QC(B_r)。这是网络异步造成的,leader 无法主动制造。

两种修法:

修法 代表 正常路径通信 代价
投票广播给所有验证者,人人都能聚合 QC Aptos O(n²) 消息数与验签量随 n² 增长,节点多时开销明显
超时消息携带 tip,强制重新提议 MonadBFT O(n),保持线性 超时路径更复杂,要处理 tip 与无背书证书

Crustle 部署假设下验证者固定为 21 个且同机房,每轮投票广播约 21 × 20 = 420 条消息,开销可以忽略,沿用 Aptos 默认的投票广播即可,不需要引入 MonadBFT 的超时机制。

14. HotStuff 系列演化总表:各代解决了什么问题?

14.1 一张表看演化

协议 年份 正常路径提交(消息延迟量级) 正常路径通信 换主通信 乐观响应 解决的主要问题 详见
PBFT 1999 约 3δ O(n²) O(n³) 否 实用 BFT 的起点 第 3 节
Tendermint 2014 起 约 3δ,逐高度 O(n²) O(n²) 否 轮换提议者、即时最终性 第 4 节
HotStuff 2018 约 7δ(链式) O(n) O(n) 是 线性通信、流水线 第 6 节
LibraBFT / DiemBFT v1–v3 2019 约 7δ O(n) O(n) 是 工程化落地 9.2 节
Fast HotStuff 2020 约 5δ O(n) O(n²) 消息,验签 O(1) 是 两链提交、防分叉 第 10 节
Jolteon(DiemBFT v4) 2021 约 5δ O(n) O(n²) 是 两链提交、比 v3 快约 30% 第 11 节
HotStuff-2 2023 约 5δ 乐观 O(n) 最坏 O(n²) 是 两阶段与响应性兼得 12.1 节
HotStuff-1 2024 推测确认比 HotStuff-2 少两跳 O(n) 最坏 O(n²) 是 流水线下的安全推测 12.2 节
MonadBFT 2025 推测最终约 3δ,最终约 5δ O(n) O(n²) 是 抗尾部分叉、推测最终性 第 13 节

延迟一列只是消息轮数的量级,不同论文的计数口径略有差异;实际延迟还要加上签名、验签、持久化与打包时间。

14.2 演化背后的几条规律

  1. 正常路径优先。 大部分时间网络正常、leader 诚实,协议越来越把优化集中在正常路径,把代价推到换主路径(Jolteon 用平方级换主换两链提交)。
  2. 轮数是延迟的主要来源。 三链到两链、两链到一轮推测,每减一轮都直接减少数个 δ;同机房部署时 δ 很小,轮数的影响相应变小,签名与持久化的开销变得显眼。
  3. 流水线带来新攻击面。 链式结构让下一个 leader 能影响前一个块的命运,于是出现分叉攻击(Fast HotStuff 修补)与尾部分叉(MonadBFT 修补)。
  4. 把不必要的事移出共识。 数据传播交给 Quorum Store 或 DAG,执行在排序之后异步进行,共识只对顺序和结果的摘要投票。

15. 什么是 Optimistic BFT?

"乐观"指假设网络良好、节点都诚实在线,走一条消息轮数更少的快速路径;一旦假设不成立,就回退到常规路径。安全性始终由 2f+1 的法定人数保证,乐观只影响常态下的速度。

这个词在共识文献里有三种用法,面试时先说清楚指哪一种:

用法 含义 例子
乐观快速路径 所有节点或超多数节点都及时回复时,一轮完成;否则补一轮 Zyzzyva、SBFT、Alpenglow
乐观响应性 网络好时,推进速度只取决于实际消息延迟,不必等最坏情况的超时 HotStuff、Fast HotStuff、Tendermint 的部分实现
乐观执行 / 推测最终性 排序还没最终确定就先执行,或者先给出一个极少回滚的"推测最终" Aptos 在收到提议时就开始执行(设计文档 3.7 节)、MonadBFT 的推测最终性(第 13 节)

HotStuff 家族不属于狭义的 Optimistic BFT。 狭义的 Optimistic BFT 指第一种用法:票数凑够比 2f+1 更多(通常是全部 3f+1)时少走一轮,凑不够再退回常规路径。Fast HotStuff、Jolteon、MonadBFT 只有一条路径,每一步都只要 2f+1 票,减少轮数靠的是链式流水线和两链提交规则;它们只在后两种意义上"乐观":都具备乐观响应性,MonadBFT 还有推测最终性。

协议 乐观快速路径 推测执行 / 推测最终
Zyzzyva 有:3f+1 个一致回复一轮完成,否则多一轮 有:副本推测执行,必要时回滚
SBFT 有:全部节点签名一轮完成,否则退回两轮 没有
Alpenglow(Votor) 有:80% 质押一轮最终确认,否则两轮各 60% 没有
Fast HotStuff 没有:始终 2f+1,两链提交 没有
Jolteon 没有:始终 2f+1,两链提交 没有
MonadBFT 没有:始终 2f+1 有:一轮后推测最终,只有提议者双签才会回滚
  • Fast HotStuff 的 "Fast" 指从三链提交降到两链提交,是在单一路径上少一轮,不是快速路径。
  • Jolteon 与 Ditto 论文标题里的"网络自适应、异步回退",指网络进入异步时回退到异步协议(第 11 节),是按网络状况切换路径,也不是按票数走快速路径。
  • 两类设计的取舍:快速路径要求几乎所有节点同时及时回复,一个节点慢就退回慢路径;HotStuff 家族每一步只要 2f+1 票,最多 f 个节点慢或故障也不影响速度,尾延迟更稳定。

Zyzzyva:推测执行加客户端提交。

  1. 主节点给请求分配序号,发给所有副本。
  2. 副本不等协商,直接按序号推测执行,把结果回给客户端。
  3. 客户端收到全部 3f+1 个一致的回复,请求完成,只用一轮。
  4. 只收到 2f+1 到 3f 个一致回复时,客户端把这 2f+1 个回复组成提交证书发给副本,副本确认后完成,多一轮。
  5. 副本之间的执行历史不一致时,靠视图切换与回滚修复,所以副本要能撤销推测执行。

SBFT:聚合签名加快速路径。 由收集者聚合各副本的签名,所有节点都签时一轮完成;有节点缺席时退回两轮的常规路径。聚合签名让每个节点只需验证一个签名,通信量从平方级降到线性。

Solana Alpenglow(2025 年提出)的 Votor。 一轮投票凑到 80% 质押即最终确认;凑不到时,两轮各凑 60% 也能最终确认。两条路径同时跑,谁先满足用谁。

Aptos 的乐观提议与顺序票。 下一轮 leader 投完票就发下一轮提议,不等 QC 形成;节点本地聚合出 QC 就发顺序票,2f+1 张顺序票即确定顺序。网络异常时退回常规的超时与换主。

代价。 快速路径要求更多节点同时在线、网络更稳定;回退路径多出一轮,而且要额外处理"快速路径与常规路径同时进行"时的一致性,协议与证明都更复杂。要和 Optimistic Rollup 的"乐观"区分:后者指默认状态正确、靠欺诈证明纠错,与共识协议无关。

16. leader 制共识的瓶颈在哪里?Narwhal、Quorum Store、DAG 如何解决?

瓶颈是 leader 的上行带宽:提议块携带全部交易时,leader 一个节点要把整块数据发给所有验证者。三种方案从两个方向解决:Narwhal 与 Quorum Store 把"数据传播"从共识里拆出去,由所有验证者并行完成;DAG 类共识更进一步,连"排序"也不再依赖单一 leader。

方案 数据传播 排序
传统 leader 制 leader 在提议块里携带全部交易 leader 提议,全员投票
Quorum Store 每个验证者各自广播批次,凑 2f+1 签名得到可用性证明 仍由 leader 提议,只放批次摘要
Narwhal 加 HotStuff 类共识 每个验证者每轮广播一个 DAG 顶点,顶点带批次摘要和对上一轮顶点的链接 由 leader 制共识对顶点排序
DAG 类共识(Tusk、Bullshark、Shoal) 同 Narwhal 不需要单独的 leader 提议,直接用锚点规则从 DAG 读出全序(第 17 节)

16.1 Narwhal 与 Quorum Store 有什么区别?

Quorum Store 是 Narwhal 的简化版:保留"批次加 2f+1 可用性签名",去掉了 DAG 的轮次和父链接。 Aptos 源码里的 Quorum Store 说明把它描述为基于 Narwhal 的数据传播层(AIP-26)。

Narwhal(Danezis 等,2022 年发表于 EuroSys)按轮次推进:

  1. 每个验证者分成一个主节点(primary)和若干工作节点(worker)。worker 负责收交易、打批次、把批次发给其他验证者的 worker;这部分可以横向扩到多台机器。
  2. 每一轮,主节点生成一个顶点,里面放本轮自己 worker 产出的批次摘要,以及对上一轮至少 2f+1 个已认证顶点的链接。
  3. 顶点可靠广播给所有验证者,收到者检查数据与链接都在本地后签名;凑够 2f+1 个签名,顶点成为已认证顶点,再广播出去。
  4. 某个验证者手里有了本轮 2f+1 个已认证顶点,就进入下一轮。

Quorum Store 没有轮次:

  1. 每个验证者按自己的节奏打批次,直接发给所有验证者。
  2. 收到者存下批次、签名回给作者;凑够 2f+1 个签名形成可用性证明,广播出去。
  3. 批次之间没有任何链接,谁先谁后完全由 leader 在提议里决定。
维度 Narwhal Quorum Store
结构 按轮次组织的 DAG:每个顶点链接上一轮 2f+1 个顶点 彼此独立的批次,没有轮次与链接
可用性证明 已认证顶点:证明顶点本身及其全部因果历史都可取 可用性证明:只证明这一个批次可取
公平性 每轮至少 2f+1 个验证者的顶点进入 DAG,排序时会一并带上 取决于 leader 选哪些证明;一个 leader 跳过某个作者,后面的 leader 仍会带上
与排序的关系 可以直接在 DAG 上做排序(Tusk、Bullshark),也可以交给 HotStuff 类协议 只能配合 leader 制共识(Aptos 配合 Jolteon)
回收 按轮次回收旧顶点 按批次过期时间回收
扩展 主节点与 worker 分离,worker 可以跨机器扩展 在验证者进程内完成,不做跨机器拆分
复杂度与开销 高:每轮每个验证者都要可靠广播并收集签名,维护 DAG 低:只有批次与证明,没有轮次同步

Aptos 选 Quorum Store 的理由是:数据传播的收益(均摊 leader 带宽、可用性先于排序)已经拿到,而不必付 DAG 的轮次同步与存储成本;排序仍交给成熟的 Jolteon。Aptos 另外也实现了 DAG 模式,两者的对比与例子见设计文档 3.3 节。

17. DAG 类共识为什么提出?它怎么给交易排序?有哪些协议?

DAG 类共识让所有验证者同时出块。每个验证者每一轮都发出一个"顶点",顶点里写着自己本轮打包的交易批次,以及自己已经收到了上一轮的哪些顶点。顶点之间互相引用,连成一张所有节点共享的图;每个节点再按同一条规则,从图里读出交易的先后顺序。它解决的是 leader 制共识的两个问题:交易数据都要经过 leader 发出去;leader 一慢,全网都要等。

17.1 为什么要提出 DAG:leader 制共识的两个问题

设想 4 个验证者 A、B、C、D,每一轮由一个 leader 出块,每块 10 MB 交易(数字只用来说明量级):

  1. 带宽都压在 leader 身上。 本轮 leader 是 A,A 要把 10 MB 分别发给 B、C、D,上行 30 MB;这段时间 B、C、D 的上行带宽基本闲着。验证者越多,leader 要发的越多,整条链的吞吐由一台机器的网卡决定。
  2. leader 一慢,全网等。 如果 A 这一轮卡住了(垃圾回收停顿、网络抖动、宕机),就没有提议,其他人只能等超时(Aptos 的默认初始值是 500 ms,见第 7 节),这段时间整条链一笔交易都不处理。

Quorum Store 与 Narwhal 解决第 1 个问题:交易数据由每个验证者各自提前分发,leader 的提议里只放数据的摘要(第 16 节)。DAG 类共识进一步解决第 2 个问题:不再有"本轮必须等某个 leader 出块"这件事,每个验证者每轮都出自己的顶点,谁慢了就不等谁。

17.2 四个基本概念:批次、顶点、链接、已认证

批次:一包交易。 验证者不断收到用户的交易,攒一小段时间就打成一包,这一包叫批次。例如 A 在 50 ms 内收到 500 笔下单交易,打成批次 a1,发给其他所有验证者。批次用摘要来指代:对批次内容算一个 32 字节的哈希,内容改动一个字节,摘要就完全不同,所以摘要相当于批次的指纹。之后大家只需要说"摘要为某某的那个批次",不必再传一遍 500 笔交易。

顶点:一张签了名的小纸条。 每个验证者每一轮写一张,上面有四样东西:

字段 例子:A 在第 2 轮的顶点 A2
作者和轮次 A,第 2 轮
本轮批次的摘要 批次 a2 的摘要
链接:上一轮若干顶点的摘要 A1、B1、C1 的摘要
作者签名 A 对以上内容的签名

顶点里只放摘要,不放交易本身,所以很小(量级是几百字节到几 KB);交易数据已经通过批次单独发过了。

链接:我已经收到并保存了这些顶点。 A2 里写上 B1 的摘要,意思是"A 写 A2 时,已经拿到了 B1,以及 B1 里的批次"。链接就像论文的参考文献:引用的是确定的内容(摘要保证内容不能被替换),而且只能引用更早的东西。顺着链接往回走,从 A2 能走到 A1、B1、C1,再往回能走到它们链接的顶点;一路能走到的全部顶点,叫 A2 的因果历史,也就是 A2 的作者写下它时已经看到的全部内容。

链接只指向上一轮,图里的箭头都指向过去,不会绕成一个圈,这就是"有向无环图"(Directed Acyclic Graph,DAG)这个名字的由来。

已认证:至少 3 个节点确认保存了它。 A 写好 A2 后发给所有人。B 收到后检查三件事:A 的签名对不对;批次 a2 自己收到了没有;A2 链接的 A1、B1、C1 自己有没有。都满足,就回给 A 一个签名,表示"我已保存 A2 及它引用的全部数据"。A 收齐 3 个签名(算上自己的,即 2f+1 个),把它们附在 A2 上,A2 就成了已认证顶点,A 再把它广播出去。

sequenceDiagram
  participant A
  participant B
  participant C
  participant D
  A->>B: 顶点 A2(批次 a2 的摘要,链接 A1、B1、C1)
  A->>C: 顶点 A2
  A->>D: 顶点 A2
  Note over B,C: 检查签名、批次 a2、A1/B1/C1 是否都在本地
  B-->>A: 签名:已保存
  C-->>A: 签名:已保存
  Note over A: 加上自己的签名,凑够 3 个
  A->>B: 已认证的 A2(附 3 个签名)
  A->>C: 同上
  A->>D: 同上
  Note over D: D 的签名还没回来也不影响,3 个已经够了

认证带来两个保证:

  • 数据一定取得到。 3 个签名里至少 2 个来自诚实节点,它们都保存了 A2 及它引用的全部数据。之后谁缺数据,都能从它们那里取到。
  • 作者不能两面说。 诚实节点在同一轮只给同一个作者签一次。假如 A 想给 B 看一个版本的 A2、给 C 看另一个版本,两个版本各需要 3 个签名,4 个节点里至少有 2 个节点两个版本都签了,其中至少 1 个是诚实节点,这不可能发生。所以每个作者每一轮最多只有一个已认证顶点。

进入下一轮:收齐上一轮 3 个已认证顶点。 验证者手里一有第 r 轮的 3 个已认证顶点,就可以写第 r+1 轮的顶点,并链接这 3 个。不必等第 4 个,所以最慢的那个节点拖不住其他人。

17.3 例子:DAG 怎么一轮一轮长出来

4 个验证者 A、B、C、D,f = 1,法定人数 3。每个验证者每轮打一个批次(A 的是 a1、a2、a3……),写一个顶点:

轮次 A B C D
1 A1:批次 a1 B1:批次 b1 C1:批次 c1 D1:批次 d1
2 A2:批次 a2,链接 A1、B1、C1 B2:批次 b2,链接 A1、B1、D1 C2:批次 c2,链接 B1、C1、D1 D2:批次 d2,链接 A1、C1、D1
3 A3:链接 A2、B2、C2 B3:链接 A2、B2、D2 C3:链接 B2、C2、D2 D 这一轮慢了,还没发出顶点

画成图,箭头从顶点指向它链接的上一轮顶点:

flowchart RL
  subgraph R3 [第 3 轮]
    A3[A3]
    B3[B3]
    C3[C3]
  end
  subgraph R2 [第 2 轮]
    A2["A2 = 锚点"]
    B2[B2]
    C2[C2]
    D2[D2]
  end
  subgraph R1 [第 1 轮]
    A1[A1]
    B1[B1]
    C1[C1]
    D1[D1]
  end
  A2 --> A1 & B1 & C1
  B2 --> A1 & B1 & D1
  C2 --> B1 & C1 & D1
  D2 --> A1 & C1 & D1
  A3 --> A2 & B2 & C2
  B3 --> A2 & B2 & D2
  C3 --> B2 & C2 & D2

几点说明:

  • 为什么 A2 没链接 D1。 A 准备写 A2 时,手里已经有 A1、B1、C1 三个已认证顶点,够 3 个就动手了;D1 的证书晚到了一点,没赶上。D1 并没有丢,B2、C2、D2 都链接了它。
  • D 慢了,其他人照常前进。 第 3 轮 D 没发出顶点,A、B、C 各自收齐第 2 轮的 3 个已认证顶点就写了第 3 轮。在 leader 制共识里,如果这一轮的 leader 恰好是 D,全网就要等超时。
  • 所有节点拼出的是同一张图。 每个节点都在本地拼这张图,收到顶点的先后可能不同;但每个作者每轮最多一个已认证顶点,顶点的链接又由摘要固定,所以某个顶点一旦被收到,它的内容和它的因果历史在所有节点那里都一样。

17.4 从图里读出顺序:锚点规则

图只记录了"谁在什么时候看到了什么",还没有给交易排出先后。排序靠一条所有节点事先都知道的确定性规则,每个节点在本地自己算,不需要再为排序单独发投票。以 Bullshark 为例:

  1. 每两轮指定一个锚点。 锚点是事先约定好的某个验证者在该轮的顶点(按轮换或按信誉决定,所有节点算出的一样),例子里第 2 轮的锚点是 A2。锚点的作用像"这一段的结账人":它被提交时,它看到的全部内容一起排序。
  2. 链接就是投票。 第 3 轮的顶点如果链接了 A2,就算给 A2 投了一票。例子里 A3、B3 链接了 A2,C3 没有,A2 得到 2 票。这些票不需要额外发送,图里本来就有。
  3. 凑够 f+1 票就提交锚点。 f+1 = 2,A2 被提交。为什么 2 票就够:以后的锚点在第 4 轮,它必须链接第 3 轮的 3 个顶点;第 3 轮最多只有 4 个顶点,其中没链接 A2 的最多 2 个(C3,以及可能晚到的 D3),所以任何 3 个里必然有 A3 或 B3,顺着它就能走到 A2。一般地,f+1 个链接者与任意 2f+1 个顶点至少有一个重合,所以以后的每个锚点都能追溯到 A2,所有节点对"A2 已提交"不会产生分歧。
  4. 把锚点的因果历史排好。 从 A2 出发能走到的、还没排过序的顶点是 A1、B1、C1 和 A2 自己。按"轮次从小到大,同一轮按验证者编号"排成 A1、B1、C1、A2,再把它们的批次依次展开:批次 a1 的 500 笔交易、b1 的交易、c1 的交易、a2 的交易,这就是这一段的交易顺序。同一笔交易如果出现在两个批次里,执行时跳过后一次。
  5. D1 在下一段排进来。 假设第 4 轮的锚点是 C4,它链接了 A3、B3、C3,并在第 5 轮拿够了票而提交。从 C4 能走到的、还没排过序的顶点是 D1、B2、C2、D2、A3、B3、C3 和 C4,按同样的规则排成 D1、B2、C2、D2、A3、B3、C3、C4。数据不会丢,只是晚一段排序。
  6. 锚点没凑够票怎么办。 比如 A 所在的节点很慢,第 3 轮没人链接 A2,A2 就暂不提交。下一个锚点提交时,如果从它能走到 A2,就先把 A2 这一段按顺序补上,再排自己的;走不到就跳过 A2。所有节点按同一规则判断,结果一致。

为什么所有节点排出同一个顺序。 三件事同时成立:大家拼出的是同一张图(17.3 节);锚点是谁、提交的条件、历史怎么排序,都是事先写死的规则;一个锚点的因果历史由链接的摘要唯一确定。每个节点自己算,结果必然一样。

17.5 DAG 解决了什么:为什么网络抖动时吞吐不会掉到零

情况 leader 制共识 DAG 类共识
交易数据怎么发出去 全部经由 leader 发给所有人,吞吐受 leader 一台机器的上行带宽限制 每个验证者发自己的批次,带宽由所有验证者分摊
本轮 leader(DAG 里是锚点的作者)很慢或宕机 这一轮没有提议,所有人等超时,这段时间吞吐为零 其他验证者照常广播批次、产生顶点,DAG 继续增长;这个锚点不提交,下一个锚点提交时一次把积累的顶点全部排进去
个别验证者网络差 leader 恰好是它时整轮变慢 只是它的顶点晚到或缺席,每轮只需 3 个顶点就能推进
恢复后 从超时后的新一轮重新开始 已经传播并认证的数据直接排序,没有浪费

直观地说:leader 制是"一个人拿着话筒念交易,他卡住全场就停";DAG 是"所有人各自往一张公共板上贴便签,并标明看到了哪些别人的便签"。没有专门的排序员:每个人手里都有同一份规则,自己把板上的便签排好;规则指定每隔一段由某个人的便签当"结账点"(锚点),结账点一到,之前的便签就按规则排定。某个人卡住,板上照样在增加便签。

锚点卡住会怎样。 DAG 里最接近"排序员"的是锚点,但锚点只是图里的一个顶点,排序由每个节点在本地完成:

卡住的是谁 后果 恢复
锚点的作者(慢或宕机) 锚点拿不到 f+1 个链接,或者根本没发出来,这一段暂不提交;批次照常分发,DAG 照常增长 下一个锚点提交时,把积累的顶点一次排进去;被跳过的锚点如果能从新锚点走到,就先补上它那一段(17.4 节第 6 步)。代价是这一段的排序延迟变长:Bullshark 每两轮一个锚点,至少多等两轮
某个节点自己(本地卡住) 只是它自己落后,别人照常排序、提交 恢复后向别人拉取缺的顶点和批次,按同一规则重算,得到与别人相同的顺序
超过 f 个节点同时卡住 每轮凑不齐 2f+1 个已认证顶点,DAG 不再增长,链停 与 leader 制一样,这是 BFT 的容错上限;节点恢复到 2f+1 个以上后继续

锚点作者卡住也不是完全没有代价。Bullshark 的部分同步版本为了让诚实锚点有机会拿够链接,节点在锚点所在轮会等锚点一段时间,等不到才超时进入下一轮(按论文描述,未实测),所以锚点作者宕机时,DAG 的增长会慢一个超时;批次的分发不受影响。后续协议从两个方向减少这个代价:Shoal 按信誉选锚点,少选慢节点,并让每一轮都有锚点;Mysticeti 等协议缩短从顶点到提交的路径(17.7 节)。

17.6 代价在哪

  • 消息更多。 每一轮,每个验证者都要可靠广播一个顶点、收 2f+1 个签名、再广播证书,一轮约 3n² 条消息;leader 制每块主要是一次提议广播加 n 张票(投票广播时约 n²)。节点多时差别明显。
  • 排序更慢。 一个顶点从产生到被排序,至少要经过它所在轮次、锚点轮、投票轮,而每一轮都包含一次可靠广播(约 2 到 3 次消息延迟);不是锚点的顶点还可能要等下一个锚点。Bullshark 的延迟明显高于两链 HotStuff。
  • 实现复杂。 要存储和回收 DAG、拉取缺失的顶点、处理异步到达的顶点、保证各节点的排序规则完全一致。

17.7 主要协议

协议 要点
Narwhal + Tusk Narwhal 负责按轮次构建已认证的 DAG;Tusk 在其上做异步排序,用公共随机数事后选锚点,不依赖超时
Bullshark 部分同步网络下的排序规则:每两轮一个锚点,下一轮 f+1 个顶点链接它即提交(17.4 节的例子)
Shoal 在 Bullshark 上做流水线,让每一轮都有锚点,并按信誉选锚点,降低排序延迟;Aptos 的 DAG 模式基于这一思路
Mysticeti(Sui) 不再为每个顶点收集签名证书,直接把链接当作隐含投票,提交只需约 3 次消息延迟,消息量也大幅减少

从 Bullshark 到 Mysticeti 的演进方向,就是在保留"没有单一 leader 瓶颈、网络抖动不停摆"的前提下,把排序延迟和消息量压下来。

18. 这几种共识怎么对比?

协议 正常路径延迟(消息延迟 δ 的量级) 正常路径通信 换主通信 流水线 适用
PBFT 约 3δ O(n²) O(n³)(朴素) 否 小规模许可链
Tendermint 约 3δ,逐高度 O(n²) O(n²) 否 应用链、需要即时最终性;Rust 实现 Malachite 用于 Arc
HotStuff 约 7δ O(n) O(n) 是 大规模验证者
Jolteon(两链) 约 5δ O(n) O(n²) 是 Aptos、Diem
Fast HotStuff(两链) 约 5δ O(n) O(n²) 条消息,每个节点只验 2 个聚合签名 是 Plasma 的 PlasmaBFT
MonadBFT 推测最终约 3δ,最终约 5δ O(n) O(n²)(超时消息全员广播) 是 Monad
加顺序票 约 3δ 定序 O(n²)(投票广播) O(n²) 是 Aptos 当前配置
Bullshark / Shoal 约 4 到 6 次可靠广播 O(n²) 每轮 无单一 leader 是 高吞吐、网络不稳定
Mysticeti 约 3δ O(n²) 每轮 无单一 leader 是 Sui

延迟一列只是消息轮数的量级,实际还要加上签名与验签、持久化和打包时间。

19. 怎样设计一个延迟低于 200 ms 的共识?

先算延迟预算:共识延迟约等于"消息轮数 × 单向网络延迟 + 每轮的计算与持久化",然后从这几项分别压缩。

  1. 网络延迟决定下限。 跨大洲单向延迟 50 到 150 ms,走 3 次消息延迟就可能超过 200 ms;同一地区单向约 1 到 10 ms,同机房亚毫秒。验证者的地理分布是第一决定因素,Hyperliquid、Crustle 的集中部署都是为此。
  2. 减少消息轮数。 用两链规则、顺序票或快速路径,把定序压到约 3δ;乐观提议让下一个 leader 不等 QC 就出块。
  3. 排序与执行解耦。 共识只对交易顺序投票,执行结果在后面单独确认(Aptos 的提交票、Monad 的延迟执行),执行时间不在共识的关键路径上。
  4. 数据传播与排序解耦。 用 Quorum Store 或 DAG 提前把交易数据分发出去,提议块只有几 KB,不受块大小影响。
  5. 压缩每轮的计算。 BLS 聚合签名批量验证;投票的持久化用顺序写的 WAL;签名验证并行化。
  6. 超时与换主。 超时按实际网络设置,而不是按最坏情况;leader 按信誉选择,慢节点不当 leader。

20. 从零实现一个 BFT 共识,要注意哪些工程问题?

  • 安全规则独立并持久化:记录最后投票的轮次与锁定的块,投票前先落盘,崩溃重启后绝不对同一轮投两次不同的票。Aptos 把它放在单独的 SafetyRules 组件里,可以跑在独立进程或安全硬件中。
  • 轮次推进(pacemaker):收到 QC 或超时证书就进入下一轮;超时指数退避,防止网络异常时轮次空转。
  • 同步:落后的节点要能向别人拉取缺失的块与证书,再追上最新轮次。
  • 消息校验:所有签名、轮次、父块链接都要校验;不合法的消息丢弃,不能让它阻塞主循环。
  • 测试:用确定性模拟器注入延迟、丢包、分区和拜占庭行为,检查安全性(永不提交冲突的块)和活性(GST 之后一定出块)。Aptos 的 twins 测试就是让同一身份的两个节点同时运行来模拟双签。