比特币的共识协议

区块链

数字货币和纸质货币区别是可以复制,叫作双花攻击 即double spending attack。

去中心化货币要解决两个问题:

  1. 数字货币的发行
  2. 怎么验证交易的有效性,防止double spending attack。

数字货币的发行和挖矿有关,我们以后介绍,首先来看第二个问题,如何防止双花攻击。

如图,比特币的发行者A拥有铸币权(create coin)假如发行10个比特币,A(10)分别给B和C各五个 → B(5)C(5),该交易需要有A的签名,证明经A同意(signed by A)。同时还要说明花掉的10个比特币从哪来的。

比特币系统中每个交易都包含输入和输出两部分。输入部分要说明币的来源和自己的公钥,输出部分要给出收款人公钥的哈希。有的交易部分比较复杂,如C的货币来源是第二第三个方框,要标识清楚。

这就构成了一个小型的区块链,这里有两种哈希指针,一种哈希指针是连接在各个区块之间的,把它们串起来构成一个链表,前面学的就是这种哈希指针。而在该图中还有第二种哈希指针,是指向前面某个交易的指针,用来指明币的来源。为什么要说明币的来源:证明币不是凭空捏造的是有记录的,同时也是防范double spending。

现在来看第二个方框里A向B的转账,该交易需要A的签名和B的地址。比特币系统里收款的地址是通过公钥推算出来的。比如B的地址就是B的公钥取哈希然后经过一些转换得到的。

A如何知道B的地址?比特币系统中没有查询对方地址的功能,必须通过其他渠道。比如某个电商网站,接受比特币支付,就可以公开它的地址或公钥。

A需要知道B的地址,B需要知道A的什么信息吗?B其实也要知道A的公钥,这代表A的身份。不仅是B,所有节点都需要知道A的公钥。而签名是用私钥签名公钥验证,所以区块链上每个节点都要独立验证。

那如何才能知道A的公钥?实际上交易里就包含了。输入时不仅要输入币的来源,还要输入公钥。那就存在了安全漏洞,假如B的同伙B’伪造了这次交易呢?B‘伪造了一次A到B的交易,在交易中将B’的公钥伪造成A的公钥,然后用B’的私钥对交易签名。别的节点收到了交易之后,用B’的公钥来验证签名,以为就是合法的,这就相当于B’把A的钱偷走了。这个问题实际不存在,因为第一个方框里铸币交易的输出就有A的公钥的哈希,所以第二个方框交易里A的公钥要跟前一个交易输出的收款人公钥哈希对的上。要是对不上,就说明币的来源是不对的。

UTXO模型

以上原理在比特币中的具体实现就是UTXO模型。大家都知道什么是余额的概念。例如,用微信支付买一瓶水,实际操作是在我的微信余额里面减掉2块钱、在卖家的微信余额里面加上2块钱。现金支付是我拿了10块钱去买水,把10块钱给到卖家,卖家给了我一瓶水、又找了我8块钱的交易模型。UTXO就很像是现金交易,拿10块钱,给了店家2块,又给了矿工5毛,剩下那7块5就给了自己。不同点在于,我拿的这10块钱全部发生了变化,而不是仅仅把2块钱给了商家、5毛钱给矿工,而是我把这10块钱全部花掉,那7块5又找给了自己。这样的交易就构成了一种链式交易,所有合法的交易都可以追溯到前一个或者是前多个交易的输出。

UTXO集合当中的每个元素要给出产生输出的交易的哈希值,以及它在这个交易里是第几个输出。这两个信息就可以定位到UTXO中的输出。

要UTXO集合有什么作用?

为了检测double spending。即检测新发布的交易是否合法。因此全节点要在内存中维护UTXO这样一个数据结构,以便快速检测double spending。

实现细节

在比特币系统当中,前面这些验证过程,是通过执行脚本来实现的。每个交易的输入提一段脚本,包括给出公钥的过程,公钥也是在输入的脚本里指定的。每个交易的输出也是一段脚本,验证其的合法性,就需要把当前交易的输入脚本跟前面交易(提供币来源的交易)的输出脚本拼在一起,然后看看能不能顺利执行,如果能执行说明是合法的。比特币脚本(BitCoin Script)。

