跳至正文
来两杯美式
返回

拜占庭将军问题:两忠一叛、口信与签名消息之解

By 来两杯美式
发布于

这是 2022 年 5 月整理的拜占庭将军问题笔记。彼时区块链与联盟链正热,PBFT、POW 频繁出现在面试与技术讨论中。笔记从兰伯特的经典论文出发,用”战国六国攻秦”的故事层层深入,整理到口信消息型方案(两轮协商)处截断,本文补全了后续推导与结论。

拜占庭容错(BFT)

拜占庭错误是莱斯利·兰伯特在《拜占庭将军问题》中提出的一个错误模型,描述了一个完全不可信的场景:除了存在故障行为,还存在恶意行为(节点故意发送错误消息、说谎、勾结)。

拜占庭容错(Byzantine Fault Tolerance,BFT):指能容忍拜占庭错误——即使存在恶意节点,系统依然能达成正确共识。

非拜占庭容错(CFT)

非拜占庭容错,又叫故障容错(Crash Fault Tolerance,CFT),解决的是分布式系统中存在故障、但不存在恶意节点的共识问题,比如进程崩溃、服务器硬件故障等。

对比项CFT(故障容错)BFT(拜占庭容错)
错误模型节点崩溃、网络分区恶意节点,故意作恶
可信假设节点诚实,只是会挂节点不可信,可能说谎
典型环境可信环境(企业内网)不可信环境(公有链)
常见算法2PC、TCC、Paxos、ZAB、Raft、Gossip、Quorum NWRPOW、PBFT

核心区别:在可信环境(比如企业内网)中,系统具有故障容错能力就可以了;而在不可信的环境(比如有人作恶)中,系统需要具备拜占庭容错能力。

共识与一致性

很多同学经常误解的一个点:将 Consensus(共识)当成了 Consistency(一致性),把 Paxos、Raft 称为”一致性算法”。

其实 Paxos 和 Raft 是共识算法。之所以出现这个问题,是因为在很多中文文章中,将 Consensus 和 Consistency 都翻译成了”一致性”,其实这样是不合适的——共识和一致性是两个完全不同的概念

概念定义
共识(Consensus)各节点就指定值(Value)达成共识,而且达成共识后的值,就不再改变了
一致性(Consistency)写操作完成后,能否从各节点上读到最新写入的数据:立即能读到 = 强一致性;最终能读到 = 最终一致性

一句话:共识解决的是”大家选同一个值”,一致性解决的是”读到的数据是不是最新”

两忠一叛

战国时期,齐、楚、燕、韩、赵、魏、秦七雄并立,后来秦国的势力不断强大,成了东方六国的共同威胁。于是六国决定联合抗秦。苏秦作为合纵长,挂六国相印,带六国军队叩关函谷,驻军秦国边境。因为各国军队分别驻扎在不同地方,只能通过信使互相联系,苏秦面临一个严峻的问题:如何统一大家的作战计划?

为了便于理解,先假设只有 3 个国家攻打秦国:齐、楚、燕。秦国很强大,只有半数以上的将军参与进攻才能击败敌人。将军们通过信使传递消息,协商一致后在同一时间点发动进攻。

正常情况:少数服从多数

有一天,三位将军讨论明天是进攻还是撤退,按照”少数服从多数”的原则投票表决,两个人意见一致即可:

那么按照原则,齐也会进攻。最终,3 支军队同时进攻,大败秦军。

齐楚燕三将军正常通信:两票进攻一票撤退,最终统一进攻

叛徒出现:计划不一致

问题来了:一旦有人在暗通秦国,就会出现作战计划不一致的情况

正常时,齐向楚、燕分别发送”撤退”,燕向齐和楚发送”进攻”。撤退:进攻 = 1:1,无论楚投进攻还是撤退,都会成为 2:1,还是能形成一致方案。

但是,楚这个叛徒在暗中配合秦国,让信使向齐发送”撤退”,向燕发送”进攻”:

按照”少数服从多数”的原则,就会出现燕单独进攻秦军——最后寡不敌众,被秦军所灭。

叛将楚发送误导消息:对齐说撤退、对燕说进攻,导致两忠将意见分裂

这就是”两忠一叛”难题:叛将楚通过发送误导信息,非常轻松地干扰了齐和燕的作战计划,导致这两位忠诚将军被秦军逐一击败。

关键洞察:一个叛徒,就能让两个忠诚将军无法达成共识。诚实的将军们各自”少数服从多数”,但因为各自收到的消息集合不同,各自算出不同的结果——这正是拜占庭问题的本质:每个节点基于不同的局部信息做决策,恶意节点可以放大这种分歧

