4.1 比特币中的协议

这一节我们来看一下,怎么能够设计出一个加密货币呢?我们先不考虑去中心化的问题,假设有一个大家都信任的中心化的机构,比如央行,这个央行是有权力发行数字货币的。假设大家都知道央行的公钥,那么央行发行数字货币,应该怎么发行?

用发行纸币的方法发行数字货币行不行?发行的数字货币都有央行私钥的签名,央行的公钥可以认为是大家都知道的。当一个人收到一个数字货币,可以验证一下是不是真的。买东西的时候,比如我需要给你100块钱,我就把这个数字货币发给你,你验证一下确实是央行发行的,这就完成了支付的过程。这个方案行不行?

这里面没有用到区块链,这里用到的是密码学中的公私钥体系——非对称加密算法。这里的问题在于,假如我要买东西,我把100块给你,但是我可以再复制一份,这数字货币是什么,就是一个文件。这个文件有央行的签名,文件的内容是不可以伪造的,但它是可以复制的,可以把它复制很多份。这个跟纸币就不一样了,我把纸币给你了,我就没了,没法花两次。

跟纸质货币的这种区别叫做花两次攻击——也叫双花攻击(double spending)。数字货币所面临的一个主要的挑战就是怎么防范double spending。

刚才的方案是不行的。我们改进一下,还是央行发行数字货币,但是每个数字货币上得有一个编号,就像人民币也有编号,然后央行要维护一个数据库,就像是一个大的表,上面记录着每个编号的数字货币在谁那里。
比如某个数字货币在我这里花出去的时候,我把数字货币给你,你不只是要验证一下这个数字货币有没有央行的签名,而且你还要给央行核实一下,这个数字货币以前我有没有花出去。央行一验证,这个数字货币确实是在我这里的,所以我用它来支付是合法的,同时央行要修改一下,这个数字货币现在从我这里转到你那里了。
那以后如果我想再花一次这个数字货币,另一个人跟央行一核实就发现不对了,这个数字货币我已经花出去过了,已经不在我这里了,这就可以防范双花攻击了。

这个方案有什么问题吗?太麻烦了。任何两人之间进行支付都要通过央行。这个方案的正确性是没有问题的,而且实践中也是可以这么用的,但这是一个中心化的方案。数字货币的发行是由央行统一控制,而且每一次交易都需要经过央行确认才能证明其合法性。那么能不能搞一个去中心化的方案就把央行这个职能改成由广大的用户来共同完成。这就是比特币这个数字货币系统要解决的一个问题。

一个去中心化的货币要解决两个问题。一个是数字货币的发行权,谁有权力决定发行数字货币。现在没有央行,那怎么决定货币什么时候该发行,该发行多少?第二问题是怎么验证交易的有效性。怎么防止刚才讲的double spending。

关于第一个问题,谁来发行货币,这个在比特币系统中是由挖矿决定。后面会详细讲,现在先讲第二个问题,怎么防范double spending。这个问题解决的方案。跟刚才讲的这个方案有点类似,也是需要维护一个数据结构来检测这个货币以前有没有被花过,被谁花过。只不过这个数据结构,不是由央行来维护,而是由所有的用户共同维护这个数据结构。这里用的就是区块链。

假设有一个用户A,他获得了发行货币的权力,就是我们说的铸币权,那么他发行10个比特币,这个叫CreatCoin给A,10个比特币,把这第一个交易写到区块链里。A拿到这个币,把它转给B和C,每人给5个比特币,这个交易需要有A的签名,证明是经过A同意的,同时这个交易还要说明A花掉的这个10个比特币是从哪来的。
这里是从前面这个叫铸币交易来的,铸币交易能够凭空发行货币,铸币交易要指明这个币是从铸币交易的输出中来的。比特币系统中每个交易都包含了输入和输出两部分。输入部分要说明币的来源,输出部分要给出收款人的公钥的哈希。比如,A要转给B,那么就要说明B的公钥的哈希是什么。
然后,B又可以把这个币转给C和D。转给C两个比特币,转给C三个比特币,这个同样是要有签名,Signed by B。这时B要说明币的来源,是从哪来的呢,从前面的这个交易中来的。这个时候C有多少币呢,C一共有7个比特币。假如C决定把这个7个比特币给E亿。
这个时候C的币的来源稍微复杂一点,有2个是A来的,还有5个是从B来的。这就构成了一个小型的区块链。

