区块链 - 比特币开发指南
原文链接: https://bitcoin.org/en/developer-guide#block-chain
翻译:terryc007
版本: 1.0
比特币开发指南
比特币开发指南主要是为你提供一些必要的信息,方面你理解比特币,以及基于比特币来开发一些应用程序。 但它并不是关于比特币的说明书。 为了更好的使用本开发指南,你可以安装当前比特币核内核源码,或者编译好的比特币内核客户端。
有关比特币开发的问题,最好去比特币开发社区提问。 在Bitcoin.org上,有关文档的错误或建议,可作为一个问题提交,或在比特币文档邮件列表发贴。
在下面的开发文档,有一些文字会被省略或隐藏起来: "[…]" 表示额外的数据已被删掉,行尾带有""符号,表示下面部分还是该行内容。如果你移动鼠标停在段落时,带有交叉引用链接的文字会变成蓝色。如果你把鼠标停在带有交叉引用链接的术语上,会显示这个术语的简要提示信息。
区块链
区块链提供了比特币公共账簿,里面包含了有序的,带有时间戳的交易记录。区块链系统被用来防止双发,防止之前的交易记录被篡改。
在比特币网络,每个全节点,各自独立的保存了一份区块链,该区块链中包含仅由自己验证的区块数据。当节点的区块链中,都有相同的区块时,他们被认为有一致的共识。这些被用节点来维持共识的验证规则叫做共识规则。 本节会介绍很多在比特币内核中用到共识规则。
区块链概要
上面的插图是个简易版区块链。一条或多条新的交易记录被收集到区块中,用于存放交易数据的地方。为每条交易生成哈希值,然后哈希值两两配对,再生成哈希值, 然后再对之前的生成的哈希值两两配对,再生成哈希,直到只剩下一个哈希值,最后这个哈希值就是默克树根。
默克尔树根被存放在区块头。每个区块会存放前一个区块头的哈希值,这样把所有的区块串联起来,便成了区块链。 这就保证了,在不修改交易所在的区块,以及该区块后续所有区块前提下,交易是不能被篡改的。
交易记录也会被串联在一起。比特币钱包给我们一个直观的印象是:satoshis(比特币最少的单位 1btc = 100,000,000 satoshis)在钱包之间来回发送,但实际上比特币是从一个交易到另外一个交易的转移。 每个交易会花之前一个或多个交易的satoshis,所以一个交易的输入就是前一个交易的输出。
一个交易可以创建多个输出,就如同当发送到多个地址,但在区块链中,每个特定的交易输出只能被交易输入使用一次。任何后续的引用都是一个禁止的双发 — 试图使用两次satoshis。
交易输出会跟交易id(TXIDS)绑定在一起,所谓的交易id就是交易的哈希值。
因为每个特定交易的输出只能被使用一次,区块链中所有的交易的输出可以被归类为: 未使用交易输出(UTXOs)或者 发费交易输出。对于一个有效的支付,它必须且只能使用UTXOs作为输入。
除coinbase类型交易(区块中的第一个交易,通常被矿工创建,用来认领挖矿奖励,以及提供100个字节的空间,可存放任何数据)外,如果一个交易的输出值大于它的输入值,交易将会被拒绝。 但是如果交易输入值大于交易输出值,其中的差额就是交易费用,用于支付比特币矿工(他们负责创建包含交易的区块)。比如,在上面的图示中,每个交易交易费用少于交易输入总和,实际上支付了10000 satoshis。
工作量证明
网络中的匿名节点一起协作来维持区块链,因此比特币在创建区块时,需要一个有效数量的工作证明,以确保试图修改历史区块的不可信节点,比起可信的节点付出更多的工作量。
把区块串联起来,在不修改当前区块以及后续所有区块情况下,使得修改任何区块中的交易变的不可能。因此,每增加一个新的区块到区块链上,就会增加修改一个特定区块的成本,也就放大了工作量证明的效果。
比特币中使用的工作量证明利用了密码学哈希中的自然随机。一个好的加密哈希算法可把任意数据转化成看似随机的数字。如果数据修改,不管任何方式,当重新计算数据的哈希值时,会产生一个新的随机数,因此没有任何方法在修改数据之后,可以预测其哈希值。
为了证明你为创建区块而做了一些工作,你必须生成一个区块头的哈希值,该哈希值不能超过一个特定的值。比如,如果最大的哈希值是2256-1 , 你可通过生成一个小于2255的哈希值来证明你尝试了两种组合。[?]
在上面的例子中,你生成一个符合要求的哈希值所做的每次尝试在概率上是相同的。 甚至可以估算出一次哈希计算尝试能生成一个小于特哈希值的概率。比特币采用的是线性概率 , 即哈希目标值越小,就需要越多的哈希计算尝试。
新区块只有其生成的哈希值,符合共识协议所预期的难度值,才能被加入到区块链中。 每2016个区块,比特币网络使用保存在区块头中的时间戳,来计算从生成第一个区块到第2016个区块所经历的时间(秒)。这实际的值是 1209600秒(两个星期)。
-
如果生成2016个区块所用的时间少于2个星期,那么生成下2016个区块时,在同一算力的情况下,为保证2016个区块生成所需的时间,恰好是2个星期,那么预期的难度值会按比例提升(差不多300%)。
-
如果生成2016个区块所用的时间多于2个星期,同理,其预期难度会按比例下调(大约75%)。
(注意:在比特币内核实现中,一个单个偏移的错误导致难度的更新从仅需2015个区块变成了2016个区块,这了导致了一点偏移)[?]。
因为每个区块头在生成哈希值时,必须要小于目标阀值,同时每个区块要跟它之前的区块链接起来,要在整个比特币网络中广播修改过的区块,平均上讲,它就需要消耗:从被修改的区块其创建时间起,到当前时间之间所有区块需要的算力。 只有你掌握了大量的哈希算力,你才能可靠的执行51算力攻击(然而需要注意的是,即使是低于50%的哈希算力, 也是有可能成功的实施这样的攻击)。
区块头提供了好几个易于修改的字段,比如一个专用的临时字段,这样可以在不需要等待新的交易,就可以获得新的哈希值。[?] 同样,用于工作量证明时,只需对只有80个字节的区块头计算哈希值就可以,所以在区块中大量的交易数据并不会降低计算哈希值的速度,而只是增加了一些额外的交易数据,需要重新计算默克树中的父节点哈希值。
区块高度和分叉
任何比特币矿工可以把整个区块加入到区块链中,只要它生成的区块头哈希值低于目标阀值。这些区块通过区块高度来寻址。所谓的区块高度 - 指的是当前区块到第一个比特币区块(通常也叫创世块)之间区块的个数。比如,区块高度为2016的区块,就是第一次难度调节后的区块。
多个区块可以同时有相同的区块高度,因为当二个或多个矿工在同一时间产出一个区块是很常见的。这会在区块链创建一个明显的分叉,如下图所示。
当矿工在区块链末端同时产出多个区块时,每个节点会各自接受区块。在不考虑其他节点的情况下,节点通常会选择他们第一次看到的区块,下面我们会讨论。
最终,每个矿工会产出另外一个区块,它会被添加到具有并发竞争挖取的区块链中[?]。这使得分叉链比其他侧链难度更大。假如一个分叉仅包含有效的区块,普通的节点通常会基于难度最大的链进行重建区块链,同时会剔除掉属于较短链上的陈腐区块(有时候也叫孤块,但是孤块,也用来表示那些没有父区块的区块。)。
陈腐区块
就是那些不在难度最大的区块链上,而在其他分叉侧链上的区块,有时候也叫孤块,但是也指那些还没有被本地节点处理的,没有父区块的区块。
很长的分叉是有可能的。 比如不同的矿工为不同的目的而工作,比如一些矿工勤恳的扩展区块链,同时,其他的矿工企图通过实施51%算力攻击修改交易记录。
在一个分叉链上,因为多个区块可能其区块高度是一样的,因此,区块高度不能用于全局唯一标识。而是,通过区块的区块头的哈希值(16进制,反字节序)来引用区块。
交易数据
每个区块必须包含一个,或多个交易。第一个交易必须是coinbase交易。也叫创世交易。它用来接收,消费区块奖励(包括挖出一个区块的补贴,以及该区块内所有交易支付的交易费用)。
创世交易的UTXO有一个特定的条款,就是少于100个区块确认,它是不能被发掉的(即不能做为其他交易的输入)。这可暂时防止矿工发掉从区块获取的区块奖励,因为在一个分叉的链上,矿工挖出的区块有可能以后被认定为是孤块。
区块并不要求包含任何非coinbase交易,但是矿工为了获得他们的交易费用,他们几乎总是会往区块中加一些额外的交易。
所以的交易,包括coinbase交易,会以二进制源交易[?]格式编码存储到区块中。
通过对源交易格式进行哈希来生成交易id(txid). 对于这些交易id,通过对txid两两配对,让后对他们进行哈希计算,来构建默克尔树。如果是个奇数个交易id,则跟自己配对来实现两两配对,再进行哈希计算。
这些哈希计算结果(即哈希值)让后又两两配对,再计算哈希值。任何哈希值如果没有配对的,就跟自己配对,再计算哈希值。这个过程会不断的重复,直到只剩下一个哈希值。这个值就是默克树根。
比如,如果交易不做哈希处理,那么5个交易的默克尔树看起来如下图所示:
ABCDEEEE .......Merkle root
/ \
ABCD EEEE
/ \ /
AB CD EE .......E is paired with itself
/ \ / \ /
A B C D E .........Transactions
如简化支付认证(SPV)段落中所讨论的,通过区块头中的默克尔树根,以及从一个全节点中获取的,一个中间哈希值列表[?],默克尔树允许客户端根自己验证一个交易是否在一个区块中。全节点不必是可信的,因为伪造区块头的成本很高,同时中间哈希值列表是不能伪造的,否则验证会失败。
比如,要验证交易D是否已被加入区块中,一个SPV客户端仅需要交易C,AB,EEEE的哈希值,以及区块的默克尔树根;客户端不需要知道其他任何交易。在这个区块中,假如按这个5个交易所占最大的大小来算,下载这整个区块需要大约500,000字节 - 但是下载3个哈希值,再加上区块头仅需要140字节。
注意: 如果在同一个区块中发现相同的交易id,默克尔树可能会跟区块发生冲突, 会导致一些或者全部交易id被删掉去,这取决于非平衡默克尔树[?]是如何实现的(复制孤立的哈希值)。 因为不同交易有相同的交易id是不可能的,这对于那些忠实的软件不是一个负担,但是如果一个区块的无效状态被缓存,那么必须要对该区块进行检查;否则,一个去除重复交易id的有效区块本可以有相同的默克尔树根,区块哈希值,但因缓存的无效的区块状态,会导致安全bug(比如)CVE-2012-2459, 而使得区块被拒绝掉。
共识规则改变
为维持共识,所以的全节点使用同样的共识规则来验证区块。然而,有时候为了引进新的功能以防止比特币网的滥用,就需要对共识规则进行改变。当新的共识规则被实施后,非升级节点会继续遵循久的共识规则,升级节点遵循新的共识规则,这种情况可能会持续一段时间,这样会导致两种不同的共识。
-
遵循新共识规则的区块被升级节点接受,但非升级节点会拒绝接受。比如,一个的交易功能在被区块使用: 升级节点知道这个功能并接受它,而非升级节点因其他违反老的共识规则而拒绝接受它。
-
违反新的共识规则的区块被升级节点拒绝,但被非升级节点接受。比如,一个滥用的交易功能在区块中使用:升级节点会拒绝它,因为它违反了新的共识规则,而非升级节点则接受它,因为它遵守了老的共识规则。
在第一种情况,非升级节点拒绝接受,那些从非升级节点获取区块数据的挖矿软件,拒绝构建跟那些从升级节点获取区块数据的挖矿软件同样的区块链。这会创建永久的分叉 — 一个非升级节点所在的链,一个升级节点所在的链 — 这也叫做硬分叉。
在第二种情况,区块被升级节点拒绝接受,如果升级节点控制了绝大部分哈希算力,那么避免硬分叉是有可能的。这是因为,在这种情况,非升级节点会接受所有升级节点中有效的区块,因此升级节点能构建更为健壮的区块链,而非升级节点会接受这条链是最好的有效链。 这叫做软分叉。
虽然分叉在区块链中是一个真实的分歧,但是修改共识规则通常使用创建一个硬分叉或软分叉来描述。比如,“增加区块大小到1MB以上需要一个硬分叉”,在这个例子中,是个实际的区块链分叉是不需要的 — 但是这是一个可能的方案。
共识规则的改变可以通过各种方式来激活。在比特币的头两年,中本聪就通过执行过好几次软分叉,他通过发布向后兼容的共识规则更改的客户端,使得这些客户端立即执行新的共识规则。很多的软分叉,如BIP30 ,是通过一个标志日期来激活。就是说新的共识规则在预定的时间或者区块高度被强制激活。像这种通过标志日期来激活的分叉被称之为用户激活软分叉(UASF)。因为在标志日期后,这些分叉要依赖一些有效的用户(节点)去执行新的共识规则。
后来,软叉需要等待大部分哈希算力(通常75%或95%)发出他们已准备好执行新的共识规则的信号。一旦超过所需信号的阈值,所以的节点就会执行新的共识规则。这样的分叉被称之为矿工激活软分叉(MASF),因为分叉依赖矿工去激活。
资源: BIP16, BIP30, BIP34 是通过共识规则的改变来实施的,导致了软分叉。 BIP50 导致了一个意外的硬分叉,后来通过临时降级升级节点的能力,以及当临时的降级被移除后,通过一个有意的硬分叉来解决这个问题。这有一个来自Gavin Andresen,有关未来共识规则改变如何实施的文档。
检测分叉
在上面两种分叉中,非升级节点可以使用,发送不正确的信息,会创建好几个情况导致财产损失。实际上,非升级节点可以转发,接受那些被升级节点认为无效的交易。这些交易永远不会成为全局公认的最佳区块链中的一部分。非升级节点也可以拒绝转发那些已经添加,或即将添加到最佳区块链中的区块,交易,这样就可以提供不完整的信息。
比特币内核里有通过查看区块链工作量证明实现检测硬分叉的代码。如果非升级节点收到一些区块头,这些区块头里面至少有6个区块可以证明,新区块所在的区块链的工作量,比起该节点认为的最佳区块链上的工作量还要多的话,则该节点会在getnetworkinfo
RPC结果中报告一个警告,同时,会运行 -alertnotify
命令,如果有设置的话。这会警告运营者非升级节点不能切换到很有可能是最佳的区块链上。
全节点也能检查区块,交易的版本号。如果最近的区块的区块版本号或交易版本号,比改节点用的区块的版本号要高,它就会认为它没有使用当前的共识规则。比特币内核会通过getnetworkinfo
RPC 来报告这个情况,以及运行-alertnotify
命令,如果有设置的话。
不论哪种情况,如果区块,交易数据明显来自一个没有使用当前共识协议的节点,区块,交易数据就不应该被转发。
SPV客户端会跟全节点链接起来,通过链接多个全节点,同时保证这些全节点在同一区块链的同一个区块高度,考虑到交易延迟,陈腐区块,可加或减掉几个区块,就可以检测可能的硬分叉。如果有分叉,SPV客户端可以断掉那些在更弱链上节点间的连接。
SPV客户端也应监控区块,交易版本号的增加,这样可以保证SPV客户端去处理接收到的交易,并使用新的共识规则创建新的交易。
声明:
文中带有[?]的地方,表示我对此翻译明显感觉不太对的,后续会不断修正。
初次翻译,可能会有些地方翻译的不好,不地道,甚至错误,如果有发现,还请留言,指出,以便我好修正,谢谢!