Skip to content

Latest commit

 

History

History
105 lines (58 loc) · 11.4 KB

consensus.byzantine.generals.md

File metadata and controls

105 lines (58 loc) · 11.4 KB

共识算法之:拜占庭将军问题

拜占庭将军问题是什么

拜占庭将军问题是由Leslie Lamport在1982年提出,被称为The Byzantine Generals Problem或者Byzantine Failure。这个问题是Lamport在论文中抽象出来的著名例子,用来描述分布式系统一致性问题(Distributed Consensus)。其核心描述是在军队中可能有叛徒的情况下保证进攻一致,并由此引申到计算领域,发展成为一种容错理论。

一个简易的描述如下:

拜占庭帝国,即中世纪的东罗马帝国,拥有巨大的财富,周围10个邻邦垂诞已久。但拜占庭高墙耸立,固若金汤,没有一个单独的邻邦能够成功入侵。任何单个邻邦入侵都会失败,同时也有可能自身被其他9个邻邦入侵。拜占庭帝国防御能力如此之强,至少要有十个邻邦中的一半以上同时进攻,才有可能攻破。然而,如果其中的一个或者几个邻邦事先答应好一起进攻,但实际过程出现背叛,那么入侵者可能都会被歼灭。于是每一方都小心行事,不敢轻易相信邻国。这就是拜占庭将军问题。

需要明确的是,在拜占庭将军问题中,并不去考虑通信兵是否会被截获或无法传达信息等问题,即消息传递的信道是可靠的。Lamport已经证明了在消息可能丢失的不可靠信道上试图通过消息传递的方式达到一致性是不可能的。所以在研究拜占庭将军问题的时候,我们已经假定了信道是可靠的,并在这个前提下,去做一致性和容错性相关研究。

在这个问题里,各邻国最重要的事情是:所有将军如何能够达成共识去攻打拜占庭帝国。这些将军需要实现某一个统一的目标,一致进攻或者一致撤退,但是单独行动却又可能面临失败,所以必须达成共识,一致合作。由于叛徒的存在,将军们缺乏达成一致的有效途径。这里的“一致性”才是拜占庭将军问题需要探讨的内容,假如本来叛徒数量就已经多到了问题无解的地步,那么这个就是“反叛”的问题了。同时我们的目标是忠诚的将军能够达成一致,对于这些忠诚的将军来说,进攻或者撤退都是可以的,只要他们能够达成一致就可以。

但是光靠“一致”就可以解决问题了吗?仔细考虑一下,如果万事俱备,客观上每个忠诚的将军只要进攻了就一定能够胜利,但是却因为叛徒的存在他们都“一致的”没有进攻;反之,在条件不利的情况下,将军们不应该进攻,但是却因为叛徒的存在所有人都“一致的”进攻了。

从这个分析可以发现,只有“一致性”是不足以解决拜占庭将军问题的,还需要有一个“正确性”的要求。这个要求是值得思考的,因为如果客观来看或许会有“绝对正确的”判断,但是针对每一个将军,大家的判断或许都不相同,我们如何定义“正确”呢?或许可以简单地说,正确就是每个忠诚的将军都正确的表达了自己的意思,不会因为叛徒让别的将军认为忠诚的将军是叛徒而不采用他传达的消息。

在这些将军里面,如果出现了叛徒,情况就会变得异常复杂,可能会出现如下的问题:

  • 叛徒可能欺骗某些将军自己将采取进攻行动
  • 叛徒可能怂恿其他将军行动
  • 叛徒可能迷惑其他将军,使他们接受不一致的信息,从而感到迷惑

至此,我们将拜占庭将军问题简化成了,所有忠诚的将军都能够让别的将军接收到自己的真实意图,并最终一致行动;而形式化的描述就是,“一致性”与“正确性”。

如果将问题发散一下,可以发现针对一致性和正确性的算法并不要求命令必须是“进攻/撤退”,而可以是“发送消息A/发送消息b”等等,这意味着拜占庭将军问题算法可以为多种分布式系统提供启发,比如火车票售票系统等。

这个问题说到底是一个关于一致性和正确性的算法问题,这个算法针对的是忠诚的将军,因为叛徒可以做出任何超出约定的判断。我们就是要在有叛徒的干扰下,找到一个抗干扰的算法。要解决这个算法问题,我们需要将形式化要求具体化。

问题分析

我们来分析一下几种具体的情况:

先看在没有叛徒情况下。假如一个将军A提一个进攻提议(如:后天下午2点进攻,你同意吗?),由通信兵分别告诉其他的将军,如果非常幸运,他收到了其他至少5位将军的同意,发起进攻。如果不幸,其他的将军也在此时发出不同的进攻提议(如:明天上午8点进攻,你同意吗?),甚至提出相反的提议(如:今天半夜11点撤退)。由于时间上的差异,不同的将军收到(并认可)的进攻提议可能是不一样的,这时可能出现A提议有2个支持者,B提议有5个支持者,C提议有2个支持者等等。

如果这些将军中有叛徒的话,一个叛徒可能会向不同的将军发出不同的进攻提议(通知A明天上午7点进攻,通知B明天下午2点进攻等等),一个叛徒也可能同意多个进攻提议(即同意下午1点进攻,又同意上午9�点进攻)。在这种情况下,整体上将军们的进攻计划就彻底乱套了。叛徒发送前后不一致的进攻提议,被称为“拜占庭错误”,而能够处理拜占庭错误的这种容错性称为“Byzantine Fault Tolerance”,简称为BFT。