解决办法一:口信消息型拜占庭问题之解(OM 算法)

核心思想

  1. 增加将军数量:三位将军都分拨一部分军队,由苏秦率领,苏秦参与作战计划讨论并执行作战指令。这样,3 位将军的作战讨论就变为 4 位将军的作战讨论,增加了讨论中忠诚将军的数量。
  2. 预设默认命令:约定如果没有收到命令,就执行预设的默认命令,比如”撤退”。
  3. 两轮协商:约定进行两轮作战信息协商,而不是一轮。

为什么需要两轮协商

第一轮:每位将军把自己的作战决定(进攻/撤退)发给其他所有将军。

第二轮:每位将军把自己第一轮收到的消息转发给其他所有将军——目的是让每个忠诚将军都掌握”全局视角”

第一轮:A 把自己的决定发给 B、C、D
第二轮:A 把"我收到 B 说进攻、C 说撤退、D 说进攻"转发给所有人
        —— 这样即使有人撒谎,忠诚将军也能交叉验证

没有第二轮会怎样? 一轮通信下,每个将军只信任自己直接收到的消息。叛徒楚可以对齐说”撤退”、对燕说”进攻”、对苏秦说”进攻”,三个忠诚将军收到的是三个不同的消息集,各自投票得出不同结论——共识仍然无法达成。

有了第二轮后:苏秦能告诉齐”楚对我说的是进攻”,齐就能发现楚对我说撤退、对苏秦说进攻,楚在撒谎,从而排除楚的干扰。

理论结论:3m + 1

兰伯特证明了:在口信消息型(Oral Messages)模型中,要容忍 m 个叛徒,将军总数 n 必须满足 n ≥ 3m + 1

叛徒数 m最少将军数 n(≥ 3m+1)可容忍的最坏比例
141/4
272/7
3103/10

为什么 3 个将军不行? 因为 3 个将军(含 1 个叛徒)时,忠诚将军只有 2 个,无法形成”多数”可信参考——叛徒对两人说的不同,两人无法互相证明谁在撒谎。而 4 个将军(1 叛 3 忠)时,忠诚将军可以通过第二轮交叉验证排除叛徒。

这正是笔记中”把 3 位将军变 4 位”的数学本质:多一个忠诚将军,就多一个交叉验证的参照系

解决办法二:签名消息型拜占庭问题之解(SM 算法)

口信消息型中,叛徒可以谎称自己收到了某些消息(消息不可验证)。如果引入数字签名,消息不可篡改、不可抵赖,容错能力会显著提升。

签名消息型(Signed Messages):将军的消息带上不可伪造的签名,收到消息的人可以验证”这条消息确实是某将军发的、且未被篡改”。

理论结论:在签名消息模型中,只要 n ≥ 2m + 1 就能容忍 m 个叛徒(允许 n = m + 2 即可达成一致),而且算法复杂度更低、轮数更少。

模型最少节点数消息可验证
口信消息(OM)3m + 1不可验证,只能靠多数投票
签名消息(SM)2m + 1可验证,签名不可伪造

直观理解:口信消息只能靠”人多力量大”来对冲谎言;签名消息直接让谎言无法伪造,所以需要的”冗余节点”更少。

现实中的应用

算法类型应用场景
POW(工作量证明)拜占庭容错比特币等公有链:以算力投票,容忍恶意节点
PBFT(实用拜占庭容错)拜占庭容错联盟链(Fabric 等):节点少、效率高
Raft / Paxos / ZAB故障容错企业内网:Etcd、Kafka、ZooKeeper 等,假设节点诚实
2PC / TCC故障容错分布式事务,假设节点诚实

选型原则:可信内网用 CFT 算法(Raft/Paxos,简单高效);公开不可信环境用 BFT 算法(POW/PBFT,容忍作恶)。

小结


分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.CAP 与 BASE:分布式系统的理论基石
  2. 02.拜占庭将军问题:两忠一叛、口信与签名消息之解
  3. 03.ZooKeeper 原理与实践:数据模型、Watcher 与 ZAB 协议
  4. 04.分布式事务五种方案:消息驱动 / XA / TCC / 本地消息表 / Saga
  5. 05.微服务限流与容错:雪崩效应、熔断器与三种限流算法

上一篇
CSS 基础(一):元素类型与 display 属性
下一篇
微服务限流与容错:雪崩效应、熔断器与三种限流算法