该图对交易系统进行了简化,实际上每个区块(对应图中的每个方框)可以有很多交易,这些交易就组成merkle tree。每个区块分为块头和块身。

块头包含的是区块的宏观信息,比如:用的是比特币哪个版本(version)的协议,区块链当中指向前一个区块的指针(hash of previous block header),整颗merkle tree 的根哈希值(merkle root hash),还有两个域是跟挖矿相关的,一个是挖矿的难度目标预值(target),另一个是随机数nonce。这里的target,就是前面讲到的,整个块头的哈希要小于这个预值,即H(block header)≤target。block header里存的就是这个目标预值的编码(nBits)。

这里需要注意,前一个区块的哈希只算的是前一个区块的块头header,取哈希时是把块头的所有部分都取哈希。

节点类型

一个比特币节点是一些功能的集合:路由、区块链数据库、挖矿和钱包服务。

前面还有一个内容讲的时候简化了:每个节点都需要验证所有的交易,实际上系统中的节点分全节点(full node)和轻节点(light node)。

全节点是保存区块链所有的信息的,验证每一个交易,所以全节点又叫fully validating node。Maintain a complete copy of the blockchain. Verify any transaction autonomously and authoritatively without external reference. Satisfy certain requirements: 200G hard dirve, 2G RAM etc.

轻节点只保存block header的信息,一般来说轻节点没法独立验证交易的合法性。Run on constrained resource devices: smartphones or tablets. Connect and transmit transactions to the Bitcoin network through full nodes. Download the block headers instead of the entire blockchain. Verify transaction using simplified payment verification (SPV).

比如一个交易是不是double spending,轻节点没有存以前的交易信息所以它没法验证。系统中大多数节点是轻节点,这节课内容主要针对全节点,因为轻节点没有参与区块链的构造和维护,只是利用了区块链的一些信息做一些查询。

区块链里的内容是如何写到区块链里面的呢:每个节点,每个账户都可以发布交易,交易是广播给所有节点的。有些交易是合法的,有些是非法的。谁来决定哪些交易应该被写入下一个区块中呢?按照什么顺序写呢?如果每个节点自己决定可以吗?如果每个人在本地维护一个区块链,那区块链的统一性得不到保证,而账本的内容是要取得分布式的共识(distributed consensus)。

共识协议

比特币中共识要解决的一个问题是,有些节点可能是有恶意的。我们假设系统中大多数节点是好的,那么该如何取得共识协议?

One Identity One Vote

第一种方案是投票,首先应该确定哪些区块有投票权,有些membership是有严格要求的,这种情况下基于投票的方案是可行的。但比特币系统创建账户是很容易的,甚至一个人产生了公私钥对别人都无法得知,只有转账时别人才知道。所以有些人可以不停的创建账户,当超过账户总数的一半时就有了控制权,这种称为女巫攻击(sybil attack)。因此投票方法不可取。

Proof of Work

比特币账户巧妙的解决了这个问题,不是按照账户数目投票,而是按照计算力来投票。每个节点都可以在本地组装出一个候选区块,把它认为合法的交易放在里面,然后开始尝试各种nonce值(占4 byte),看哪一个能满足不等式H(block header)≤target的要求。如果某个节点找到了符合要求的nonce,它就获得了记账权。

所谓的记账权,就是往比特币账本里写入下一个区块的权利。只有找到这个nonce,获得记账权的节点才有权利发布下一个区块。其他节点收到这个区块之后,要验证这个区块的合法性。

比如括号里block header的内容填的对不对;block header里面有一个域,叫nBits域,实际上它是目标预值的一个编码检查一下nBits域设置的是不是符合比特币协议中规定的难度要求;该不等式是否成立。假设都符合要求,然后检查block body 里面的交易列表,验证一下每个交易都是合法的:①要有合法的签名②以前没有被花过。如果有一项不符合要求,这个区块就是不能被接受的。如果所有条件都符合,也不一定接受。

Longest Chain Rule

假如生成了一个新区块,怎么知道新区块插在了哪里呢?根据生成区块的指针。有可能就存在一个问题,如图这两个交易指B转账给C,以及B转账给自己。这种情况不是double spending,判断一个交易是不是double spending ,是看这个区块所在的分支上币又没有被花掉。如图,一直到第三个区块,币都没有花过,所以这个交易是合法的。虽然该交易是合法的,但是它不在最长合法链(longest valid chain)上。这种称为分叉攻击(forking attack)。所以接收的区块应该是扩展最长合法链。