最简模型

假设只有3个人 A、B、C,假设三人中其中一个是叛徒。当A发出进攻命令时,B如果是叛徒,他可能告诉C,他收到的是“撤退”的命令。这时C收到一个“进攻”,一个“撤退“,于是C被信息迷惑,而无所适从。

如果A是叛徒。他告诉B“进攻”,告诉C“撤退”。当C告诉B,他收到“撤退”命令时,B由于收到了A的“进攻”的命令,而无法与C保持一致。

正由于上述原因,在只有三个角色的系统中,只要有一个是叛徒,即叛徒数等于1/3,拜占庭问题便不可解。

如果叛徒的数量大于或等于1/3,拜占庭问题不可解。

Lamport在论文中提出了口头信息方案和书面协议两个方案。

解决方案一:用口头信息

所谓口头信息,就是将军们使用信使传递口头信息,要满足以下三个条件:

  • 被发送的消息能够被信使正确传递
  • 接受者知道消息是哪个将军发的
  • 能够知道谁没有发送消息

也就是说,信道可信,消息来源可知。口头协议的算法很简单,如果其中一个将军,比如A 发布消息出去,其他9个将军都接收到将军A的消息,然后这9个将军也分别转告给其他的将军,每个将军都是信息的转达者。一轮下来,每个将军手上都会有10个信息(进攻或者撤退)。如果有叛徒的话,那信息可能包含有进攻或者不进攻这样的不一致消息。每个将军相当于手里有一本消息的账本,该怎么决策呢?如果有一半以上的人说进攻,那么采取进攻行动就是能成功的,所以这时即便有叛徒,只要听大部分人的,少数服从多数来行动即是有利的。

这种口头协议的算法也存在明显的缺点:口头协议并不会告知消息的上一个来源是谁,也就是消息不可追根溯源,出现信息不一致也很难找到叛徒在哪。

解决方案二:用书面信息

一种不同于使用口信来传递信息的方法,是使用书信,并且在书信上都要签上将军们的印章,相比于口头协定,又多了两个条件:

  • 将军使用印章对书信签名,这个签名就确定了将军的身份,不可伪造,篡改签名可被发现
  • 收到书信的任何其他将军都可以验证签名的有效性

书面信息的本质就是引入了“签名系统”,这使得所有消息都可追本溯源。只要采用了书面信息,忠诚的将军就可以达到一致。现在这种方式下,将军们按照以下方式发送消息:

  • 每位将军分别给其他将军发送书信,并在书信上附上自己的签名
  • 其他将军收到书信后,附上自己的签名后再发给所有其他将军
  • 每位将军根据自己收到的书信进行决断

书面信息貌似完美地解决了拜占庭将军问题,但是不得不说实际上的解决是建立在诸多限制条件下的。在现实的分布式系统中,我们可能会遇到各种各样的问题。例如:

  • 没有考虑信使传递消息的时延问题
  • 真正可信的签名体系很难实现,也很难避免签名造假
  • 将军们的印章是国王颁发的,难以褪去中心化机构的影响

另外,如果每个将军都向其他将军派遣信使表达自己的观点,那么一轮信息交流需要90次的信使往来,而且每个将军的观点都可能不一致。在异步通信模式下,这种模式几乎很难达成一致。而且让所有将军都相信中心化的国王签发的印章的真实性,实际上也违反了整个问题的前提,那就是将军们互相不信任,即便是有国王的存在。

解决思路

拜占庭将军问题之所以难解,一个重要的原因就是在任意时间,系统中可能会存在多个提案,也就是问题描述中的每个将军都可以提出自己的进攻或者撤退意见。这样一来,很难在一个时刻对结果进行一致性确认。中本聪创新性地引入PoW共识算法,解决了两个困难。

  • 限制一段时间内提案的个数,只有拥有对应权限的节点(将军)可以发起提案。在比特币里,是通过随机哈希计算分配权限的,谁第一个计算出对应的答案,谁才有权限发起提案。这种方案就极大的减少了在某一个时间点的提案数量,整体上减少了提案太多的混乱状况
  • 区块链技术使用非对称加密算法对节点间的消息传递提供签名技术支持,每个节点(将军)都有属于自己的秘钥(公钥私钥),唯一标识节点身份。使用非对称加密算法传递消息,能够保证消息传递的私密性,而且消息签名不可抵赖,不可篡改
  • 使用公钥加密的数据,使用公钥对应的私钥进行解密;使用私钥进行签名的消息,只需要使用私钥对应的公钥验证签名即可。比如,A将军想要给B将军发送消息,那么只需要使用B将军的公钥加密消息,B将军收到消息后使用自己的私钥解密消息即可。而如果A将军想申明自己的身份,只需要将消息使用自己的私钥进行签名即可,B将军收到消息后就可以使用A将军的公钥验证消息的来源。这样就将一个不信任的网络变成了信任网络

在区块链这样的分布式网络中,我们还是以将军为例:

  • 每位将军都保留一份历史消息账本
  • 因为每份消息都是进行过签名的,所以如果有背叛的将军,我们很容易就能找出来; 在一轮共识的流程里,即便有消息不一致,但是只要背叛将军的个数少于1/3,这一轮共识就能达成

小结

到了这里,我们可以很清楚地知道,区块链和拜占庭将军问题的共性所在,都是决定由谁发起消息(提案);然后在消息的传递过程中都需要明确是谁在传递消息;以及如何在分布式系统中达成一致的问题。