注意,这个地方有两种哈希指针,一种哈希指针就是连接各个区块之间的,把它们串起来构成一个链表,上一节讲的就属于这种哈希指针。

这里还有第二种哈希指针,是指向前面某个交易的,这种指针是为了说明币的来源,是从哪来的。为什么要说明币的来源?证明这个币不是凭空捏造的,是有记录的,同时是为了防范double spending。假如,现在假如现在B已经把币转给了C和D,他又要转给F五个比特币,签名还是有B的签名,如果只是验证签名,交易看上去好像是合法,但币是从哪来的呢?其它的节点收到这个交易之后,查一下区块往币的来源回溯一下当初币的来源,这5个币在前面的交易中已经被花出去了,说明这个交易是不合法的,就不会把它接收到区块链里,这就是检测double spending。

到这里有问题吗?我们再仔细看一下这里面的转账交易。A把币转给B这个交易,这个交易需要有A的签名,同时还需要有B的地址。这个接收方的地址是通过公钥算出来的,比如这个转账的地址,就是B的公钥取哈希,然后经过一些转换得到的。这个地址相当于银行账号,要给B转账就需要知道B的地址。那么A怎么才能知道B的地址?B的公钥是公开的,他会告诉A,但是他会不会告诉所有的人,也没有必要告诉所有人。

我们日常生活当中,比如说,我需要给你转账,我得知道你的银行账号才能转过去。我怎么才能知道你银行账号呢。需要你告诉我。
比如说我要买个东西,你接受的支付方式是银行转账。那我就问你,你的银行账号是多少,我才能转给你,银行本身并没有提供这种查询功能,你要告诉我才行。比特币系统是类似的,就是A要给B转账,B的地址是多少,比特币系统内部没有提供一种功能去查询某个人他所对应的比特币地址,这个得要其他的渠道来获得。比如说某个电商网站,它接受比特币支付,那么它可以在它的网站上公开它的比特币地址,或者公开它的公钥,公钥反正不用保密。所以很多比特币地址在网站上就是个二维码,扫一下就可以知道地址。

接下来的问题,A需要知道B的地址,必须要知道A的什么信息吗?必须要知道A到底有没有转给B,转了多少。这个交易要写到区块链里,然而怎么能够证明这个交易已经写进区块链里了?要写到区块链之前,其实还要符合一些条件。
A有没有足够的币转账。A要给B转账,他得有足够的币的来源。A指向这个铸币的交易,表明A确实有10个比特币能转。币的来源,是A要证明的,不是B要证明的。
B要知道A的公钥。A的公钥代表了A的身份,所以要知道A的公钥,知道这币是从哪里转来的。还有一个更重要的原因,不只是B要知道A的公钥,所有节点都需要知道A的公钥,因为要验证A的签名。这个转账交易要是合法的,必须要有A的签名。前面讲过签名是过程,是私钥签名,公钥验证。区块链上每个节点都得独立验证。因为有的节点可能是有恶意的,所以你不能依靠别人验证,我收到这个交易,我得自己验证一下是不是合法的。这个币可能不是转给我的,是另外两人之间转账,我是个旁观者,但是我也得验证。所以大家都需要知道A的公钥。

那问题来了,怎么才能知道A的公钥?A的公钥是这个交易中A自己给出来的。就是A给B转账这个交易,A在他的输入里面,要说明币的来源,还要说明A的公钥是什么,就是A自己说的。
还有没有存在问题?。假如有一个人冒名顶替A,比如叫C,C伪造一个A到B的转账,交易他用他自己的公钥,在输入中用A的公钥。然后C用自己的私钥签名,别的节点收到之后,用这个假造的公钥去验证这个签名。那肯定是对的,就以为这个交易是合法的,那不就等于把A账户的钱给偷走了。这个问题是存在的,怎么去避免这个问题呢?每个交易分为输入和输出两部分。输入部分要说明币的来源,还要说明这个A的公钥。输出部分要给出收款人公钥的哈希,这个币要转给谁,要给出他的公钥的哈希,类似于他的地址。那么这个交易的币是从哪来的呢?从铸币交易,这个叫作coinbase tx。

铸币交易的输出里面有A的公钥的哈希,转账交易里说明的A的公钥要跟币的来源里面说明的A的公钥的哈希要吻合才行。如果对不上的话,表明这些币的来源,你说的是不对的,这个币当初不是给你的。上面讲的这个例子当中,如果C冒名顶替把他自己的公钥说成是A的公钥,那么这个交易的公钥和前面铸币交易输出中指定的A的公钥的哈希就对不上,所以验证是通不过的。

