首页 > 技术文章 > 比特币 三、协议

lazyuan 2021-02-11 19:34 原文

1.怎么设计出一个数字货币
①类比传统货币设计数字货币
首先不考虑去中心化问题,假设有一个大家都信任的中心化机构,比如说央行,央行是有权利发行数字货币的,央行发行数字货币就像人民币一样发行。人民币是如何发行的呢?印钞厂在上面做一些防伪标志,那么用同样的方法央行也可以发行数字货币,发行的数字货币都有央行的私钥的签名,央行的公钥是公开的,所以在我们收到一个数字货币的时候可以验证他是否是真的,这样就实现了数字货币的防伪
如果仅仅如此,这个体系仅用到了数字签名技术,并没用到区块链技术,而且存在一个最大的问题就是这样的数字货币,如果和传统的纸质货币以一样的方式印发,则无法抵抗复制攻击。数字货币本质上就是一个代码/文件,可以随意复制很多份,这样就可以无限复制,这种攻击方法也可以叫作双花攻击(double spending attack)
 
货币实现中心化到去中心化的过渡,要解决两个问题:谁发行货币? 怎么验证交易的有效性?(防止double spending attack)
 
②中心化数字货币
央行给每个数字货币一个编号,并且维护一个数据库。央行在数据库中维护每个货币的编号,及其所属人,如001号货币属于张三,002号货币属于李四……张三在将数字货币支付(拷贝)给王五时,王五在数据库中进行查验,检查以下两点
  • 货币是否真实
  • 货币支付属于肖臻
都通过,即完成支付。完成支付后,数据库中,该数字货币编号指向王五。张三买东西的时候,如果在花费完该货币后仍使用该001号货币支付,菜农在数据库中进行查验时,就会发现该货币属于王五而不是张三,因此交易不通过。这种方案预防了double spending attack,但每次交易都要通过央行,并且是一个中心化的交易。
 
那么我们能否发明一个方案,一个去中心化的方案,将验证的职责,从由央行承担,转变为由大众承担呢?
 
 
2.协议
(1)谁发行货币?
比特币的发行,是由挖矿决定的
 
(2)怎么验证交易的有效性,防止double spending attack?
依靠区块链的数据结构,区块链这个数据结构不是由中心化机构维护,而是由所有用户共同维护。
比特币的发行者拥有铸币权(createcoin)
 
假如发行10个比特币给A,A再分别给B和C各五个比特币,该交易需要有A的签名,证明经A同意,同时还要说明花掉的10个比特币从哪来的。
 
B再给C 2个比特币,给D 3个比特币
上图就构成了一个小型的区块链。这里有两种哈希指针,一种哈希指针是连接在各个区块之间的,把它们串起来构成一个链表,前面学的就是这种哈希指针
 
第二种哈希指针,是指向前面某个交易的指针,用来指明币的来源,以证明币不是凭空捏造的而是有记录的,同时也是防范double spending
 
现在来看第二个方框里A向B的转账,该笔发生时需要以下两个信息:
  • A的签名
  • B的地址
比特币系统里收款的地址是通过公钥推算出来的。比如B的地址就是B的公钥取哈希然后经过一些转换得到的。B的地址相当于银行账号,A要给B转钱,A需要知道B的账号
 
那么A如何知道B的地址?比特币系统中没有查询对方地址的功能,必须通过其他渠道。比如某个电商网站,接受比特币支付,就可以公开它的地址或公钥。
 
在A向B转账的时候,A需要知道B的地址,B需要知道A的什么信息吗?从转账角度看B只负责收钱,不需要知道任何信息,正如别人向你银行卡转账一样,但B其实是需要知道A的公钥的,这代表A的身份,不仅是B,所有节点都需要知道A的公钥,目的是用于验证A的签名,签名是用私钥签名、公钥验证,所以区块链上每个节点都要独立验证。
 
那如何才能知道A的公钥?实际上A的公钥在交易里就包含了。输入时不仅要说明币的来源,还要说明自己的公钥,也就是在A向B转账的时候,A的公钥是自己声称的。
 