区块链在正常情况下也可能出现分叉:两个节点同时获得记账权。每个节点在本地自己组装一个它认为合适的区块,然后去试各种nonce,如果两个节点在差不多同一个时间找到了符合要求的nonce,就都可以把区块发布,这时会出现两个等长的分叉。这两条都是最长合法链,那该接受那条呢?比特币协议当中,在缺省(默认的意思)情况下,每个节点是接受它最早收到的那个。所以不同节点根据在网络上的位置不同,有的节点先听到新生成的其中一个区块,那就接受这个区块;有些节点先听到另一个区块,那就接受另一个区块。

如何判断接收了一个区块?比特币协议中用到了implicit consign,如果沿着这个区块往下继续扩展,就算认可了这个发布的区块。比如在新生成的其中一个区块后面又拓展一个区块,表明就认可了这个新区块。

等长的临时性的分叉会维持一段时间,直到一个分叉胜出。也就是哪一个链抢先一步生成了新的区块,哪一条就是最长合法链。另一个作废的就叫orphan block。这两个新区块有可能会各自拉拢,两个区块链看谁的算力强,有时候也是看谁的运气好,就会胜出。

Block Confirmation

胜出的分叉上的区块不能马上用来记账,需要经过n block confirmation才能确认交易。每当一个新的区块被挖出来并添加到区块链上,就相当于对之前所有交易的一次确认。因此,如果交易有6个确认,就意味着有6个区块在你的交易所在的区块之后被添加到了区块链上。这样做的目的是为了保证交易的安全性和不可逆性,防止双重支付或者篡改。一般来说,比特币网络要求至少6个确认才能认为交易是有效的。

竞争记账权的好处:首先获得记账权的节点本身有一定的权力,可以决定哪些交易写到下一个区块里。但这些不应该被设定为竞争记账权的动力,所以巧妙地建立了一个机制:区块奖励(block reward)。

比特币协议中规定获得记账权的节点在发布的区块里可以有一个特殊的交易:铸币交易。在这个交易里可以发布一定数量的比特币。

这里要回到前面的问题①,谁来决定货币的发行?coinbase transaction币基交易是比特币系统中发行新的比特币的唯一方法,后面的交易都是比特币的转移。这个交易不用指出币的来源。

那么能造多少币呢?开始时比特币刚上线的时候,每一个发布的区块可以产生50BTC(BTC就是比特币的符号)。协议中规定,21万个区块以后,初块奖励就要减半,就变成了25BTC。再过21万个区块,又要减半。

因此当一个区块胜出后,另一个作废的区块得到的比特币是没有作用的,其他诚实的区块是不会承认的。

Consensus Rule Change

区块链网络升级的方式有两种,hard fork和soft fork。主要区别在与有着不同的兼容性。

Hard Fork

hard fork是一种不向后兼容的升级,意味着旧版本的节点无法识别新版本的区块,区块链只能在新版本节点的区块上延续。Reason:correcting important security risks found in older versions of the software, to add new functionality, or to reverse transactions, etc. Examples: Bitcoin Cash, Bitcoin gold, etc.

Soft Fork

soft fork是一种向前兼容的升级,意味着旧版本的节点仍然可以接受新版本的区块,但不能使用新功能。因此,soft fork只需要一部分节点升级,而hard fork需要所有节点升级。

Example: consider a new rule lowering block size from 3MB to 2MB. Older nodes will still be able to process transactions and push new blocks that are 2MB or less. But if an older node tries to push a block that is greater than 2MB to the network, newer nodes will reject the block because it violates the new rules.

小结

比特币系统中要取得什么共识?去中心化的账本要取得共识。谁又能决定账本的内容呢?只有获得记账权的节点才能写东西。怎么获得记账权呢?就是解PoW(挖矿)。按照算力记票,算力可以用每秒能试多少nonce数值表示。那怎样防范女巫攻击呢?按算力记票,即使创建再多的账户,也无法使算力增强。

比特币争夺记账权的过程叫作挖矿(mining),比特币被称为数字黄金(digital gold),争夺记账权的节点被称为矿工(miner)。