比特币开发指南——区块链

作者: 厚朴儒学 | 来源:发表于2017-11-17 23:05 被阅读189次

    区块链提供了比特币的公共总账——一个有序的、具有时间戳的交易记录。该系统用于防止双重支付和修改以前的交易记录。

    比特币网络中的每个完整节点独立存储只包含由该节点验证过的区块的区块链。当几个节点在他们的区块链中都有相同的区块时,他们就被认为是达成共识的。这些节点遵循以保证共识的验证规则叫做共识规则。本章描述了比特币核心使用过的许多共识规则。

    区块链概述

    image.png

    上面的插图展示了区块链的一个简化版本。交易信息被收集到区块的交易数据部分中。每一个交易的副本都会被hash,配对,再hash,再配对...,重复hash直到一个单独的hash被保留,这就是merkle tree的merkle root。

    merkle root被存储在区块头中。每个区块也存储前一个区块头的hash,从而把区块链接到一起。这确保在不修改记录它的区块和后续所有区块情况下不能修改交易。

    交易也被链接到一起。比特币钱包软件给人的印象就是聪是从一个钱包发送到另一个钱包,但实际上比特币是从一笔交易转移到另一笔交易。每笔交易花费从之前的一个或多个交易中收到的聪,所以一笔交易的输入就是之前的一笔交易的输出。


    image.png

    一笔单独的交易可以创建多个输出,如发送给多个地址,但是在区块链中,一笔特定交易的每次输出只能被用作一次输入。任何后续的引用都是一个双花——试图重复花费相同的聪。

    输出和交易标识符挂钩(TXIDs),TXIDs是已签名交易的hash值。

    因为一笔特定交易的每笔输出只能被花费一次,所以区块链中的所有交易的输出不是被划分成未花费交易输出(UTXOs),就是被划分成已花费交易输出。支付若想有效,必须使用UTXOs作为输入。

    先忽略coinbase交易(后续会陈述),如果交易的输出值超过输入,这笔交易就会被拒绝——但如果输入值超过输出,差值就会被创建了包含该交易的区块的矿工视作交易费用。例如,在上面的插图中,每笔交易收到的都要比联合输入的少10000聪,即实际上支付了10000聪的交易费用。

    工作量证明

    区块链是被网络中的异步节点协作维护,所以比特币要求每个区块证明一定数量的有意义的工作被投入到它的创建当中,确保意图修改过去区块的不可靠的节点要比仅想添加新区块到区块链中的诚实节点做更多的工作。

    注解:对于现实生活中发生的事件的真实性,因为具有空间性我们可以借助物理手段(协议、笔迹、摄像头...)证明;区块链记录了许多数据,同时被不同的节点维护,但是如何保证这些数据的真实性呢?对于虚拟网络中的数据,我们无法在现实生活中一一校验(尤其是不同节点互不认识而且处于不同地理位置),但是数学是不会骗人的,所以我们可以选择借助数学的手段确保数据的真实性。怎么保证呢?首先借助秘钥可以保证该比特币存在且属于支付者,但是这还不够,因为51%攻击仍然会导致双花,所以需要提升计算难度,并在记录了该交易的区块后又增加了6个区块,才能避免攻击者借助分叉产生双花。至于为什么要6个区块才能完全确认呢,其实也是一个概率问题,要在确认了6个区块之后再修改交易记录,成功的概率是非常小的。具体的计算方式可参加中本聪论文。

    把区块链接到一起使得修改包含在任意区块中的交易都不可能,除非修改该区块后的所有区块。因此,修改特定区块的成本随着区块链新块的增加而增加,放大了工作量证明的效果。

    比特币中的工作量证明利用了密码hash的随机性。一个好的加密hash算法将任意数据转换成一个伪随机数。如果以任何方式修改数据并重新运行hash,都会产生一个新的伪随机数,所以没有办法通过修改数据预测hash数。

    为了证明你为创建新块做了一些额外的工作,必须创建一个没有超过确定值的区块头hash。例如,如果最大的hash值是2^256 -1,通过生成小于2^255的值,可以证明你尝试了两种组合。

    注:此处举例有误,最大的hash值是如果是2256-1,尝试了两种组合的结果是2256-1-2=2^256-3。

    在上面给定的例子中,平均每隔一次就会生成一个成功的hash值。甚至可以预估hash请求生成小于目标阈值的可能性。比特币假定一个线性概率,目标阈值越低,平均需要尝试的hash请求次数越多。

    新的区块将被添加到区块链中,如果他们的hash值像共识协议预期的困难值一样具有挑战性。

    注解:新区块的hash值小于目标阈值

    每2016块,网络使用存储在区块头中的时间戳计算这2016个区块的第一个和最后一个的时间差。理想值是1209600秒(2周)

    • 如果花费少于两周的时间生成了2016个区块,预期困难值将会按比例增加(如300%)以便下一个2016个区块花费两周时间生成,如果hash校验速率相同的话。
    • 如果花费超过两周的时间才生成2016个区块,同理预期困难值就会按比例降低(比如减到75%)

    注:时间少于两周有两种可能性。1.算力提升;2.目标阈值太大。可以通过降低目标阈值方式调整。时间多于两周也有两种可能性。1.算力降低,目标阈值太小。可以通过提高目标阈值方式调整。

    (注意:比特币核心实现中的差一错误导致每2016个区块更新使用的时间戳实际仅是2015个区块,产生了轻微的误差)

    因为每一个区块头hash值必须低于目标阈值,而且每个区块都被链接到之前一个区块,所以传播一个修改过的区块需要的算力和整个比特币网络从初始区块创造的时间到现在所花费的算力一样多。只要拥有大多数的网络算力,就可以发起诸如51%攻击交易历史(甚至低于50%的算力也有机会实现该攻击)。

    区块头提供了几个易于修改的字段,例如一个专用的nonce字段,所以获取新的hash值并不要求等待新的交易。而且,工作量证明只hash具有80个字节的区块头,所以区块虽然包含了大量的交易数据并不减缓hash速度,而且添加额外的交易数据仅要求重新计算merkle tree的祖先节点。

    区块高度和分叉

    任何成功计算出来低于目标阈值的区块头的hash值的比特币矿工都可以将整个区块添加到区块链中(假设该区块有效)。这些区块一般都被他们的区块高度锁定——该区块和第一个比特币区块(区块0,即创世区块)之间的区块数量。例如,区块2016是目标阈值困难度第一次被调整的位置。


    image.png

    多个区块可以有相同的区块高度,当两个或多个矿工在大致相同的时间产生各自的区块时,就会发生。这在区块链中产生了一条明显的分叉,如上图所示。

    当矿工在区块链末同时产生区块时,每个节点独立选择接受哪个区块。如果不考虑其他,节点总会使用它们看到的第一个区块。

    最后一个矿工产生了另一个块,它只会连系到相互竞争同时开采的区块中(即分叉)的一个。这使得分叉的一侧比另一侧更强(拥有更大困难度)。假设一个分叉仅包含有效的区块,正常的节点总是遵循最困难的链去重新创建和抛弃属于较短分叉的陈旧的块。(陈旧的块也被称之为孤儿或孤儿块,但这些术语也用于没有父区块的真正的孤儿块)

    如果矿工以不同的目的工作,就会出现长期的分叉。比如一些矿工努力工作扩展区块链,与此同时一些矿工试图发起51%攻击修改交易历史。

    因此在区块链分叉期间,多个区块可能具有相同的高度。区块高度不应用作全局的唯一标识符。相反,区块总是通过它们头部hash被索引到。

    交易数据

    每个区块必须包含一个或更多的交易。这些交易中的第一笔交易必须是coinbase(币基)交易,也叫做生成交易。主要用于收集和花费区块奖励(包括区块补贴和该区块中所有交易的交易费)

    所谓收集和花费区块奖励是指收集区块补贴和交易费用发给矿工自己,这也是一笔交易,只不过输入是区块补贴和交易费用;输出是矿工接收

    coinbase交易的UTXO有特定的条件以至于至少100个区块以后才能花费(作为输入)。这临时性的避免矿工花费了在区块链分叉后,被证明是孤儿块(因此这个coinbase交易会被销毁)的区块奖励和交易费用。

    区块并不要求包含非coinbase交易,但是矿工为了获取交易费用几乎总会纳入额外的交易。

    所有的交易,包括coinbase交易,都以二进制rawtransaction格式编码到区块中。

    rawtransaction格式是被hash以创建交易标识符(txid)。merkle tree通过配对txid然后把它们hash到一起被构成。如果有奇数个txid,没有伙伴的txid将会与自己的副本hash。

    hash的结果就是互相配对然后hash到一起,任意没有伙伴的hash将与自己副本hash。不断重复这个过程直至只有一个hash被保留,这就是merkle root。

    例如,如果交易仅被加入(没有被hash),一个有五个交易的merkle tree如下所示:


    image.png

    正如在简化支付验证(SPV)子章节所讨论的,通过从区块头获取merkle root和从全节点获取的中间hash值列表,merkle tree允许客户端验证交易是否被加入到区块中。完整节点不需要被信任:修改区块头代价很大而且中间的hash值不能修改否则验证会失败。

    例如,为了验证交易D被加入到区块中,除了merkle root,SPV客户端只需要复制C、AB和EEEE hash就行;SPV客户端不需要知道了解任何其他交易。如果该区块中的五个交易都是最大体积,下载整个区块要500000个字节——但是下载三个hash加上区块头只有140个字节。

    注:三个hash是32*3=96 bytes,区块头有80个字节,此处应刨除merkle root 32 bytes,结果就是96 + 80 - 32 = 140 bytes

    共识规则变化

    为了维护共识,所有的完整节点使用相同的共识规则验证区块。然而,有时共识规则也要改变以便引入新的特性或阻止网络攻击。当新的规则被实现时,可能有一段时期未升级节点遵循旧规则,新节点遵循新规则,创建两种可能达成共识的方法:

    1. 遵循新共识规则的区块被升级节点接受但被未升级节点拒绝。例如,一个新的交易特性被用到一个区块中,升级节点理解该特性并接受它,但是未升级节点会因为它违反了旧规则而拒绝它。

    2. 违反了新规则的区块被升级节点拒绝但被未升级节点接受。例如,一个旧交易特性被用到区块内,升级节点因为它违反新规则而拒绝它,但是未升级节点因为它遵循旧规则接受它。

    第一种情况,被未升级节点拒绝,从这些未升级节点获得区块链数据的挖矿软件拒绝和从升级节点获取数据的挖矿软件构建同一条链。这创建了永久的分歧链——一条针对未升级节点,一条针对升级节点——叫做硬分叉。
    [图片上传失败...(image-ed3d82-1510922915738)]

    第二种情况,被升级节点拒绝,有可能保持区块链避免永久分歧如果升级节点控制大部分算力的话。这是因为,这种情况下,未升级节点将像升级节点一样接受所有有效和相同的区块。所以升级节点可以构建一条链,同时被未升级节点接受作为最有效的区块链。这叫做软分叉。
    [图片上传失败...(image-c9f0fc-1510923596926)]

    虽然分叉实际上是区块链的分歧,共识规则的改变常被视作创建一个硬货软分叉。例如,增加区块到1M以上需要硬分叉,此例中,一个实际的区块链分叉不是必要的——但是可能出现。

    共识规则变化常以不同方式被触发。在比特币的头两年,中本聪通过在立即执行新规则的客户端释放向后兼容改变,从而实现了好几次软分叉。多重软分叉如BIP30已经经由标志日触发,在标志日里新的规则在预设的时间或区块高度被执行。这种经由标志日触发的分叉作为用户触发软分叉(UASF)是非常出名的,因为它们是依赖于完整节点在标志日后实施新的规则。

    之后,软分叉等待大量的算力(典型的如75%或95%)示意实施新的共识规则的意愿。一旦示意(支持新规则的人数)阈值通过,所有节点开始实施新规则。例如以矿工触发软分叉(MASF)出名的分叉,因为它们是依赖于矿工触发。

    BIP16,BIP30和BIP3都被作为可能会导致软分叉的改变被实现。BIP50描述了一个意外的硬分叉——通过暂时的降级升级节点的兼容性实现和一个故意的硬分叉——当暂时性降级被移除的时候。

    检测分叉

    未升级节点在两种分叉期间或许会使用和描述不正确的信息,创建几种可能会导致经济损失的情境。尤其是未升级节点或许会传播和接受被升级节点视作无效的交易,所以将永远不会成为共识的最好的区块链。未升级节点或许会拒绝传播已经被添加到最好的区块链上的区块或交易,因此提供不完整的信息。

    比特币核心包括通过关注区块链工作量证明检测硬分叉的代码。如果一个未升级节点收到区块链头部显示比它视作有效的最好的链多至少六个区块工作量证明,该节点在getnetworkinfo PRC结果中提交一个警告并运行-alertnotify命令如果设置了的话。警告操作者未升级节点无法切换到可能是最好的区块链中。

    全节点也校验区块和交易版本号。如果在最近的几个区块中看到的区块或交易版本号比节点使用的版本号更高的话,节点会假设它没有使用当前的共识规则。比特币核心通过getnetworkinfo PRC和-alertnotify命令(如果设置了的话)报告这种情况。

    在以上两种情况下,区块和交易数据如果来自一个很明显不是使用当前共识规则的节点,就不应被信赖。

    连接到完整节点的SPV客户端通过连接到几个完整节点,并确保这些节点在具有相同区块高度的同一条链上检测可能的硬分叉,加上或减去几个区块导致传输延迟和老化块。如果有分歧,客户端就会从具有更弱的链的节点断开。

    SPV客户端也应检测区块和交易版本号,确保他们使用当前的共识规则处理接收到的交易和创建新的交易。

    相关文章

      网友评论

        本文标题:比特币开发指南——区块链

        本文链接:https://www.haomeiwen.com/subject/zimdvxtx.html