那就可能存在安全漏洞,假如B的同伙B'伪造了这次交易呢?其实,第一个方框里铸币交易的输出里有A的公钥的哈希,所以第二个方框交易里A的公钥要跟前面哈希对的上,因为在执行交易的时候要逐层回溯直到根节点来进行验证,对不上的话则验证不通过
 
总结:比特币系统中每个交易都包含输入和输出两部分。输入部分要说明币的来源,输出部分要给出收款人公钥的哈希
 
 
3.BitCoin Script
在比特币系统当中,前面这些验证过程,是通过执行比特币脚本来实现的。每个交易的输入是一段脚本,包括给出公钥的过程,公钥也是在输入的脚本里指定的。每个交易的输出也是一段脚本,验证其合法性,就是要把当前交易的输入脚本跟前面交易(提供币来源的交易)的输出脚本拼在一起,然后看看能不能顺利执行,如果能执行说明是合法的。
该图对交易系统进行了简化,实际上每个区块可以包括很多交易,这些交易就组成merkle tree,每个区块分为块头(block header)和块身(block body)。块头包含的是区块的宏观信息,比特币区块头共6个字段如下
int32_t nVersion; //版本号,4字节 
uint256 hashPrevBlock; //前一个区块的区块头hash值,32字节 
uint256 hashMerkleRoot; //包含进本区块的所有交易构造的Merkle的根哈希值,32字节 
uint32_t nTime; //Unix时间戳,4字节 
uint32_t nBits; //和挖矿有关,记录本区块难度,4字节 
uint32_t nNonce; //和挖矿有关,随机数,4字节

这里的nBits,是target的编码,就是前面讲到的整个块头的哈希要小于这个目标预值,即H(block header)≤target。

前一个区块的哈希算的只是前一个区块的block header的哈希,并不包含block body,而block body的正确性,可以通过hashMerkleRoot就能验证没有被篡改
 
系统中的节点分全节点(full node)和轻节点(light node),全节点是保存区块链所有的信息的,验证每一个交易,所以全节点又叫fully validating node。轻节点只保存block header的信息,一般来说轻节点没法独立验证交易的合法性。比如一个交易是不是double spending,轻节点没有存以前的交易信息所以它没法验证。系统中大多数节点是轻节点,全节点的数目不是很多,下面我们主要针对全节点进行分析,因为轻节点没有参与区块链的构造和维护,只是利用了区块链的一些信息做一些查询。
 
 
4.比特币的共识机制——POW (proof of works)
①什么是共识机制
在区块链上,每个人都会有一份记录链上所有交易的账本,链上产生一笔新的交易时,每个人接收到这个信息的时间是不一样的,有些想要干坏事的人就有可能在这时发布一些错误的信息,这时就需要一个人把所有人接收到的信息进行验证,最后公布最正确的信息。
 
加密货币都是去中心化的,去中心化的基础就是P2P节点众多,那么如何吸引用户加入网络成为节点,有哪些激励机制?同时,开发的重点是让多个节点维护一个数据库,那么如何决定哪个节点写入?何时写入?一旦写入,又怎么保证不被其他的节点更改(不可逆)?回答这些问题的答案,就是共识机制
 
②浅谈共识机制
分布式的共识一个简单的例子就是分布式的哈希表(distributed hash table),比如系统里有很多台机器,共同维护一个全局的哈希表
 
这里需要取得共识的内容是什么?哈希表中包含了哪些键值对key valve pair。假如有人在自己电脑上插入一个键值对,'xiao'这个pair对应的是12345,即'xiao'→12345。那么别人在另一台读的时候也要能把这个读出来,这就叫一个全局的哈希表。
 
关于分布式共识有很多不可能结论(impossibility result),其中最著名的是FLP:在一个异步的(asynchronous)系统里,(网络传输迟延没有上限就叫异步系统),即使只有一个成员是有问题的(faulty),也不可能取得共识
 
