区块链的最早应用是比特币,而比特币的概念最初由中本聪在2009年提出,根据中本聪的思路设计发布的开源软件以及建构其上的P2P网络构成比特币系统。区块链发源于比特币技术。在中本聪的白皮书里,“区块”和“链”是分开谈论的。但大概在 2014年的时候,金融科技界和 IT 界专家将区块链这样一种比特币支撑技术抽离出来,单独立论,才有了区块链。
了解过比币和区块链的人,多少都听说过拜占庭将军问题,区块链正是解决了拜占庭将军问题。
一、拜占庭将军问题是什么
关于拜占庭将军问题,一个简易的非正式描述如下:
正如前文所说,拜占庭将军问题和两军问题实质是不一样的。国内大量解释拜占庭将军问题的文章将两者混为一谈,其实是混淆了两个问题的实质,由此造成了许多误解。这两个问题看起来的确有点相似,但是问题的前提和研究方向都截然不同。
如图1所示,白军驻扎在沟渠里,蓝军则分散在沟渠两边。白军比任何一支蓝军都更为强大,但是蓝军若能同时合力进攻则能够打败白军。他们不能够远程的沟通,只能派遣通信兵穿过沟渠去通知对方蓝军协商进攻时间。是否存在一个能使蓝军必胜的通信协议,这就是两军问题。
看到这里您可能发现两军问题和拜占庭将军问题有一定的相似性,但我们必须注意的是,通信兵得经过敌人的沟渠,在这过程中他可能被捕,也就是说,两军问题中信道是不可靠的,并且其中没有叛徒之说,这就是两军问题和拜占庭将军问题的根本性不同。由此可见,大量混淆了拜占庭将军问题和两军问题的文章并没有充分理解两者。
两军问题的根本问题在于信道的不可靠,反过来说,如果传递消息的信道是可靠的,两军问题可解。然而,并不存在这样一种信道,所以两军问题在经典情境下是不可解的,为什么呢?
倘若1号蓝军(简称1)向2号蓝军(简称2)派出了通信兵,若1要知道2是否收到了自己的信息,1必须要求2给自己传输一个回执,说“你的信息我已经收到了,我同意你提议的明天早上10点9分准时进攻”。
然而,就算2已经送出了这条信息,2也不能确定1就一定会在这个时间进攻,因为2发出的回执1并不一定能够收到。所以,1必须再给2发出一个回执说“我收到了”,但是1也不会知道2是否收到了这样一个回执,所以1还会期待一个2的回执。
虽然看似很可笑,但在这个系统中永远需要存在一个回执,这对于两方来说都并不一定能够达成十足的确信。更要命的是,我们还没有考虑,通信兵的信息还有可能被篡改。由此可见,经典情形下两军问题是不可解的,并不存在一个能使蓝军一定胜利的通信协议。
不幸的是,两军问题作为现代通信系统中必须解决的问题,我们尚不能将之完全解决,这意味着你我传输信息时仍然可能出现丢失、监听或篡改的情况。但我们能不能通过一种相对可靠的方式来解决大部分情形呢?这需要谈到TCP协议。事实上,搜索“两军问题与三次握手”,您一定可以找到大量与TCP协议相关的内容。若您是通信方面的专家,权当笔者是班门弄斧,这里仅用最浅显易懂的方式科普TCP协议的原理和局限,可能存在一些毛刺,如需详解见上一篇区块链(一)之传统协议
TCP协议中,A先向B发出一个随机数x,B收到x了以后,发给A另一个随机数y以及x+1作为答复,这样A就知道B已经收到了,因为要破解随机数x可能性并不大;然后A再发回y+1给B,这样B就知道A已经收到了。这样,A和B之间就建立一个可靠的连接,彼此相信对方已经收到并确认了信息。
而事实上,A并不会知道B是否收到了y+1;并且,由于信道的不可靠性,x或者y都是可能被截获的,这些问题说明了即使是三次握手,也并不能够彻底解决两军问题,只是在现实成本可控的条件下,我们把TCP协议当作了两军问题的现实可解方法。
二、问题实质及形式化
我们已经了解了拜占庭将军问题的场景,并且明确了这个问题的解决是建立在通信兵可以正确的传达信息的基础上的,即信道绝对可信。同时,通过前文对于两军问题的探讨,我们明白了理论上可信的信道也是可以实现的。接下来,我们将探讨拜占庭将军问题的实质。
2.1. 拜占庭将军问题实质
回顾问题,一群将军想要实现某一个目标(一致进攻或者一致撤退),但是单独行动行不通,必须合作, 达成共识;由于叛徒的存在,将军们不知道应该如何达到一致。注意,这里“一致性”才是拜占庭将军问题探讨的内容,如果本来叛徒数量就已经多到了问题不可解的地步,这个就是“反叛”的问题了;同时,我们的目标是忠诚的将军能够达成一致,对于这些忠诚的将军来说,进攻或者撤退都是可以的,只要他们能够达成一致就行。
但是,光靠“一致”就可以解决问题吗?考虑一下,如果万事俱备,客观上每个忠诚的将军只要进攻了就一定能够胜利,但是却因为叛徒的存在他们都“一致的”没有进攻;反之,条件不利,将军们不应该进攻,但是却因为叛徒的存在所有人都“一致的”进攻了。
可以发现,只有“一致性”是不足以解决拜占庭将军问题的,我们还需要提出一个“正确性”要求。这个要求是值得斟酌的,因为如果客观来看或许会有“绝对正确的”判断,但是针对每一个将军,大家的判断或许都不相同,我们如何定义“正确”呢?我们或许可以简单地说,正确就是每个忠诚的将军都正确的表达了自己的意思,不会因为叛徒让别的将军认为忠诚的将军是叛徒而不采用他传达的消息。
至此,我们将拜占庭将军问题简化成了,所有忠诚的将军都能够让别的将军接收到自己的真实意图,并最终一致行动;而形式化的要求就是,“一致性”与“正确性”。
如果将问题推广开来,可以发现针对一致性和正确性的算法并不要求命令必须是“进攻/撤退”或是“1/0”,而可以是“发送消息1/发送消息2/待机”或“x/y/z/w”,这意味着拜占庭将军问题算法可以为多种分布式系统提供启发,比如电力系统或网络系统。
由此可见,这个问题说到底是一个关于一致性和正确性的算法问题,这个算法是针对的是忠诚的将军,因为叛徒可以做出任何超出约定的判断。我们就是要在有叛徒的干扰下,找到一个抗干扰的算法。要解决这个算法问题,我们需要将形式化要求具体化。
(4)对于任意k<m,在第m-k+1步中OM(k)的vi,都是利用OM(k-1)得来的,即每个将军将会在OM(k-1)中询问其他人,i将军传给他们的是什么,而其他人传递回来的信息则是利用OM(k-2)得到。
这个就是递归的意义,若您觉得笔者表达得不甚清楚,不用担心,您只用记住每一步都会牵涉到下面很多步骤就可以了,之后将在实例推演中明白算法的核心思路。(m=1,n=3中司令忠诚而副官2是叛徒的情形)
司令把进攻命令传给了两个副官1和副官2,但是由于副官2为了不让他们达成一致,将司令的命令改成了撤退。那对于副官1来说,他应该如何判断?他无法知道是司令是叛徒还是副官2是叛徒,从而无法判断。
(m=1,n=3中司令是是叛徒的情形)
而如果司令是叛徒,两个副官忠诚,司令会发送两个不同的命令。当两个副官对照命令时,他们又凌乱了,无法判断司令是叛徒或者对方是叛徒,从而又无法判断。这个情形非常简易的说明了三模冗余是无法动态容错的。(2)m=1,n=4的情形 n=4,意味着一个司令发送命令给三个副官,m=1意味着他们中有一个叛徒。 首先考虑司令忠诚而副官3是叛徒的情况。
(m=1,n=4中司令忠诚而副官3是叛徒的情形)
倘若司令在OM(1)中给各副官发送的消息都是进攻(A),之后OM(0)时,叛徒副官3给副官1和副官2说他收到的消息是撤退(R)。那么对于副官1(或副官2)来说,他综合司令、副官3和副官2(或副官1)后得到的消息向量都将会是(A,A,R),利用majority函数之后,将会采用A,满足了IC1和IC2(回顾IC1:所有忠诚的副官遵守一个命令,IC2:若司令是忠诚的,每一个忠诚的副官遵守他发出的命令)。
(m=1,n=4中司令是是叛徒的情形)
倘若司令是叛徒,那么我们已经不需要满足IC2。为方便,我们假设叛徒司令在OM(1)会给三个副官发送的信息是(x,y,z),其中x,y,z都可以是A或R的任意一种。之后,三位忠诚的副官将会按照OM(0)要求的那样,交换他们收到的信息。
对于副官1,他综合司令、副官2和副官3后得到的消息向量将会是(x,y,z),可以发现对于其他两个忠实的副官,他们得到的消息向量也将是(x,y,z)。不管x,y,z如何变化,majority(x,y,z)对于三人来说都是一样的,所以三个副官将会采用一致的行动。
(3)m=2,n=7的情形 接下来,我们将讨论m=2,n=7的情形,虽然只是多了一个叛徒,但是这里会出现递归过程,所以会复杂很多。
首先,我们先讨论司令忠诚的情形,假设叛徒为副官5和副官6。
(m=2,n=7中司令忠诚而副官5和副官6是叛徒的情形)
在OM(2)中,司令将攻击命令(A)传给各个副官。在OM(1)中,忠诚的副官们将会发送他们收到的消息(A),但由于副官5和副官6是叛徒,他们将会发送别的信息(比如R)。这时,忠诚的副官们将会采用使用OM(1)中的方法来确定各个v1~v6。例如,对于副官1,他收到了司令传来的命令,他会直接采用majority函数综合司令和其他将军传来的信息吗?他不会,因为这还在OM(1)中,他并不知道司令是不是叛徒,他会利用询问别人的方式来确认将军的命令,但是按照算法他会把司令的命令作为v1(即v1=A)并传给其他人。
接下来他会努力取得其他的v2~v6的值,这时已经在OM(1)中了,副官1绝不会轻易相信别人传来的消息,比如副官2给他传来了命令A,但是他会怀疑副官2传来的消息,所以他用OM(1)大法,问其他人副官2传给了他们什么,副官3和副官4诚实的告诉副官1,副官2给他们传的是A,而这时副官5和副官6又要撒谎了,他们又乱说,我们姑且假定他们传来的是x’和y’吧。这样,终于进入到了OM(0),这时副官1将会综合其他副官对于v2的反馈,得到向量(A,A,A,x’,y’),再利用majority函数,得到了v2=A。如下图,这是副官1在OM(1)中得到的信息(x,y等表示了不确定的命令)。
(司令忠诚时副官1在OM(1)中得到的信息)
我们就可以得到副官1的v1~v6向量为(A,A,A,A,x,y),利用majority函数,副官1最终采用的行动会是A。 类似的,我们可以发现,其他几个忠诚的副官得到的命令向量都会是(A,A,A,A,x,y),利用majority函数后采用的行动都会是A。所以,我们可以发现忠诚的副官采用的命令都是A(满足IC1),并且和忠诚的将军的命令一致(满足IC2)。至此,您应该已经明白了这个算法是怎么递归的,不管m等于多少,都只是一个算法步骤多寡的问题。 至于司令是叛徒的情形,其实是相似的,这里简单的再提一下便于理解。若您已经明白了算法过程,完全可以跳过。
(m=2,n=7中司令和副官6是叛徒的情形)
为方便,我们假定了副官6是叛徒。司令在OM(2)中就很鸡贼的给副官1~副官6发送了不同的命令(A,R,A,R,A,x)。在OM(1)中,各副官把自己收到的命令传出去,而(为方便,假定)副官6则给其他副官分别发送了(A,R,A,R,A)。类似于前文推演的那样,考虑副官1,他将司令传来的命令A作为v1后,还会询问其他人传来的命令,由此去确认v2~v6,类似的,我们将之表达为下图:
(司令反叛时副官1在OM(1)中得到的信息)
如图,我们就可以得到副官1的v1~v6向量为(A,R,A,R,A,A),利用majority函数,副官1最终采用的行动会是A。类似的,我们可以发现忠诚的副官1~副官5得到的消息向量都是(A,R,A,R,A,A),最终他们采用的行动都会是A(满足了IC1),而司令是叛徒不需要满足IC2。值得提醒的是,若副官6传递的是(R,A,R,A,R),那么他们所有人得到的消息向量都是(A,R,A,R,A,R),此时A和R数量一样多,这并不代表majority不起作用了,他将采用默认值R(回顾前文:majority(v1,…,vn-1)代表了大多数人的命令,若不存在则默认为撤退命令),所有人的行动都会采用R,这同样是满足的。
到此为止,我们已经将口头算法的实例推演进行的很彻底了,若您还有兴趣,可以试一试当n=7,m=3的时候为什么就不能达成一致了,操作是类似的。 3.3.口头协议算法证明 算法的证明思路其实并不复杂,简单的来说,对于一个递归算法,我们使用数学归纳法来证明是最直观而又有效的方法了。我们回顾一下命题:将军总数为n,叛徒数量为m,OM(m)可以实现,在n>3m的情况下,使得:
IC1:所有忠诚的副官遵守一个命令。
IC2:若司令是忠诚的,每一个忠诚的副官遵守他发出的命令。
为了证明整个命题,我们先引入一个针对IC2的引理:
引理:对于任意m和k,如果有超过2k+m 个将军和最多k 个背叛者,那么算法OM(m)满足IC2。
证明:
(1)m=0,而将军是忠诚的,直接满足IC2;
(2)m>0,此时假定OM(m-1)是有效的,那么只需要考虑OM(m)这一轮即可。
n>2k+m,意味着n-1>2k,n-1是除了司令以外的所有副官,而所有副官的数量比叛徒的两倍还多,那他们直接利用majority函数的时候,就可以直接正确得到司令的命令。
可以发现,这个引理说明了如果只需要考虑IC2,将军总数是不需要3倍背叛者之多的,接下来我们回归命题。
证明:
首先考虑司令是忠诚的,令引理中的k=m,直接得到OM(m)可以满足IC2。
这时,我们只用考虑司令是叛徒的状况。同样利用数学归纳法。
(1)m=1,之前我们已经推演过OM(1)可以满足1个叛徒司令,3个忠诚副官的情况;
(2)m>1,那么假设n’>3m’的情况下,OM(m-1)能够满足IC1和IC2。
由于司令是叛徒,在OM(m)中司令会把命令发给各个副官,而这些副官中会有m-1个叛徒。在下一轮中,副官的数量至少有3m个,叛徒数为m-1,很显然3m>3(m-1),也就是说n’>3m’,根据假设,OM(m-1)可以满足IC1和IC2,尽管司令是叛徒,由于OM(m-1)是有效的,OM(m)这一轮中忠诚的副官可以得到相同的(v1,…,vn-1)向量,所以忠诚的副官将会利用majority函数采用相同的命令,得证。
总结一下,口头协议中,我们始终要求的是相同的(v1,…,vn-1)向量,只要这个向量是相同的我们怎么处理都可以。又由于算法是递归的,所以我们一定需要把这个处理方法变得通用而逻辑有效才行,所以我们才选用了majority函数这个算法。这一点至关重要却又没有这么直观,因为我们的第一反应是要得到相同的majority函数值。但是反过来一想,既然算法是递归的,majority函数值相同不就意味着(v1,…,vn-1)向量相同吗?正确理解递归的思想是使用该算法和利用数学归纳法证明该算法的关键点。
至此,我们已经大致明确了口头协议是怎么回事,可以做到什么不能做到什么,以及这个算法的推演和证明。很多系统都会出现口头协议的情况,即各个系统节点可以把自己的消息准确的发送出去,同时可以知道消息的来源于何处。但是,这个方法的消息并不能追本溯源,这使得在口头协议中至少得四模冗余,我们可以试图找到一个方法,让消息能够追本溯源,可以想象这能够拓宽使用条件,这个方法可以是书面协议。
(m=1,n=3中司令是叛徒的情形)
很显然,副官1得到的V1={A,R},副官2得到相同的V2={A,R}。他们采用choice函数后得到的命令一定相同。
相似的,n=4,m=2,其中司令是叛徒,这同样是口头协议不能解决的状况。
(m=2,n=4中司令和副官3是叛徒的情形)
副官1和副官2得到的V1=V2={A,R},他们采用choice函数后得到的命令也相同。即使命令不是布尔值,经过上面的分析框架,也可以得到相似的结论。至于这个算法的证明,有兴趣的读者可以参考Lamport的原文,笔者就不做过多解释了,如有问题仍可与笔者联系。
书面协议的本质就是引入了签名系统,这使得所有消息都可追本溯源。这一优势,大大节省了成本,他化解了口头协议中1/3要求,只要采用了书面协议,忠诚的将军就可以达到一致(实现IC1和IC2)。这个效果是惊人的,相较之下口头协议则明显有一些缺陷。
书面协议的结论非常令人兴奋,这不是解决了拜占庭将军问题了吗?但请注意我们在A1~A4中实际上是添加了一些条件的,这使得拜占庭将军问题在这些假设下能够解决,但是在实际状况中却会有一些问题。观察A1~A4,我们做了一些在现实中比较难以完成的假设,比如没考虑传输信息的延迟时间,书面协议的签名体系难以实现,而且签名消息记录的保存难以摆脱一个中心化机构而独立存在。事实上,存在能够完美解决书面协议实际局限的方法,这个方法就是区块链。