上面的图中对比特币系统的一些细节做了简化,图里好像每个区块只有一个交易,实际系统当中每个区块可以包含很多个交易。这些交易就组织成merkle tree,每个区块分成块头和块身两部分,Block header和Block body。Block header里包含的是这个区块的一些宏观的信息,比如说用的是比特币的哪个版本的协议,还有区块链当中指向前一个区块的指针,还有整棵merkle tree的根哈希值,还有两个域是跟挖矿相关的。一个是挖矿的难度目标域制——target,还有就是随机数——nonce。上节讲的挖矿求的那个puzzle,整个块头的哈希要小于等于目标域值,H(block header) <= target。Block header里存的就是目标域值的一个编码,叫nBits。这里要注意,前一个区块的哈希,只计算的是区块的块头。

有的书中图示是把指针画在上面的,原因就是只有block header才有这种哈希指针。可以把每个区块看成是由两部分构成,上面部分是个block header,下面部分是block body,block body里有交易列表transaction list。取哈希的时候是把块头的所有部分取哈希,block body是不管的。Merkle root hash就已经能够保证这个block body里所包含的transaction list是没有办法被篡改的。

上面的讨论中有一个简化的假设,好像是每个节点都需要验证所有的交易,实际系统中的节点分为全节点 full node和轻节点 light node。全节点是保存所有的信息的,就是区块链的所有信息,然后验证每一个交易,所以全节点也叫作fully validating node。

轻节点只保存block header的信息,一般来说轻节点没有办法独立验证交易的合法性,比如说这个交易是不是double spending,轻节点是不知道的,因为它没有存以前的交易信息,它查不出来。系统中大多数节点其实是轻节点,全节点的数目不是很多。这系列的内容主要是针对全节点讲的,因为轻节点没有参与区块链的构造和维护,轻节点只是利用了区块链的一些信息,做一些查询之类的。

区块链中包含的内容,是怎么被写到区块链里面。每个节点都可以发布交易,每个账户都可以发布交易,这些交易是广播给所有的节点。有些交易是合法的,有些交易可能是非法的,那么谁来决定哪些交易应该被写到下一个区块中,按照什么样的顺序写?由每个节点自己决定行不行?比如我是一个节点,我收到比特币网上各个交易,我判断一下哪些是合法的,然后我把它打包,写到下个区块里,我构造出一个本地的区块链,就是说每个节点独立决定行不行?

这样的话,一致性得不到保证。我本地维护一个区块链,跟你本地维护一学校链,它们不是一条链。区块链是什么,是个去中心化的账本。它既然是个账本,这个账本里的内容得有一个统一的内容,否则的话,我记得账跟你记得账不一样,,最后按哪个账本来算呢?所以,用分布式系统的术语来说,叫做账本的内容要取得分布式的共识distributed consensus。

分布式共识,一个比较简单的例子就是分布式的哈希表。系统里有很多台机器共同维护一个全局的哈希表,这里需要取得共识的内容是什么?需要取得共识的是哈希表中包含哪些key-value,比如某人在他的机器上插入一个key-value。那么别人在另一台机器上去读的时候,也要能把这个读出来,这叫一个全局的哈希表。关于分布式共识,有很多的理论研究,在这方面有很多的论文,而且有很多的不可能结论。这个叫做impossibility results,其中最著名的一个就是FLP impossibility results。这三个字母是三个作者的姓的开头字母,都是分布式系统的专家。他们的结论是,在一个异步的系统里(asynchronous),网络传输时延没有上限,这叫异步系统,即使只有一个成员是有问题的(faulty),那么也不可能取得共识。

还有一个结论,比较著名的叫做CAP Theorem。CAP这三个字母指的是分布式系统的三个性质。C是consistency,A是availability,P是partition tolerance。这个CAP Theorem是说,任何一个分布式系统,比如这个分布式哈希表,这三个性质当中最多只能满足两个,不可能三个性质都满足。比如,你想要的是consistency和availability,你就得不到partition tolerance。这就是CAP Theorem。分布式共识的一个比较著名的协议,是Paxos。这个协议能够保持保证一致性,就是如果这个协议达成了共识,那这个共识一定是一致的。不会一个成员认为的共识,与另外一个成员认为的共识不一样,一定是consistent。但是某些情况下,这个Paxos协议有可能一直没有办法达成共识。这种可能性在实际系统当中是比较小的,但是客观存在。这些理论跟比特币的实际应用关系是不大的。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注