还有一个著名结论:CAP Theorem。(CAP是指分布式系统的三个我们想要的性质,Consistency【系统状态的一致性】 Availability【可用性】 Partition tolerance【分区容错性】)。该理论内容是:任何一个分布式系统,这三个性质中,最多只能满足两个,假如想要前两个性质,那么就不会得到第三个性质
 
分布式共识一个著名的协议是Paxos,该协议能够保证一致性,即第一个性质。如果该协议打成了共识,那么这个共识一定是一致的,即每个成员所认为的共识都是相同的。但是,某些情况下,该协议可能永远无法达成共识,这种可能性比较小,但是是客观存在的
 
③比特币中的共识协议
区块链里的内容是如何写到区块链里面的呢?每个节点,每个账户都可以发布交易,交易是广播给所有节点的。有些交易是合法的,有些是非法的。谁来决定哪些交易应该被写入下一个区块中?按照什么顺序写呢?如果每个节点自己决定可以吗?如果每个人在本地维护一个区块链,那区块链的统一性得不到保证。因此,账本的内容是要取得分布式的共识(distributed consensus)
 
比特币中共识要解决的一个问题是,有些节点可能是有恶意的。我们假设系统中大多数节点是好的,那么该如何取得共识协议?
 
既然系统中大多数结点都是非恶意的,那么可以采取投票的方式。首先应该确定哪些区块有投票权,有些membership是有严格要求的,这种情况下基于投票的方案是可行的,但是存在很多问题:
  • 没有办法强迫每个结点都投票,有些结点就不投票,当所有区块都不投票的时候区块链会陷入瘫痪状态
  • 效率上的问题,网络延迟不清楚,每轮投票要等多久
  • 比特币系统创建账户是很容易的,创建账户时不需要任何人批准,甚至一个人产生了公私钥对别人都无法得知,只有转账时别人才知道,所以有些人可以不停的创建账户,当超过账户总数的一半时就有了控制权,这种称为女巫攻击(sybil attack)。因此投票方法不可取。
 
比特币账户巧妙的解决了这个问题,它不是按照账户数目投票,而是按照计算力来投票(hash rate)。每个节点都可以在本地组装出一个候选区块,把它认为合法的交易放在里面,然后开始尝试各种nonce值,看哪一个能满足不等式H(block header)≤target的要求。如果某个节点找到了符合要求的nonce,它就获得了记账权。所谓的记账权,就是往比特币账本里写入下一个区块的权利。只有找到这个nonce,获得记账权的节点才有权利发布下一个区块。其他节点收到这个区块之后,要验证这个区块的合法性,比如:
  • 验证括号里block header的内容填的对不对
  • 检查一下nBits域设置的是不是符合比特币协议中规定的难度要求(block header里面有一个域,叫nBits域,实际上它是目标预值的一个编码)
  • 验证不等式H(block header)≤target是否成立
 
假设都符合要求,然后检查block body 里面的交易列表,验证一下每个交易都是合法的,第一是验证要有合法的签名,第二是要验证以前没有被花过。如果有一项不符合要求,这个区块就是不能被接受的。
 
如果以上所有条件都符合,也不一定能被接受。假如生成了一个新区块,怎么知道新区块插在了哪里呢?根据生成区块的指针,有可能就存在一个问题:
 
上图中包含了两个交易,这两个交易指A转账给B,以及A转账给自己。这种情况类似交易回滚,也是不允许的,但这种情况不是double spending,判断一个交易是不是double spending ,是看这个区块所在的分支上币又没有被花掉。如图,一直到第三个区块,币都没有花过,所以这个交易是合法的。虽然该交易是合法的,但是它不在最长合法链(longest valid chain)上。这种称为分叉攻击(forking attack)。所以接收的区块应该是扩展最长合法链
 
区块链在正常情况下也可能出现分岔:两个节点同时获得记账权。每个节点在本地自己组装一个它认为合适的区块,然后去试各种nonce,如果两个节点在差不多同一个时间找到了符合要求的nonce,就都可以把区块发布出去,这时会出现两个等长的分岔,这两条都符合最长合法链原则,那该接受那条呢?
 
