2018 年,VMware Research 的 Maofan Yin、Dahlia Malkhi、Michael K. Reiter、Guy Golan-Gueta 和 Ittai Abraham 发表了 HotStuff 协议。这篇论文解决了一个困扰 BFT(Byzantine Fault Tolerance)共识领域近二十年的工程问题:PBFT 的视图切换(view change)消息复杂度是 O(n²),如何把它压到 O(n)?
这个问题为什么重要?因为在实际部署中,视图切换不是异常事件,而是常态操作。Leader 宕机、网络分区、节点超时——任何一个都会触发视图切换。如果每次视图切换都需要 n 个节点之间两两交换消息,那在 n=100 的规模下就是上万条消息。这使得 PBFT 在超过几十个节点的场景中几乎不可用。
HotStuff 的方案出人意料地简单:把视图切换的逻辑嵌入正常操作路径,让每个阶段都通过 Leader 中转消息,用门限签名(threshold signature)把 n 个签名聚合成一个。结果是每个阶段的消息复杂度降到 O(n)——每个副本只需要和 Leader 通信,不需要和其他副本直接通信。
更进一步,Chained HotStuff 把三轮投票流水线化:每个视图只执行一轮 Generic 投票,一个提案的三轮投票分散在连续三个视图中完成。这意味着系统的吞吐量不再受限于单个提案的三轮延迟——多个提案在流水线中并行推进。
Facebook(后改名 Meta)在 2019 年选择 HotStuff 作为 Libra(后改名 Diem)区块链的共识骨架,直接推动了这个协议从学术论文走向工业实现。虽然 Diem 项目最终被搁置,但 HotStuff 的设计思想深刻影响了后续的 BFT 协议——从 Aptos 的 Jolteon/DiemBFT 到 Sui 的 Narwhal-Bullshark,再到以太坊 2.0 的 Casper FFG,现代区块链共识的设计语言很大程度上是 HotStuff 定义的。
这篇文章从 PBFT 的瓶颈出发,逐步拆解 HotStuff 的核心设计,追踪 Chained HotStuff 的流水线优化,分析 DiemBFT 的工程实践,然后把视野扩展到 Tendermint、Narwhal-Bullshark 等现代 BFT 的演化方向,最后讨论 BFT 在非区块链场景的实际价值。
一、PBFT 的瓶颈:O(n²) 的代价
要理解 HotStuff 的设计动机,必须先理解 PBFT 的痛点在哪里。
PBFT 正常路径的消息模式
PBFT(Practical Byzantine Fault Tolerance)由 Miguel Castro 和 Barbara Liskov 在 1999 年提出,是第一个在异步网络中可实际使用的 BFT 共识协议。它的正常操作路径分三个阶段:
- Pre-prepare:Leader(称为 Primary)向所有副本广播提案,消息数 O(n)。
- Prepare:每个副本收到 Pre-prepare 后,向所有其他副本广播 Prepare 消息,消息数 O(n²)。
- Commit:副本收集到 2f+1 个 Prepare 消息后,向所有其他副本广播 Commit 消息,消息数 O(n²)。
正常路径的总消息复杂度是 O(n²)。对于 n=4(f=1),这意味着 Prepare 阶段有 12 条消息,Commit 阶段又有 12 条。节点数翻倍,消息数翻四倍。
这个 O(n²) 在正常路径上还勉强可接受——毕竟每个节点只发 O(n) 条消息。真正的问题出在视图切换。
视图切换:PBFT 的阿喀琉斯之踵
当 Leader 出现故障(宕机、响应过慢、被怀疑是拜占庭节点),副本需要发起视图切换(view change),选出新的 Leader。PBFT 的视图切换协议如下:
- 副本检测到 Leader 超时,向所有节点广播
VIEW-CHANGE消息。这个消息必须包含该副本已经 Prepare 但尚未 Commit 的所有请求的证明(proof)——具体来说,是 2f+1 个 Prepare 消息的集合。 - 新 Leader 收集 2f+1 个
VIEW-CHANGE消息,计算出需要在新视图中重新提议的请求,构造NEW-VIEW消息广播给所有副本。
问题在于第一步:每个 VIEW-CHANGE
消息的大小是 O(n)(因为要包含 2f+1 个签名),而有 n
个节点各发一条这样的消息,新 Leader
需要验证所有这些消息。因此视图切换的通信复杂度是
O(n²)(每条消息 O(n) 大小,共 O(n) 条),新 Leader
的计算复杂度也是 O(n²)(验证 O(n) 条消息,每条包含 O(n)
个签名)。
在 n=4 时这没什么问题。但当 n 增长到 100,一次视图切换就涉及数万条签名的传输和验证。如果网络不稳定导致连续多次视图切换,系统可能在视图切换的循环中无法自拔,完全无法提供服务。
用一个类比来说明:PBFT 的正常路径像一个高效的流水线工厂,但每次工厂需要换主管(视图切换),所有工人就得停工,拿出各自的工作记录互相核对。工人越多,核对的时间就越长。对于一个需要频繁换主管的工厂来说,大部分时间花在核对记录上,实际生产几乎停滞。
消息复杂度对比
上图展示了 n=4 时两种协议每轮的消息模式差异。PBFT 中每个节点向所有其他节点发送消息,形成全连接拓扑;HotStuff 中所有消息通过 Leader 中转,形成星型拓扑。
| 指标 | PBFT | HotStuff |
|---|---|---|
| 正常路径消息复杂度 | O(n²) | O(n) |
| 视图切换消息复杂度 | O(n²) | O(n) |
| 视图切换与正常路径是否统一 | 否(独立协议) | 是(嵌入正常路径) |
| 单条消息大小 | O(1) | O(1)(门限签名聚合后) |
| Leader 瓶颈 | Prepare/Commit 不经过 Leader | 所有消息经过 Leader |
这张表揭示了一个关键的权衡:HotStuff 通过把所有通信集中到 Leader 来降低消息复杂度,但这也意味着 Leader 成为通信瓶颈。当 Leader 是诚实的且网络通畅时,这个权衡是值得的;但如果 Leader 是恶意的或者性能低下,需要快速切换视图,HotStuff 的优势才真正体现——因为视图切换本身也是 O(n) 的,不会成为系统的瓶颈。
二、HotStuff 的核心设计:三轮投票与线性视图切换
HotStuff 的设计目标可以用一句话概括:让视图切换和正常操作使用完全相同的协议路径,消息复杂度统一为 O(n)。
为了实现这个目标,HotStuff 引入了两个关键技术:门限签名(Threshold Signature)和三阶段投票协议。
门限签名:把 n 个签名压成一个
门限签名是一种密码学原语。一个 (k, n)-门限签名方案允许 n 个参与者各持一个私钥份额(key share),任意 k 个参与者可以合作生成一个有效签名,但少于 k 个参与者无法伪造签名。验证时只需要一个公钥和一个聚合签名,验证成本是 O(1),与参与者数量无关。
在 HotStuff 中,使用 (2f+1, n)-门限签名方案,其中 n = 3f+1。Leader 收集副本的投票(每个投票是一个签名份额),当收集到 2f+1 个份额后,将它们聚合成一个门限签名,称为 Quorum Certificate(QC,法定证书)。
这个设计的效果是:
- 传输成本:Leader 向副本发送的消息中只需包含一个 QC(常数大小),而不是 2f+1 个独立签名。
- 验证成本:副本验证一个 QC 的成本是 O(1),而不是逐一验证 2f+1 个签名。
- 视图切换:新 Leader 发送的
NEW-VIEW消息只需包含一个 QC,而不是 PBFT 中的 O(n) 个 Prepare 证明。
class ThresholdSignature:
"""门限签名的简化接口"""
def __init__(self, threshold, total):
self.threshold = threshold # k = 2f+1
self.total = total # n = 3f+1
self.shares = {}
def add_share(self, node_id, signature_share):
"""收集一个签名份额"""
self.shares[node_id] = signature_share
def is_complete(self):
"""是否收集到足够的份额"""
return len(self.shares) >= self.threshold
def aggregate(self):
"""聚合为单个签名(QC)"""
assert self.is_complete()
return combine_shares(list(self.shares.values()))
@staticmethod
def verify(qc, public_key, message):
"""验证 QC,成本 O(1)"""
return verify_threshold_sig(qc, public_key, message)三阶段投票协议
HotStuff 的正常操作路径包含三个投票阶段,每个阶段的消息模式完全相同:
- Prepare 阶段:Leader
向所有副本发送提案(包含上一个阶段的
QC),副本验证后发送投票给 Leader。Leader 收集 2f+1
个投票,聚合成
prepareQC。 - Pre-commit 阶段:Leader 将
prepareQC发送给所有副本,副本验证后发送投票。Leader 收集投票聚合成precommitQC。 - Commit 阶段:Leader 将
precommitQC发送给所有副本,副本验证后发送投票。Leader 收集投票聚合成commitQC。 - Decide:Leader 将
commitQC发送给所有副本。副本收到后执行提案中的命令并回复客户端。
每个阶段的消息流动模式是相同的:Leader 广播(O(n) 条消息)→ 副本投票给 Leader(O(n) 条消息)→ Leader 聚合生成 QC。总消息数是每阶段 2n,三个阶段加上 Decide 共 8n = O(n)。
以下时序图展示了一个完整提案从 Prepare 到 Decide 的四阶段消息流。以 n=4(f=1)为例,注意每个阶段的消息模式完全对称,且所有通信都通过 Leader 中转:
sequenceDiagram
participant C as Client
participant L as Leader
participant R1 as Replica-1
participant R2 as Replica-2
participant R3 as Replica-3
C->>L: 客户端请求 (cmd)
rect rgb(230, 245, 255)
Note right of L: Prepare 阶段
L->>R1: Propose (block, justify=highQC)
L->>R2: Propose (block, justify=highQC)
L->>R3: Propose (block, justify=highQC)
R1-->>L: vote-prepare (签名份额)
R2-->>L: vote-prepare (签名份额)
R3-->>L: vote-prepare (签名份额)
Note over L: 聚合 2f+1=3 个份额 → prepareQC
end
rect rgb(255, 245, 230)
Note right of L: Pre-commit 阶段
L->>R1: prepareQC
L->>R2: prepareQC
L->>R3: prepareQC
R1-->>L: vote-precommit (签名份额)
R2-->>L: vote-precommit (签名份额)
R3-->>L: vote-precommit (签名份额)
Note over L: 聚合 → precommitQC
end
rect rgb(230, 255, 230)
Note right of L: Commit 阶段
L->>R1: precommitQC
L->>R2: precommitQC
L->>R3: precommitQC
R1-->>L: vote-commit (签名份额)
R2-->>L: vote-commit (签名份额)
R3-->>L: vote-commit (签名份额)
Note over L: 聚合 → commitQC
end
rect rgb(245, 230, 255)
Note right of L: Decide 阶段
L->>R1: commitQC(执行命令)
L->>R2: commitQC(执行命令)
L->>R3: commitQC(执行命令)
end
par 各副本回复
R1-->>C: REPLY (result)
R2-->>C: REPLY (result)
R3-->>C: REPLY (result)
end
Note over C: 收到 f+1=2 个相同结果
该时序图体现了 HotStuff 与 PBFT 的核心区别:所有通信都经过 Leader 中转,形成星型拓扑而非全连接拓扑。每个阶段产生 2n 条消息(Leader 广播 n 条,副本回复 n 条),四个阶段总计 8n = O(n) 条消息。与 PBFT 的 O(n^2) 相比,在 n=100 的规模下从约 2 万条消息降到约 800 条。代价是 Leader 成为通信瓶颈,但由于视图切换也是 O(n) 的,Leader 故障时可以快速切换。
这里有一个核心问题:为什么需要三轮投票,而不是像 PBFT 那样两轮就够?
为什么是三轮:安全性的代价
PBFT 的 Prepare 和 Commit 两个阶段使用了不同的消息模式——Prepare 是全连接广播(每个节点给所有节点发),Commit 也是全连接广播。这种 O(n²) 的通信模式实际上携带了更多信息:每个诚实节点在 Prepare 阶段结束时可以确认”至少有 2f+1 个节点(包括自己)看到了同样的 Pre-prepare”。这个信息是全局共识的——所有诚实节点都知道其他诚实节点也知道这件事。
但在 HotStuff 的星型拓扑中,副本之间不直接通信。副本 A 给 Leader 发了投票,副本 B 也给 Leader 发了投票,但 A 不知道 B 是否投了票——只有 Leader 知道。所以一轮投票后,虽然 Leader 知道有 2f+1 个投票,但副本只知道”我自己投了票”。
为了建立全局共识,HotStuff 需要 Leader 把聚合后的 QC 广播回去,让每个副本都知道”投票已经达到法定数量”。但这还不够——因为 Leader 可能是拜占庭节点,它可能只把 QC 发给部分副本。所以需要再来一轮,让副本们确认他们都收到了 QC。
具体来说,三轮投票建立了以下安全性梯度:
- 一轮投票(prepareQC):至少 2f+1 个副本投票支持这个提案。但只有 Leader 持有这个 QC。
- 两轮投票(precommitQC):至少 2f+1 个副本知道 prepareQC 存在。这意味着任何后续的 Leader 在发起视图切换时,一定能从某个诚实副本那里获得这个 prepareQC。
- 三轮投票(commitQC):至少 2f+1 个副本知道 precommitQC 存在。这提供了最终的安全保证——即使视图切换发生,新 Leader 也能恢复出正确的状态。
如果只有两轮投票,存在如下攻击场景:
- Leader L₁ 在视图 v 中完成了提案 B 的 Prepare 阶段,获得了 prepareQC。
- L₁ 把 prepareQC 发给了部分副本(比如 f 个副本),但在发给其余副本之前宕机了。
- 视图切换到 v+1,新 Leader L₂ 没有收到任何关于提案 B 的信息(因为拥有 prepareQC 的 f 个副本可能都是拜占庭节点,不转发)。
- L₂ 提出了一个冲突的提案 B’,并完成了两轮投票。
- 现在 B 和 B’ 都有可能被提交,违反了安全性。
三轮投票中的第二轮(Pre-commit)确保了在进入 Commit 之前,prepareQC 已经被”广播”到了 2f+1 个副本中。这意味着在上述攻击场景的第 3 步中,L₂ 一定能从至少一个诚实副本那里获得 prepareQC,从而知道提案 B 正在进行中,不会提出冲突的 B’。
伪代码:基本 HotStuff 协议
以下是 HotStuff 协议的核心逻辑伪代码:
class HotStuffReplica:
def __init__(self, id, n, f):
self.id = id
self.n = n
self.f = f
self.view = 0
self.locked_qc = None # 当前锁定的 QC(precommitQC)
self.prepare_qc = None # 最高的 prepareQC
self.b_exec = None # 最后执行的区块
def on_receive_proposal(self, msg, leader):
"""副本收到 Leader 的提案"""
block = msg.block
qc = msg.justify # 上一阶段的 QC
# 安全性检查:提案必须扩展自 lockedQC 对应的区块,
# 或者 justify 的视图号高于 lockedQC 的视图号
safe = (
self.extends(block, self.locked_qc.block) or
qc.view > self.locked_qc.view
)
if safe and self.valid(block):
# 发送投票给 Leader
vote = self.sign_share(block.hash, self.view)
send(leader, VoteMsg(vote, self.view))
def leader_on_prepare(self, block):
"""Leader 发起 Prepare 阶段"""
# 广播提案给所有副本
msg = PrepareMsg(block, justify=self.prepare_qc)
broadcast(msg)
# 收集 2f+1 个投票
votes = collect_votes(2 * self.f + 1)
prepare_qc = aggregate(votes) # 门限签名聚合
return prepare_qc
def leader_on_precommit(self, prepare_qc):
"""Leader 发起 Pre-commit 阶段"""
msg = PrecommitMsg(prepare_qc)
broadcast(msg)
votes = collect_votes(2 * self.f + 1)
precommit_qc = aggregate(votes)
return precommit_qc
def leader_on_commit(self, precommit_qc):
"""Leader 发起 Commit 阶段"""
msg = CommitMsg(precommit_qc)
broadcast(msg)
votes = collect_votes(2 * self.f + 1)
commit_qc = aggregate(votes)
return commit_qc
def leader_on_decide(self, commit_qc):
"""Leader 广播 Decide"""
msg = DecideMsg(commit_qc)
broadcast(msg)
def on_commit(self, precommit_qc):
"""副本在 Commit 阶段更新锁定状态"""
self.locked_qc = precommit_qc
def on_decide(self, commit_qc):
"""副本执行已提交的命令"""
block = commit_qc.block
self.execute(block)
self.b_exec = block视图切换:嵌入正常路径
HotStuff 最优雅的设计在于:视图切换不需要独立的协议。
当副本检测到当前 Leader 超时时,它只需要向新 Leader
发送一条 NEW-VIEW 消息,其中包含它所知道的最高
prepareQC。新 Leader 收集 2f+1 条
NEW-VIEW 消息,选择视图号最高的那个
prepareQC 作为自己提案的
justify(证明),然后走正常的三阶段流程。
def on_view_timeout(self):
"""视图超时,发起视图切换"""
self.view += 1
new_leader = self.get_leader(self.view)
# 只需发送自己最高的 prepareQC,消息大小 O(1)
send(new_leader, NewViewMsg(self.prepare_qc, self.view))
def leader_on_new_view(self, new_view_msgs):
"""新 Leader 处理视图切换"""
# 从 2f+1 条 NEW-VIEW 消息中选择最高的 prepareQC
highest_qc = max(
[msg.prepare_qc for msg in new_view_msgs],
key=lambda qc: qc.view
)
self.prepare_qc = highest_qc
# 然后直接走正常的 Prepare 流程
new_block = self.create_block(self.prepare_qc)
self.leader_on_prepare(new_block)和 PBFT 的视图切换对比:
- PBFT 中,每个
VIEW-CHANGE消息包含 O(n) 个签名(Prepare 证明),总通信量 O(n²)。 - HotStuff 中,每个
NEW-VIEW消息只包含一个 QC(常数大小),总通信量 O(n)。
这就是门限签名带来的核心收益:把 O(n) 个独立签名压缩成一个 QC,使得证明的传递成本从 O(n) 降到 O(1)。
响应性(Responsiveness)
HotStuff 还有一个重要的性质:乐观响应性(optimistic responsiveness)。
在很多 BFT 协议中,Leader 需要等待一个固定的超时时间(比如 2Δ,其中 Δ 是网络延迟的已知上界)才能推进到下一阶段。即使所有副本都在 10 毫秒内回复了,Leader 也得等到超时才能继续。
HotStuff 不需要这样。Leader 只要收到 2f+1 个投票就可以立即推进,不需要等待任何超时。系统的实际延迟完全由网络速度决定,而不是由预设的超时参数决定。
这在实践中意味着:当网络状况良好时,HotStuff 的延迟可以非常低——接近三个网络往返时间(3 RTT)。而 PBFT 在某些实现中需要等待超时,导致延迟被超时参数下限限制。
响应性的形式化定义是:在 GST(Global Stabilization Time)之后,如果当前 Leader 是诚实的,协议的推进速度只取决于实际的消息传递延迟,与任何超时参数无关。
安全性与活性的分离
HotStuff 明确分离了安全性(safety)和活性(liveness)的职责:
- 安全性由投票规则(voting rule)保证:副本只在提案满足安全条件时投票。即使 Leader 是拜占庭节点,只要诚实副本遵守投票规则,就不会有两个冲突的提案都被提交。
- 活性由 Pacemaker(起搏器)模块保证:Pacemaker 负责同步各副本的视图号,确保最终所有诚实副本进入同一个视图,并且该视图的 Leader 是诚实的。
Pacemaker 的设计是可插拔的——不同的实现可以使用不同的 Pacemaker 策略(基于超时、基于指数退避、基于同步器等),而不影响安全性。这种模块化设计大大简化了协议的正确性证明:安全性的证明只涉及投票规则,不涉及 Pacemaker 的具体实现。
class Pacemaker:
"""起搏器:负责活性,不影响安全性"""
def __init__(self, base_timeout=1.0):
self.current_view = 0
self.base_timeout = base_timeout
self.timeout = base_timeout
def start_timer(self, view):
"""为当前视图设置超时"""
self.current_view = view
set_timer(self.timeout, lambda: self.on_timeout(view))
def on_timeout(self, view):
"""视图超时,推进到下一个视图"""
if view == self.current_view:
self.timeout *= 2 # 指数退避
self.advance_view(view + 1)
def on_qc_received(self, qc):
"""收到 QC 后重置超时"""
if qc.view >= self.current_view:
self.timeout = self.base_timeout # 重置超时
self.advance_view(qc.view + 1)
def advance_view(self, new_view):
"""推进到新视图"""
self.current_view = new_view
leader = get_leader(new_view)
self.start_timer(new_view)
notify_replica(new_view, leader)三、Chained HotStuff:流水线化的共识
基本 HotStuff 需要三轮投票来提交一个提案。在每个视图中,Leader 要依次执行 Prepare、Pre-commit、Commit 三个阶段,每个阶段都是一个完整的”Leader 广播 → 副本投票 → Leader 聚合”的循环。这意味着一个提案从提出到被提交需要三个网络往返时间。
Chained HotStuff 的核心洞察是:三个阶段的消息模式完全相同——都是 Leader 广播然后收集投票。那为什么不把它们合并成一种通用消息(Generic Phase),让每个视图只执行一轮 Generic 投票,把一个提案的三轮投票分散到连续三个视图中完成?
流水线设计
在 Chained HotStuff 中,每个视图的 Leader 执行以下操作:
- 创建一个新提案(新的区块/命令)。
- 广播这个提案给所有副本,附带上一个视图的 QC。
- 收集 2f+1 个投票,聚合成 QC。
- 将 QC 传递给下一个视图的 Leader。
关键在于:对于某个提案 cmd_k,它的 Prepare
阶段在视图 v 完成,Pre-commit 阶段在视图
v+1 完成(作为 cmd_{k+1} 的
Prepare 的”副产品”),Commit 阶段在视图 v+2
完成。当视图 v+3 的 QC
生成后,cmd_k 正式被提交。
上图展示了流水线的运作方式。每个 View 只有一轮 Generic 投票,但每一轮投票同时推进了多个提案的不同阶段:
- View 1 的投票是 Cmd₁ 的 Prepare。
- View 2 的投票既是 Cmd₁ 的 Pre-commit,也是 Cmd₂ 的 Prepare。
- View 3 的投票既是 Cmd₁ 的 Commit,也是 Cmd₂ 的 Pre-commit,也是 Cmd₃ 的 Prepare。
- View 4 的投票使 Cmd₁ 达到 Decide(可以执行),同时推进 Cmd₂、Cmd₃、Cmd₄。
k-chain 提交规则
Chained HotStuff 使用”k-chain”的概念来描述一个提案的提交进度:
- 1-chain:一个区块 B 有一个直接子 QC(即下一个视图的 QC 指向 B)。对应基本 HotStuff 的 Prepare 完成。
- 2-chain:B 有一个 1-chain,且那个 1-chain 的下一个视图也有 QC 指向它。对应 Pre-commit 完成。副本在此时锁定(lock)到这个提案。
- 3-chain:B 有一个 2-chain,且链条继续延伸。对应 Commit 完成。副本在此时提交(commit)这个提案。
需要注意的是,k-chain 要求视图号是连续的。如果中间有视图被跳过(比如 Leader 超时导致视图切换),链条就断了,之前的提案回退到更早的阶段。
QC 链形成与提交规则的具体推演
用一个具体的区块高度推演来理解 QC 链如何触发提交。假设从 View 1 开始,连续四个视图的 Leader 都成功出块:
- View 1:Leader L1 提出区块 B1,收集 2f+1 投票,生成 QC1(指向 B1)。
- View 2:Leader L2 提出区块 B2(携带 QC1 作为 justify),收集投票生成 QC2(指向 B2)。此时 B1 形成 1-chain(QC1 存在)。
- View 3:Leader L3 提出区块 B3(携带 QC2),收集投票生成 QC3。此时 B1 形成 2-chain(QC1→QC2 连续),副本对 B1 执行锁定——后续投票时只会投给扩展 B1 的提案,或者 justify 视图号高于 QC1 的提案。
- View 4:Leader L4 提出区块 B4(携带 QC3),收集投票生成 QC4。此时 B1 形成 3-chain(QC1→QC2→QC3 连续且视图号 1→2→3 递增),B1 被正式提交。
如果 View 3 的 Leader 超时未出块(视图被跳过),那么 QC2 之后的下一个 QC 的视图号不是 3 而是 4,视图号不连续,B1 的 2-chain 无法形成,锁定不会发生。这是 Chained HotStuff 安全性的关键:只有连续视图的 QC 链才能推进提交。
flowchart TD
B1["B1 (View 1)<br/>QC1 指向 B1"] --> B2["B2 (View 2)<br/>QC2 指向 B2<br/>justify = QC1"]
B2 --> B3["B3 (View 3)<br/>QC3 指向 B3<br/>justify = QC2"]
B3 --> B4["B4 (View 4)<br/>QC4 指向 B4<br/>justify = QC3"]
B1 -.->|"1-chain: QC1 存在"| C1["Prepare 完成"]
B1 -.->|"2-chain: QC1→QC2<br/>View 1→2 连续"| C2["Pre-commit 完成<br/>副本锁定到 B1"]
B1 -.->|"3-chain: QC1→QC2→QC3<br/>View 1→2→3 连续"| C3["Commit 完成<br/>B1 正式提交并执行"]
B4 -.->|"触发 B1 提交"| C3
style B1 fill:#f9d,stroke:#333
style C3 fill:#bfb,stroke:#333
上图展示了 Chained HotStuff 的核心提交规则。每个区块通过后续连续视图中积累的 QC 链逐步推进提交状态。B1 需要”等待”三个后续连续视图的 QC 链才能被提交,这就是”三轮投票分散在连续三个视图中完成”的含义。如果任何中间视图因为 Leader 故障被跳过,链条断裂,B1 的提交进度被重置,需要重新开始积累 QC 链。这也解释了为什么 Pacemaker 模块对系统吞吐至关重要——它必须尽快让系统稳定到一个有诚实 Leader 的连续视图序列中。
class ChainedHotStuff:
def __init__(self, id, n, f):
self.id = id
self.n = n
self.f = f
self.view = 0
self.generic_qc = None # 最新的 genericQC
self.locked_qc = None # 锁定的 QC(对应 2-chain)
self.b_exec = None # 最后执行的区块
def on_propose(self, cmd):
"""Leader 在当前视图提出新命令"""
# 创建新区块,指向 genericQC 对应的区块
block = Block(
cmd=cmd,
parent=self.generic_qc.block if self.generic_qc else GENESIS,
qc=self.generic_qc,
view=self.view
)
broadcast(ProposalMsg(block, self.view))
def on_receive_proposal(self, msg):
"""副本收到提案"""
block = msg.block
qc = block.qc # 提案中附带的 QC
# 更新 k-chain 状态
self.update(qc)
# 安全性检查
safe = (
self.extends(block, self.locked_qc.block) or
(qc is not None and qc.view > self.locked_qc.view)
)
if safe and self.valid(block):
vote = self.sign_share(block.hash, self.view)
send(get_leader(self.view), VoteMsg(vote))
def update(self, qc):
"""根据收到的 QC 更新 k-chain 状态"""
if qc is None:
return
b_prime = qc.block # 1-chain 头
b_double_prime = b_prime.qc.block # 2-chain 头(如果存在)
b_triple_prime = b_double_prime.qc.block if b_double_prime.qc else None # 3-chain 头
# 更新 genericQC(1-chain)
if qc.view > self.generic_qc_view():
self.generic_qc = qc
# 更新 lockedQC(2-chain):连续视图号检查
if (b_prime.view == b_double_prime.view + 1 and
qc.view > self.locked_qc_view()):
self.locked_qc = b_prime.qc
# 提交(3-chain):连续视图号检查
if (b_prime.view == b_double_prime.view + 1 and
b_double_prime.view == b_triple_prime.view + 1):
self.commit(b_triple_prime)
def commit(self, block):
"""提交区块及其所有祖先"""
if block.view > self.b_exec_view():
# 按顺序执行从 b_exec 到 block 的所有区块
for b in self.path(self.b_exec, block):
self.execute(b)
self.b_exec = block流水线的实际影响
Chained HotStuff 的流水线设计带来了几个实际好处:
吞吐量提升。虽然单个提案从提出到提交仍然需要三个视图的延迟(latency),但系统的吞吐量取决于每个视图能发起多少新提案——答案是每个视图一个。在基本 HotStuff 中,一个提案占用三个视图(三轮投票),在提交之前 Leader 不会发起新提案。在 Chained HotStuff 中,每个视图都可以发起新提案,吞吐量理论上提升三倍。
协议简化。基本 HotStuff 有三种消息类型(Prepare、Pre-commit、Commit),每种有细微不同的处理逻辑。Chained HotStuff 只有一种消息类型(Generic),大大简化了实现和验证。
自然的视图切换。在 Chained HotStuff 中,每个视图本身就是一轮投票。视图切换只是跳到下一个视图——新 Leader 带着自己收集到的最高 QC 发起新的提案。被跳过的视图不会形成 k-chain,所以对应的提案不会被提交。没有额外的视图切换协议,没有额外的消息类型。
延迟的代价。流水线设计有一个隐含的代价:如果视图切换发生,正在流水线中的提案可能需要重新开始。比如 Cmd₁ 在 View 1 完成了 Prepare,View 2 应该做 Pre-commit。但如果 View 2 的 Leader 超时了,View 3 的新 Leader 可能无法形成连续的 2-chain(因为 View 2 被跳过),Cmd₁ 的锁定就无法建立。在极端情况下——比如连续多个 Leader 超时——流水线可能不断被打断,导致没有提案能被提交。
这也是 Pacemaker 模块如此重要的原因:Pacemaker 需要确保在 GST 之后,系统能快速稳定到一个有诚实 Leader 的视图,让流水线恢复运转。
四、Diem(Libra)与 DiemBFT:从论文到工业实现
为什么 Facebook 选择了 HotStuff
2019 年 6 月,Facebook 发布了 Libra 白皮书,宣布将推出一种全球性的加密货币和支付系统。Libra 区块链的共识协议——LibraBFT(后来改名为 DiemBFT)——直接基于 HotStuff。
Facebook 选择 HotStuff 的原因可以从几个维度理解:
节点规模需求。Libra 最初计划由 Libra Association 的成员(约 100 个组织)各运行一个验证节点。PBFT 在 100 个节点的规模下视图切换的通信开销已经不可接受。HotStuff 的 O(n) 消息复杂度使得 100 节点规模的 BFT 共识成为可能。
确定性终结(deterministic finality)。与 Nakamoto 共识(比特币、以太坊 PoW)的概率性终结不同,HotStuff 提供确定性终结——一旦提交就不会被回滚。这对于支付系统至关重要:用户不会接受”你的转账有 99.99% 的概率成功”。
模块化设计。HotStuff 将安全性和活性分离,Pacemaker 可以独立设计和优化。这使得 Facebook 的工程团队可以在不修改核心安全逻辑的前提下,迭代改进系统的性能和可靠性。
协议简洁性。HotStuff 的协议描述可以用不到一页的伪代码完整表达。相比 PBFT 复杂的视图切换协议,HotStuff 更容易实现、测试和审计。对于一个面向全球用户的金融系统,协议的简洁性直接关系到安全审计的可行性。
DiemBFT 的迭代
DiemBFT 经历了四个主要版本(v1 到 v4),每个版本都在 HotStuff 的基础上做了工程优化:
DiemBFT v1(LibraBFT):基本上是 Chained HotStuff 的直接实现。使用轮转(round-robin)的方式选择每个视图的 Leader。Pacemaker 基于超时,使用指数退避策略。
DiemBFT v2:引入了两个关键优化——
- 声誉机制(reputation-based leader election):不再使用轮转选 Leader,而是根据节点最近的表现(是否及时提出提案、是否及时投票)来选择 Leader。表现差的节点被降低成为 Leader 的概率。这减少了因为低性能或恶意 Leader 导致的视图超时。
- 跳过无效 Leader:如果当前 Leader 在上一个视图中没有投票(被认为是不可靠的),直接跳到下一个 Leader,不需要等超时。
DiemBFT v3:进一步改进了 Pacemaker
的设计。引入了”最高 QC 同步”机制——在视图切换时,新 Leader
不仅收集 NEW-VIEW 消息中的
QC,还主动向其他副本请求它们可能持有的更高
QC。这减少了因为网络延迟导致 QC
丢失而引起的不必要的视图切换。
DiemBFT v4:增加了两轮(two-chain)提交规则的优化。在网络稳定的情况下(连续视图没有超时),只需要两轮投票就可以提交提案,减少了正常路径的延迟。这是通过引入一个额外的”超时证书”(Timeout Certificate,TC)来实现的——当视图超时时,副本广播超时消息,Leader 收集 2f+1 个超时消息形成 TC,TC 的存在允许后续的 Leader 在不满足 3-chain 连续性的情况下仍然安全地推进协议。
Diem 的结局与遗产
2022 年 1 月,Diem 项目被 Meta 正式放弃,相关资产被出售给 Silvergate Capital。项目的终结主要是监管原因,而非技术原因——美国监管机构对 Meta 发行全球性货币的风险表示了严重关切。
但 Diem 的技术遗产被两个后续项目继承:
Aptos:由前 Diem 团队成员创立,使用 DiemBFT v4 的改进版本(称为 Jolteon)。Jolteon 在正常路径上使用两轮 HotStuff,在视图切换时回退到三轮。Aptos 使用 Move 编程语言和 Move 虚拟机,这些都直接来自 Diem 项目。
Sui:同样由前 Diem 团队成员创立,但走了一条不同的路线。Sui 的共识协议 Narwhal-Bullshark 不再基于 HotStuff 的链式结构,而是采用了 DAG(Directed Acyclic Graph)的范式。我们在后面的章节会详细讨论这种演进。
Diem 项目最重要的贡献可能不是具体的代码或协议,而是它证明了 BFT 共识可以在工业级规模下工作。DiemBFT 的 Rust 实现被大量测试和优化,相关的工程经验(比如如何设计可测试的 Pacemaker、如何处理网络分区恢复后的状态同步)为整个 BFT 工程领域提供了宝贵的参考。
五、现代 BFT 协议的演进
HotStuff 的发表标志着 BFT 共识从”理论上可行但工程上痛苦”走向”理论优雅且工程可实现”的转折点。在此之后,BFT 协议的演进主要沿着几个方向展开。
Tendermint / CometBFT:PoS 区块链的 BFT 选择
Tendermint 由 Jae Kwon 在 2014 年提出,比 HotStuff 早了四年。它是第一个被广泛部署的 BFT 共识协议,也是 Cosmos 生态系统的核心组件。
Tendermint 的设计和 HotStuff 有很多相似之处,但也有关键差异:
两轮投票。Tendermint 使用两轮投票(Prevote 和 Precommit),而不是 HotStuff 的三轮。它通过一个不同的机制来保证安全性:Tendermint 要求副本在 Prevote 阶段锁定到某个提案后,只有在看到更高轮次的有效提案时才能解锁。这依赖于一个”锁定-解锁”(lock-unlock)规则,而不是 HotStuff 的三阶段 QC 链。
缺乏响应性。Tendermint 不具有 HotStuff 的响应性。在 Prevote 阶段之后,Leader 需要等待一个超时时间(timeoutPrecommit)才能进入下一轮。即使所有诚实节点都已经投票,Leader 也不能跳过这个等待。这是因为 Tendermint 的安全性证明依赖于同步假设——在超时期间,所有诚实节点的消息必须被传递。
O(n²) 通信。原始的 Tendermint 没有使用门限签名,而是在每一轮中让所有节点广播投票给所有其他节点。这使得通信复杂度为 O(n²)。后来的实现中引入了聚合签名来优化,但协议本身的设计没有像 HotStuff 那样从一开始就以线性复杂度为目标。
成熟的工程实现。Tendermint 的最大优势在于它的工程成熟度。Tendermint Core(后改名为 CometBFT)是一个经过多年生产验证的 BFT 引擎,支撑了 Cosmos Hub、Osmosis、dYdX 等数十个区块链项目。它的 ABCI(Application Blockchain Interface)设计将共识逻辑和应用逻辑解耦,使得开发者可以用任何语言编写区块链应用。
Tendermint vs HotStuff 对比:
Tendermint HotStuff
投票轮次: 2 轮 3 轮
消息复杂度: O(n²) O(n)
响应性: 否 是
视图切换: 独立协议 嵌入正常路径
锁定机制: lock-unlock QC 链
工程成熟度: 高(生产验证) 中(Diem 已终止)
Narwhal-Tusk 与 Bullshark:分离数据传播与共识排序
2022 年,George Danezis、Lefteris Kokoris-Kogias 等人提出了 Narwhal-Tusk 协议,带来了 BFT 共识设计的范式转变:将数据传播(data dissemination)和交易排序(transaction ordering)彻底分离。
传统 BFT 协议(包括 PBFT、HotStuff、Tendermint)中,Leader 需要同时完成两件事:
- 将交易数据传播给所有节点。
- 就交易的顺序达成共识。
这导致了一个性能瓶颈:Leader 是数据传播的单点,所有交易都要通过 Leader 广播。如果 Leader 的带宽有限,系统的吞吐量就被 Leader 的带宽所限制。
Narwhal 将数据传播从共识中剥离出来。每个节点独立地将交易打包成”批次”(batch),广播给其他节点,并收集 2f+1 个确认(形成”证书”,certificate)。这些证书通过 DAG(有向无环图)的方式链接起来——每个节点在每一轮创建一个包含交易批次和指向上一轮其他节点证书的区块。
Tusk(后来被 Bullshark 改进)是运行在 Narwhal DAG 之上的共识排序层。它不需要额外的消息交换——共识顺序直接从 DAG 的结构中推导出来。具体来说,Bullshark 使用以下规则:
- 每隔两轮选出一个”锚点”(anchor)节点。
- 如果一个锚点在下一轮中被 2f+1 个节点引用(即这些节点的区块包含指向锚点区块的指针),则这个锚点被”提交”。
- 被提交的锚点按因果关系排序它引用的所有区块。
这种设计的优势是显著的:
- 吞吐量:数据传播在所有节点之间并行进行,不再受限于单个 Leader 的带宽。
- 公平性:每个节点都有机会提交交易到 DAG 中,恶意 Leader 无法审查特定交易。
- 零消息开销的共识:共识排序层不需要额外的消息交换,直接复用 DAG 中已有的信息。
DAG-BFT 范式
Narwhal-Bullshark 开启了 DAG-BFT 的范式。在这种范式中,区块链的基础数据结构不再是链(chain),而是 DAG。每个节点在每一轮都可以出块,这些区块通过引用关系形成一个 DAG。共识协议的任务不再是”选出一个 Leader 来提出下一个区块”,而是”在 DAG 上确定一个全序”。
DAG-BFT 的主要协议包括:
DAG-Rider(2021):理论上第一个完全异步的 DAG-BFT 协议。使用随机化来绕过 FLP 不可能性。每个节点在每一轮广播一个包含交易和对前一轮区块引用的提案。共识通过”波”(wave)的方式进行——每四轮构成一个”波”,每个波通过一个局部币翻转(local coin flip)决定是否提交。
Tusk(2022):DAG-Rider 的实际化版本,降低了延迟。在乐观情况下(网络同步、Leader 诚实)只需要两轮就可以提交。
Bullshark(2022):Tusk 的改进版,在部分同步假设下进一步降低延迟。使用确定性的提交规则(不需要随机化),在乐观路径上只需两轮延迟。
Mysticeti(2024):由 Sui 团队提出,进一步优化了 Bullshark。减少了 DAG 中区块的引用数量,允许更灵活的出块策略(不必等到前一轮的所有证书都到齐才出块)。
DAG-BFT 的发展趋势表明,未来的 BFT 共识可能不再围绕”选 Leader → Leader 出块 → 投票提交”的范式,而是转向”所有节点并行出块 → 确定全序”的范式。HotStuff 解决了 PBFT 的通信复杂度问题,但保留了 Leader 瓶颈;DAG-BFT 则试图彻底消除 Leader 瓶颈。
异步 BFT 的趋势
传统 BFT 协议(PBFT、HotStuff、Tendermint)都假设部分同步网络模型——在某个未知的时间点 GST 之后,网络延迟有上界。在 GST 之前,协议保证安全性但不保证活性(可能一直无法提交新的提案)。
异步 BFT 协议则在纯异步网络模型下保证安全性和活性(通过随机化绕过 FLP 不可能性)。代表性的协议包括:
HoneyBadgerBFT(2016):第一个实用的异步 BFT 协议。使用异步公共子集(Asynchronous Common Subset,ACS)原语,让每个节点提出自己的交易集合,然后通过 ACS 协议确保所有诚实节点最终就相同的交易集合达成一致。
VABA(Validated Asynchronous Byzantine Agreement,2019):改进了 HoneyBadgerBFT 的效率,减少了密码学操作的开销。
DispersedLedger(2022):将数据传播和共识分离(类似 Narwhal 的思路),在异步环境下实现了高吞吐量。
异步 BFT 的主要优势是对网络条件的鲁棒性——即使网络出现任意延迟波动,协议仍然能保证最终提交。代价是需要使用随机化(通常通过门限签名实现的公共币 / common coin),且延迟通常高于部分同步协议。
六、BFT 在非区块链场景的应用
BFT 共识常常被等同于”区块链技术”,但它的应用范围远不止于此。任何需要在存在恶意参与者的环境中维持一致性的系统,都可能从 BFT 中获益。
防御内部恶意操作者
在传统的分布式系统中,我们通常假设节点只会发生崩溃故障(crash fault)——节点要么正常工作,要么停止响应。这是 Raft、Paxos、ZooKeeper 等 CFT(Crash Fault Tolerant)协议的基础假设。
但现实中,节点可能被黑客入侵、被恶意内部人员操控、或者运行了有 bug 的软件(bug 的效果有时和恶意行为无法区分)。在这些场景下,CFT 协议无法提供安全保障——一个被入侵的 Leader 可以向不同的 Follower 发送不同的提案,导致系统状态分裂。
BFT 协议可以防御这类攻击。具体的应用场景包括:
多云部署的关键服务。一个金融机构在 AWS、Azure、GCP 三个云上部署了交易清算系统。如果其中一个云提供商被入侵(或者因为 bug 导致行为异常),CFT 协议可能无法检测到不一致的行为。使用 BFT 协议(如 HotStuff),即使一个云上的节点被入侵并发送恶意消息,只要其他两个云上的节点是诚实的(n=3f+1=4,f=1),系统仍然能正确运行。
权限管理系统。大型组织的权限管理系统是高价值攻击目标——如果攻击者能修改权限数据库,就能获取任意系统的访问权限。使用 BFT 复制可以确保即使部分权限管理节点被入侵,攻击者也无法单方面修改权限记录。
审计日志。不可篡改的审计日志对于监管合规至关重要。如果审计日志存储在单个节点上,恶意管理员可以删除或修改日志来掩盖自己的行为。使用 BFT 复制的审计日志系统可以确保即使 f 个节点被入侵,日志的完整性仍然得到保证。
BFT 在关键基础设施中的应用
航空航天。飞行控制系统使用冗余计算(redundant computing)来容忍硬件故障。传统方法是使用三模冗余(Triple Modular Redundancy,TMR)——三个独立的计算单元执行相同的计算,取多数结果。这本质上是一个 n=3、f=1 的 BFT 系统。
现代航空系统正在探索更复杂的 BFT 方案。NASA 的 Malicious Fault Tolerance (MFT) 研究项目评估了 PBFT 类协议在飞行控制中的应用可行性。挑战在于航空系统对延迟有严格的实时要求(通常在毫秒级),这与 BFT 协议多轮通信的延迟特性存在张力。
医疗系统。药物输注泵、心脏起搏器等植入式医疗设备的控制逻辑如果被恶意篡改,后果是致命的。BFT 协议可以用来保护这些设备的固件更新过程——只有当多个独立的审核节点达成共识后,固件更新才能被应用。
金融系统。证券交易所的撮合引擎(matching engine)是金融市场的核心基础设施。如果撮合引擎的行为被恶意修改(比如优先处理某些订单),将导致严重的市场操纵。一些交易所已经开始探索使用 BFT 复制来保护撮合引擎的完整性。
BFT vs CFT 的成本收益分析
在决定是否使用 BFT 时,工程团队需要权衡以下因素:
性能开销。BFT 协议通常比 CFT 协议有更高的延迟和更低的吞吐量。HotStuff 需要三轮投票(3 RTT),而 Raft 在正常路径上只需要一轮(1 RTT)。门限签名的计算开销也高于普通签名。
节点数量要求。BFT 需要 n >= 3f+1 个节点来容忍 f 个拜占庭故障,而 CFT 只需要 n >= 2f+1。这意味着在相同容错能力下,BFT 需要更多的节点。
| 容错数 f | CFT 最少节点数 | BFT 最少节点数 | BFT 额外开销 |
|---|---|---|---|
| 1 | 3 | 4 | +33% |
| 2 | 5 | 7 | +40% |
| 3 | 7 | 10 | +43% |
| 5 | 11 | 16 | +45% |
威胁模型的合理性。BFT 假设最多 f 个节点可以是拜占庭的。如果实际环境中拜占庭故障极其罕见(比如全部节点在同一个可信数据中心内),BFT 的额外开销可能不值得。但如果节点分布在不同的信任域(不同组织、不同国家、不同云提供商),BFT 的投资就更合理。
复杂性成本。BFT 协议的实现和调试比 CFT 协议复杂得多。门限签名的密钥管理、Pacemaker 的设计调优、拜占庭场景的测试覆盖——这些都是额外的工程成本。如果团队没有足够的专业知识,BFT 的部署可能引入更多风险,而不是降低风险。
实践建议:
- 大多数企业应用:使用 Raft 或 etcd/ZooKeeper 等 CFT 方案。拜占庭故障在受控环境中极其罕见,CFT 的简单性和性能优势更有价值。
- 跨组织协作:当参与者之间互不完全信任时(如联盟链、跨机构结算),BFT 是合理的选择。
- 高价值安全关键系统:当被攻击的后果极其严重时(金融核心系统、关键基础设施),BFT 提供的额外防护值得投资。
- 合规要求:某些行业的监管要求可能隐含或明确要求 BFT 级别的容错。
七、从 HotStuff 到未来:BFT 共识的演化方向
回顾 BFT 共识的演化历程,可以看到一条清晰的优化路径:
PBFT (1999)
│ 问题:O(n²) 视图切换
▼
Tendermint (2014)
│ 改进:实用化 BFT,PoS 区块链
│ 局限:仍然 O(n²),无响应性
▼
HotStuff (2018)
│ 突破:O(n) 消息复杂度,响应性
│ 局限:三轮延迟,Leader 瓶颈
▼
Chained HotStuff / DiemBFT (2019)
│ 优化:流水线化,工业实现
│ 局限:仍然依赖 Leader
▼
Narwhal-Bullshark (2022)
│ 范式转变:DAG-BFT,无 Leader 瓶颈
│ 局限:延迟较高,实现复杂
▼
Mysticeti / Shoal++ (2024)
│ 继续优化:降低 DAG-BFT 延迟
▼
?
几个值得关注的演化方向:
自适应 BFT
现有的 BFT 协议在设计时需要预先设定容错参数 f。一个 n=3f+1=100 的系统可以容忍 f=33 个拜占庭节点,但如果实际只有 f=1 个拜占庭节点,其余 99 个都是诚实的,系统仍然需要三轮投票。
自适应 BFT 协议尝试根据实际的故障情况调整协议行为:在没有拜占庭节点时,行为接近 CFT 协议(一轮投票);检测到拜占庭行为时,切换到完整的 BFT 模式。这类协议的挑战在于如何安全地检测拜占庭行为——如果检测机制本身可以被欺骗,安全性就无法保证。
Flexible BFT(2019)是这个方向的代表工作,它允许不同的客户端对故障模型有不同的假设——一些客户端假设只有崩溃故障,另一些假设可能有拜占庭故障——并在同一个协议中同时服务这两类客户端。
带问责的 BFT
传统 BFT 假设拜占庭节点的行为不可预测且不受约束。但在许多实际系统中(尤其是联盟链),节点的运营者是有身份的、可追责的。
带问责的 BFT(Accountable BFT)协议不仅要容忍拜占庭行为,还要在拜占庭行为发生时生成可验证的证据(proof of misbehavior)。这些证据可以用来惩罚违规节点(比如在 PoS 系统中罚没其质押)或者触发法律追责。
PeerReview(2007)是这个方向的早期工作。以太坊 2.0 的 Casper FFG 也内置了可问责性机制——如果验证者投了冲突的票,其他节点可以生成”slashing 证据”来罚没该验证者的质押以太币。
轻客户端验证
BFT 协议的一个实际限制是验证成本。在区块链场景中,轻客户端(如手机钱包)没有能力存储完整的区块链数据或参与共识过程。它需要一种方式来验证某个交易确实被共识提交了。
HotStuff 的 QC(Quorum Certificate)天然适合轻客户端验证:轻客户端只需要验证一个门限签名就可以确认某个区块已经被 2f+1 个节点投票。相比之下,验证 PBFT 的提交证明需要验证 2f+1 个独立签名。
这也是 HotStuff 在区块链领域受到欢迎的原因之一——门限签名不仅优化了共识的通信复杂度,还优化了外部验证者的验证成本。
异构信任模型
传统 BFT 假设全局统一的信任模型——所有节点对”谁可能是拜占庭的”有相同的假设。但在现实中,不同的参与者可能有不同的信任关系。比如,银行 A 信任银行 B 但不信任银行 C,而银行 B 可能同时信任 A 和 C。
Stellar 共识协议(SCP)使用联邦拜占庭协议(Federated Byzantine Agreement,FBA)来处理这种异构信任模型。在 FBA 中,每个节点自主选择自己的”信任切片”(quorum slice),共识通过重叠的信任切片来传播。
这种模型更接近现实世界的信任结构,但也带来了新的理论挑战:在异构信任模型下,安全性和活性的保证取决于信任图的拓扑结构,而不是简单的 f < n/3 阈值。
八、总结
HotStuff 的核心贡献可以归结为三点:
- 线性消息复杂度:通过门限签名将每个阶段的通信量从 O(n²) 压缩到 O(n),使得 BFT 共识在上百节点的规模下成为可能。
- 统一的协议路径:视图切换不再需要独立的协议,而是自然嵌入正常的三阶段投票流程中。这不仅简化了实现,还消除了视图切换作为性能瓶颈的问题。
- 可流水线化:Chained HotStuff 将三轮投票拆分成每个视图一轮 Generic 投票,使得多个提案可以在流水线中并行推进。
从更宏观的视角看,HotStuff 是 BFT 共识从”学术好奇心”走向”工程实践”的关键里程碑。它的设计哲学——用密码学工具(门限签名)换取通信效率,用协议的对称性简化实现和证明——深刻影响了后续的 BFT 协议设计。
但 HotStuff 也不是终点。DAG-BFT 范式正在尝试彻底消除 Leader 瓶颈,异步 BFT 协议正在扩展可用的网络模型,自适应 BFT 和问责 BFT 正在让 BFT 协议更贴近实际需求。BFT 共识的研究远未结束——只要存在互不信任的参与者需要合作,就需要 BFT 共识来提供安全保障。
对于工程师来说,理解 HotStuff 的价值不仅在于可能需要直接使用它,更在于它提供了一种思考分布式系统安全性的框架:你的系统能容忍什么样的故障?你愿意为更强的容错付出多少性能代价?当安全性和活性不可兼得时,你选择牺牲哪个? 这些问题的答案不在协议本身,而在你的业务需求和威胁模型中。
延伸阅读:
- HotStuff: BFT Consensus with Linearity and Responsiveness – HotStuff 原始论文,建议从 Section 4 的协议描述开始阅读
- Tendermint: Byzantine Fault Tolerance in the Age of Blockchains – Tendermint 的学位论文,详细描述了协议的设计和正确性证明
- Narwhal and Tusk: A DAG-based Mempool and Efficient BFT Consensus – DAG-BFT 的开创性论文
- Bullshark: DAG BFT Protocols Made Practical – Bullshark 对 DAG-BFT 的实际化改进
- DiemBFT v4: State Machine Replication in the Diem Blockchain – DiemBFT 的完整技术报告
参考资料:
- Yin, M., Malkhi, D., Reiter, M.K., Gueta, G.G., & Abraham, I. (2019). HotStuff: BFT Consensus with Linearity and Responsiveness. PODC 2019.
- Castro, M. & Liskov, B. (1999). Practical Byzantine Fault Tolerance. OSDI 1999.
- Buchman, E., Kwon, J., & Milosevic, Z. (2018). The Latest Gossip on BFT Consensus. arXiv:1807.04938.
- Danezis, G., Kokoris-Kogias, L., Sonnino, A., & Spiegelman, A. (2022). Narwhal and Tusk: A DAG-based Mempool and Efficient BFT Consensus. EuroSys 2022.
- Spiegelman, A., Giridharan, N., Sonnino, A., & Kokoris-Kogias, L. (2022). Bullshark: DAG BFT Protocols Made Practical. CCS 2022.
- Team Diem. (2021). DiemBFT v4: State Machine Replication in the Diem Blockchain. Diem Technical Report.
- Miller, A., Xia, Y., Croman, K., Shi, E., & Song, D. (2016). The Honey Badger of BFT Protocols. CCS 2016.
- Gelashvili, R., Kokoris-Kogias, L., Sonnino, A., Spiegelman, A., & Xiang, Z. (2023). Jolteon and Ditto: Network-Adaptive Efficient Consensus with Asynchronous Fallback. FC 2022.
- Babel, K., Daian, P., Kelkar, M., & Juels, A. (2023). Mysticeti: Low-Latency DAG Consensus with Fast Commit Path. arXiv:2310.14821.
- Malkhi, D. & Nayak, K. (2019). Flexible Byzantine Fault Tolerance. CCS 2019.
上一篇:Viewstamped Replication 与 PBFT:从 CFT 到 BFT 下一篇:共识协议的工程权衡:Raft vs Multi-Paxos vs EPaxos 实测对比
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【分布式系统百科】Viewstamped Replication 与 PBFT:从 CFT 到 BFT
从 Oki 和 Liskov 1988 年提出的 Viewstamped Replication,到 Castro 和 Liskov 1999 年的 PBFT,共识协议经历了从崩溃容错到拜占庭容错的质变。本文深入拆解 VR 的正常操作、视图切换与恢复协议,对比 PBFT 的三阶段提交、O(n²) 消息复杂度与水位机制,分析从 CFT 到 BFT 的本质代价。
【分布式系统百科】ZooKeeper 内核:从 ZAB 协议到分布式协调实践
深入拆解 ZooKeeper 的核心机制:ZAB 协议的三阶段流程、ZNode 数据模型、Watch 一次性通知、会话管理,以及分布式锁、Leader 选举、配置管理等典型用法。分析惊群效应等已知问题,并梳理 ZooKeeper 在 Kafka、HBase、Hadoop 生态中的角色。
【分布式系统百科】共识协议的工程权衡:Raft vs Multi-Paxos vs EPaxos 实测对比
从性能基准、选型决策、隐藏成本三个维度,系统对比 Raft、Multi-Paxos、EPaxos 三大共识协议在工程实践中的真实表现,帮助架构师做出有据可依的选型决策。
【分布式系统百科】分布式系统模型:你的假设决定你的命运
分布式系统的正确性证明和协议设计都建立在系统模型之上。同步还是异步?崩溃还是拜占庭?这些看似学术的分类,直接决定了你能用什么协议、不能用什么协议。本文拆解通信模型、故障模型和进程模型三个维度,把 Paxos、Raft、PBFT、Bitcoin 放回它们各自的模型空间。