比特币协议当中,在默认情况下,每个节点是接受它最早收到的那个。所以不同节点根据在网络上的位置不同,有的节点先收到新生成的其中一个区块,那就接受这个区块;有些节点收到到另一个区块,那就接受另一个区块。什么叫接收一个区块?怎么区分是接收还是不接收?
 
比特币协议中用的是如果它沿着你这个区块继续往下扩展,他就认可你这个区块,如果不扩展就没有认可你这个区块
所以系统中如果出现两个结点差不多同时发布区块的情况,这种等长的、临时性的分叉会维持一段时间,直到其中有一个分叉胜出为止,假设上面的分叉抢先一步先找到了下一个区块,上面的就成为了最长合法链,下面的区块就变成了被丢弃的区块(orphan block),因此当一个区块胜出后,另一个作废的区块得到的比特币是没有作用的,其他诚实的区块是不会承认的。
 
为什么大家要竞争记账权?消耗很多的计算资源去争夺这个记账权,还挺费电的,有什么好处?
 
首选获得记账权的结点本身有一定的权力,他可以决定哪些交易被写到下一个区块里,但是我们设计这个协议的时候,不应该让这个成为争夺记账权的主要动力,如果这个成为主要动力那就有问题了,因为我们是希望凡是合法的交易都应该能被写入到区块链里,那怎么办呢?
 
比特币中设计了一个很巧妙的机制来解决这个问题,即block reward。比特币协议中规定,获得记账权的结点,在发布这个区块的时候,可以有一个特殊的交易,即铸币交易(coinbase transaction),在这个交易里,可以发布一定数量的比特币。一个去中心化的数字货币要解决两个问题,一个是谁有权发行货币,第二是怎么验证交易的合法性,这节课到现在为止都是在讲第二个问题,现在我们来讲第一个问题,谁有权发行货币
 
coinbase transaction是比特币系统中产生新的比特币的唯一方法,其他所有的交易都只不过是把已有的比特币从一个账户转移到另外一个账户,包括我们在交易所中用法币购买比特币,这个也没有产生新的比特币,只不过是你把USDT给了对方,对方把他账户上的比特币转给了你
 
coinbase transaction不需要说明币的来源,因为这个币是凭空造出来的,那能造多少币呢?开始时比特币刚上线的时候,每一个发布的区块可以产生50BTC。协议中规定,21万个区块以后,初块奖励就要减半,就变成了25BTC。再过21万个区块,又要减半
 
现在每个区块只能产生12.5个比特币了,看上去好像减少的很快,现在的出块奖励只有当初的1/4了,而且现在的竞争比以前厉害的很多了,一开始比特币系统里没有多少人去竞争这个事情,比特币系统中刚创立的时候,没有什么人争夺记账权,这50个出块奖励很容易就能得到。现在竞争更激烈了,大家要耗费很多的计算资源,什么先进的设备都要用上了,但是只能得到12.5个比特币。
 
但是大家不要忘了,当初的时候,那50个比特币是不值钱的,那时候没有什么人看好比特币,比特币第一次和实体世界发生交互的时候是有人用两万个比特币买了一个披萨,现在的比特币已经达到5万多美元一枚了,所以发布一个区块可以得到很多钱,这就是为什么有很多结点争破了头去争夺记账权
 
比特币争夺记账权的过程叫挖矿(mining),这个是一种形象的说法,因为比特币有人把他称之为数字黄金(digital gold),求解这个 computation puzzle 去寻找 nonce 的过程就相当于当初的淘金,找到了合法的nonce,获得了记账权就能够得到比特币,相当于挖到了金矿,所以争夺记账权的结点被成为矿工(miner),如果他获得了记账权,我们就说他挖到了矿了,所以区块链中的每个区块都是这样挖矿挖出来的,比特币系统中挖矿的过程和人类历史上淘金热的挖矿的过程有很多类似的地方,都很不容易,都是在一个很大的搜索空间中去寻找,找到的概率很小,但是一旦找到了就能赚一大笔钱,这就是加密货币世界里的挖矿

 

 

推荐阅读