×

第3章:比特币的机制

96
瓜子_06af
2017.11.23 22:31 字数 19539

本章主要叙述的是比特币的机制。而在前两章中,我们已经在较高层次上进行了讨论,现在我们将深入细节。我们将查看真实的数据结构,真正的脚本,并试图学习比特币的细节和语言,以便在本书的其余部分以精确的方式来建立我们想要谈论的一切。这一章将是具有挑战性的,因为很多细节将会飞向你。你将会学到使Bitcoin成为什么的具体细节和怪癖。

为了回顾上次我们停止的地方,比特币的共识机制给了我们一个附加的分类账,一个我们只能写的数据结构。一旦数据写入了它,它就永远在那里。有一个分散的议定书来确定对该分类帐价值的共识,还有一些矿工们执行该协议并验证交易。他们一起确保交易良好形成,他们还没有被花费,分类帐和网络可以作为货币运作。同时,我们假定存在一种货币来激励这些矿工。在本章中,我们将看看我们如何实际构建该货币的细节,以激励矿工使整个流程运转。

3.1比特币交易

让我们从交易开始,比特币的基本构建区块。我们将使用一个简化的分类帐模型。而不是区块,我们假设单个交易一次性被添加到分类账中。

                                  图3.1一个基于账户的分类账

我们如何在这样的分类账之上建立货币呢?你可能会想到的第一个模型,实际上是许多人对Bitcoin如何工作的心理模型,就是你有一个基于帐户的系统。你可以添加一些创建新硬币并将授予某人的交易。然后,你可以转移他们。一笔交易将会像“我们正在将17个硬币从Alice转移给Bob”一样,并由Alice签署。这就是分类账中包含的所有交易信息。在图3.1中,Alice在第一个交易中收到25个硬币,然后在第二个交易中将17个硬币转入Bob,她的账户将剩下8个比特币。

这种做事方式的缺点是,任何想要确定交易是否有效的人都必须跟踪这些账户的余额。再来看看图3.1。Alice试图转移15个硬币给大卫,如何确定她有这些?为了弄清楚这一点,你必须一直回头看看每一个影响Alice的交易,以确认她试图向大卫转移15个硬币时的净额是否大于15个硬币。当然,我们可以通过一些数据结构来跟踪Alice在每个交易之后的余额,使其更有效率。但是,除了分类账本身之外,还需要大量的额外管理。

由于这些缺点,比特币不使用基于账户的模式。相反,比特币使用的分类帐只跟踪第1章中与ScroogeCoin类似的交易。

图3.2这是一个基于交易的分类帐,与比特币非常接近

交易指定了一些输入和多个输出(ScroogeCoin中召回PayCoins)。你可以将输入视为消费中的硬币(在先前的交易中创建的),并将输出视为正在创建的硬币。对于新货币被铸造的交易,没有硬币被消费(ScroogeCoin中召回PayCoins)。每一个交易都有唯一的标识符。输出从0开始索引,所以我们将第一个输出称为“输出0”。

现在让我们来看看图3.2。交易1没有输入,因为这个交易正在创建新的硬币,它输出给Alice25个硬币。另外,由于这是一个正在创建新硬币的交易,所以不需要签名。现在我们来说,Alice想把那些硬币发给Bob。为了做到这一点,她在我们的例子中创建了一个新的交易,交易2。在交易中,她必须明确提及这些硬币来自的前一笔交易。在这里,她提到交易1的输出0(实际上是交易1的唯一输出),它向Alice分配了25个比特币。她也必须在交易中指定输出地址。在这个例子中,Alice指定了两个输出,17个硬币给Bob,8个硬币给Alice。 而且,这整个事情当然是由Alice签署的,所以我们知道Alice实际上是授权这个交易的。

转换地址。为什么Alice在这个例子中必须向自己汇钱?正如ScroogeCoin中的硬币是不可变的,在比特币中,整个交易输出必须被另一个交易消耗,或者没有它。Alice只想向Bob支付17个比特币,但她拥有的输出价值是25比特币。所以她需要创建一个新的输出,其中8个比特币被送回给自己。这可能是一个不同于拥有25个比特币的地址,但它必须由她拥有。 这就是所谓的转换地址。

有效的验证。当新的交易被添加到分类帐时,如何容易地检查它是否有效?在这个例子中,我们需要查询Alice引用的交易输出,确保它的值为25比特币,而且还没有被花费。查询交易输出很容易,因为我们使用散列指针。为了确保它没有被花费,我们需要扫描引用的交易和最新区块之间的区块链。我们不需要一路回到区块链的开始,也不需要保存任何额外的数据结构(尽管我们将看到,额外的数据结构将加快速度)。

整合资金。像ScroogeCoin一样,由于交易可以有很多输入和许多输出,分割和合并的价值很容易实现。例如,Bob在两次交易中收到钱,一个是17个比特币,另一个则是2个。Bob可能会说,我想有一个交易,我以后可以花费,因为我拥有19个比特币。这很容易——他用两个输入和一个输出创建一个交易,输出地址是他拥有的一个。这让他整合了这两个交易。

联合支付。同样,联合支付也很容易做到。卡罗尔和Bob都想支付给大卫。他们可以创建一个具有两个输入和一个输出的交易,但两个输入由两个不同的人拥有。他们可以创建一个具有两个输入和一个输出的交易,但两个输入由两个不同的人拥有。与上一个例子的唯一区别是,由于这里要求的先前交易中的两个输出来自不同的地址,交易将需要两个单独的签名——一个由卡罗尔和一个由Bob。

交易的语法。在概念上,这真的是Bitcoin交易。现在让我们看看它在比特币中低层的表现。最终,在网络上发送的每个数据结构都是一个位串。图3.3中显示的是非常底层的,但是这进一步被编译成一个不可读的紧凑二进制格式。

图3.3一个实际的比特币交易

如图3.3所示,交易有三个部分,一些元数据,一系列输入和一系列输出。

·元数据这里有一些管理信息——交易的大小,输入的数量和输出的数量。整个交易的哈希是用作交易的唯一ID。这就是允许我们使用哈希指针来引用交易的原因。最后还有一个“lock_time”字段,我们稍后讲述。

·输入。交易输入形成一个数组,每个输入都具有相同的形式。一个输入指定一个先前的交易,所以它包含该交易的哈希,并作为一个哈希指针存在。输入中还包含了要声明的先前交易的输出索引。然后,还有一个签名。请记住,我们必须签署,以表明我们实际上有能力要求前面的交易输出。

·输出。输出又是一个数组。每个输出只有两个字段。它们各自具有一个值,并且所有输出值的总和必须小于或等于所有输入值的总和。如果输出值的总和小于输入值的总和,则差额是发布此交易的矿工的交易费用。

··然后,有一个有趣的行列,看起来像我们想要的收件人地址。每个输出都应该是一个特定的公共密钥,而且这个字段中有一些东西看起来像是公钥的哈希。但是还有一些其他的东西看起来像一组命令。事实上,这个区域是一个脚本,我们即将讨论这个。

3.2比特币脚本

每个交易输出不仅指定一个公钥。它实际上指定了一个脚本。 什么是脚本,为什么要使用脚本? 在本节中,我们将研究Bitcoin脚本语言,并了解为什么使用脚本而不是简单地分配公钥。

比特币中最常见的交易类型是通过使用正确的密钥进行签名来兑换先前的交易输出。在这种情况下,我们希望交易输出说:“这可以通过地址X所有者的签名来兑换。”回想一下地址是一个公钥的散列。所以只是指定地址X不告诉我们什么是公钥,而不是给我们一个方法来检查签名!所以交易输出必须说:“这可以通过哈希到X的公钥来兑换,以及该公钥的所有者的签名。”我们将看到,这正是最常见的Bitcoin脚本类型。

图3.4一个PubkeyHash支付的脚本,Bitcoin中最常见的输出脚本类型

但是这个脚本会发生什么?谁运行它,以及这个指令序列究竟如何执行上面的语句的?秘密在于输入包含脚本而不是签名。为了验证交易是否正确地兑换了以前的交易输出,我们将新交易的输入脚本和较早的交易的输出脚本相结合。我们简单地连接它们,并且生成的脚本必须成功运行才能使交易有效。这两个脚本称为scriptPubKey和scriptSig,因为在最简单的情况下,输出脚本只是指定公钥(或公开公钥哈希的地址),输入脚本使用该公钥指定签名。组合脚本可以在图3.5中看到。

比特币脚本语言。脚本语言是专门为Bitcoin而设计的,它被称为“脚本”或比特币脚本语言。它与一种称为Forth的语言有很多相似之处,一种旧的,简单的,基于堆栈的编程语言。但是您不需要通过了解Forth来了解Bitcoin脚本。脚本的关键设计目标是简单而紧凑,而且还具有对加密操作的本机支持。因此,例如,有专门的指令来计算哈希函数并计算和验证签名。

脚本语言是基于堆栈的。这意味着每条指令都以线性方式执行一次。特别的是Bitcoin脚本语言中没有循环。所以脚本中的指令数量给了我们运行多长时间以及可以使用多少内存的上限。语言不是图灵完备的,这意味着它不具备计算任意强大功能的能力。这就是设计——矿工必须运行由网络中的任意参与者提交的这些脚本。我们不想让他们有权提交一个可能有无限循环的脚本。

图3.5要检查事务是否正确兑换输出,我们将通过参考输出交易(bottom)的scriptPubKey附加到兑换交易(top)的scriptSig来创建组合脚本。请注意,包含一个'?'。我们使用这个符号表示我们稍后会检查以确认这等于兑换脚本中提供的公钥的哈希值。

当Bitcoin脚本被执行时,只有两个可能的结果。它可以成功执行,没有错误,在这种情况下,交易是有效的。 或者,如果脚本执行时出现任何错误,则整个交易将无效,并且不应被接受到块链中。

比特币脚本语言非常小。只有256个指令的空间,因为每个指令由一个字节表示。在这256位中,15位目前被禁用,75位被保留。保留的指令代码尚未分配任何具体含义,但可能稍后添加指令。

许多基本指令都是你希望在任何编程语言中使用的指令。有基本的算术,基本的逻辑——如'if'和'then'——,抛出错误,不会抛出错误,还有提前返回。最后,还有一些加密指令,其中包括哈希函数,签名验证指令,以及一个称为CHECKMULTISIG的特殊而重要的指令,可以让你用一条指令来检查多个签名。图3.6列出了Bitcoin脚本语言中最常见的一些指令。

CHECKMULTISIG指令需要为阈值指定n个公钥和一个参数t。为了有效地执行该指令,从n个公共密钥中的t个,必须至少有t个签名才是有效的。我们将在下一节中介绍一些你使用多重签名的例子,但是应该立即清楚这是一个非常强大的原语。我们可以用紧凑的方式表达n个指定实体的T必须为交易有效而签名的概念。

顺便说一句,多重签名实现有一个错误,它一直在那里。CHECKMULTISIG指令从堆栈中弹出一个额外的数据值,并忽略它。这只是比特币语言的一个怪癖,必须通过在堆栈上放置一个额外的虚拟变量来处理它。这个bug是在原来的实现中,修复它的代价远远高于它造成的损害,我们将在后面的3.5节中看到。在这一点上,这个错误被认为是比特币的一个功能,因为它不会消失。

图3.6列出了常见的脚本指令及其功能。

执行脚本。要以基于堆栈的编程语言执行脚本,我们需要的是一个堆栈,我们可以将数据推送到其中并从中弹出数据。我们不需要任何其他内存或变量。这就是使它计算上如此简单的原因。有两种类型的指令:数据指令和操作码。当数据指令出现在脚本中时,该数据被简单地推到堆栈顶部。另一方面,操作码执行一些功能,通常将其作为堆栈顶部的输入数据。

现在来看看图3.5中的Bitcoin脚本是如何执行的。参见图3.7,我们在每个指令之后显示堆栈的状态。这个脚本中的前两个指令是数据指令——签名和公钥用来验证签名在兑换交易中输入指定交易scriptsig组件。正如我们所提到的,当我们看到一个数据指令时,我们只是把它推到堆栈上。脚本的其余部分是在参考交易中的交易输出指定ScriptPubKey组件。

首先我们有重复的指令OP_DUP,所以我们只需将公钥的副本推到堆栈的顶部。下一个指令是OP_HASH160,它告诉我们弹出顶部值,计算其加密哈希,并将结果推送到堆栈的顶部。当这个指令完成执行时,我们将堆栈顶部的公钥替换成它的哈希。

图3.7比特币执行脚本。在底部,我们将显示脚本中的指令。数据指令用周围的尖括号表示,而操作码以“OP_”开头。在顶部,我们在执行该指令之后显示堆栈。

接下来,我们将再次将数据推送到堆栈上。回想一下,这个数据是被引用交易的发送者指定的。它是发送者指定的公钥哈希值;必须使用相应的私钥来生成签名来兑换这些硬币。此时,堆栈顶部有两个值。有发件人指定的公共密钥的哈希,以及收件人在尝试索取硬币时使用的公共密钥的哈希。

此时,我们将运行EQUALVERIFY命令,该命令检查堆栈顶部的两个值是否相等。如果没有,则会抛出错误,脚本将停止执行。但在我们的例子中,我们假设他们是相等的,也就是说硬币的收件人使用正确的公钥。该指令将消耗堆栈顶部的两个数据项,而堆栈现在包含两个项目——一个签名和一个公钥。

我们已经检查了这个公钥实际上是引用交易的指定公钥,现在我们必须检查签名是否有效。这是一个很好的例子,其中比特币脚本语言是用加密技术构建的。即使在逻辑方面这是一个相当简单的语言,还有一些非常强大的指令,就像这个“OP_CHECKSIG”指令一样。这个单个指令从堆栈中弹出这两个值,并且一次完成整个签名验证。

但这是什么签名呢?签名函数的输入是什么?事实证明,只有一件事你可以在比特币上签署——一个完整的交易。因此,“CHECKSIG”指令会从堆栈中弹出两个值,即公钥和签名,并使用该公钥验证整个交易的有效签名。现在我们已经执行了脚本中的所有指令,堆栈中没有任何内容。如果没有任何错误,此脚本的输出将简单地表示交易是有效的。

实际上使用的是什么呢?从理论上讲,脚本可以让我们在某种意义上指定为了花钱而必须满足的任意条件。但是,截至今天,这种灵活性并没有被非常重视。如果我们看看到目前为止比特币历史上实际使用的脚本,绝大多数,99.9%都是完全相同的脚本,这实际上是我们在示例中使用的脚本。正如我们所看到的,这个脚本只是指定一个公共密钥,并且需要该公钥的签名才能花费这些硬币。还有一些其他指令可以使用。MULTISIG使用了一些特殊类型的脚本,称为Pay-to-Script-Hash,我们将在稍后讨论。但除此之外,在脚本使用方面还没有太多的多样性。这是因为Bitcoin节点在默认情况下具有标准脚本的白名单,并且拒绝接受不在列表中的脚本。这并不意味着这些脚本根本不能使用;它只是使它们更难使用。事实上,这个区别是一个非常微妙的点,当我们谈论比特币对等网络时,我们将再回来一点。

销毁证明。销毁证明是一个永远不能被挽回的脚本。将硬币发送到销毁证明脚本,它们就已确定被销毁,因为没有任何可能再去花费它们。一种使用销毁证据的方式是通过强迫人们摧毁比特币以便在新系统中获得硬币来引导比特币的代币。我们将在第10章中更详细地讨论这一点。销毁证明相当容易实现:OP_RETURN操作码如果达到,就会抛出错误。无论在OP_RETURN之前放置什么值,该指令将最终执行,在这种情况下,该脚本将返回false。

因为抛出错误,OP_RETURN之后的脚本中的数据将不被处理。因此,这是人们将任意数据放入脚本中,从而进入区块链的一个机会。如果出于某种原因,你想写你的名字,或者如果你想要时间戳,并在某个特定的时间证明你知道一些数据,你可以创建一个非常低价值的比特币交易。你可以销毁一个非常小量的货币,但你想要将任何你想的东西写入区块链,这就需要它永远保持下去。

支付脚本的哈希。Bitcoin脚本工作方式的一个后果是硬币的发送者必须准确地指定脚本。但这有时可能是一件很奇怪的事情。比方说,你作为消费者正在在线购物,而你即将订购一些东西。你说,“好的,我准备付钱了。告诉我我应该将硬币发送到哪个地址。“现在,你购物的公司正在使用MULTISIG地址。那么,由于硬币的花费必须具体说明,所以零售商必须回过头来说:“哦,很好,我们现在做点什么不一样的,我们正在使用MULTISIG。 我们将要求您将硬币发送到一些复杂的脚本上。你可能会说,“我不知道该怎么做。这太复杂了。作为消费者,我只想发送到一个简单点的地址”

对于这个问题,比特币有一个聪明的解决方案,它不仅适用于多个SIG地址,而且适用于任何复杂的条件,管理可以花费的硬币。相比于告诉发件人“发送你的硬币到这个公钥的哈希”,接收方可以告诉发件人“发送你的硬币到这个脚本的哈希。强加兑换那些硬币的条件,有必要揭示具有给定哈希的脚本,并进一步提供使脚本评估为真的数据。发件人通过使用具有上述语义的付费脚本散列(P2SH)交易类型来达到此目的。

具体而言,P2SH脚本只是简单地对堆栈的顶部值进行哈希,检查它是否与提供的哈希值相匹配,然后执行特殊的第二步验证:将堆栈中的顶层数据值重新诠释为指令序列,并二次作为脚本执行,用堆栈的其余部分作为输入。

获得P2SH的支持非常复杂,因为它不是Bitcoin初始设计规范的一部分。它是事后补充的。这可能是原始规范中没有的,添加到比特币中最显著的功能。它解决了一些重要的问题。它消除了发件人的复杂性,因此接收方只需指定发送者发送钱的哈希。在上面的例子中,Alice不用担心Bob正在使用多个SIG;她只是发送到Bob的P2SH地址,Bob有责任在兑换硬币时指定花哨的脚本。

P2SH也有一个很好的效率增益。矿工们必须跟踪尚未兑换的输出脚本集,并且使用P2SH输出,输出脚本现在要小得多,因为它们只用指定一个哈希。所有的复杂性被推送到输入脚本。

3.3比特币脚本的应用

现在我们了解了比特币脚本的工作原理,让我们来看看一些可以用这种脚本语言实现的强大应用程序。事实证明,我们可以做许多优美的事情,这将证明具有脚本语言的复杂性,而不是仅仅指定公钥。

托管交易。Alice和Bob想彼此做生意——Alice想用比特币支付Bob,而Bob把一些实体商品送给Alice。问题在于,Alice想收到商品后才付款,但Bob不想在付款前发货。我们能做些什么呢?在实践中比特币使用的一个不错的解决方案是引入第三方并进行代管交易。

托管交易可以很简单地用MULTISIG来实现。Alice不把钱直接发送给Bob,而是创建了一个需要三个人中的两个签署才能兑换硬币的MULTISIG交易。这三个人将是Alice,Bob和一些第三方仲裁员朱迪,如果有任何争议,他将会起作用。所以Alice创造了一个2/3的MULTISIG交易,发送一些她拥有的硬币,并指定如果Alice,Bob和Judy中的两个签署了就可以花费。这个交易被包括在区块链中,在这一点上,这些硬币在Alice,Bob和朱迪之间进行代管,这样他们中的任何两个可以指定硬币应该去哪里。在这一点上,Bob相信将货物发送给Alice是安全的,所以他会邮寄或交付它们。现在在正常情况下,Alice和Bob都是诚实的。所以,Bob会发送Alice所期待的货物,当Alice收到货物时,Alice和Bob都签署了一笔交易,赎回代管的资金,并将它们发给Bob。请注意,在这种情况下,Alice和Bob都是诚实的,朱迪根本就不必参与其中。这里没有争议,Alice和Bob的签名符合MULTISIG交易的2/3要求。所以在正常情况下,效率并不比Alice直接把硬币发送给Bob低。它在区块链上只需要一个额外的交易。

但是,如果Bob没有真正发送货物或者丢失了邮件,会发生什么?或者商品与Alice订购的不一样?Alice现在不想支付Bob,因为她认为她被骗了,她想要把钱要回去。所以Alice绝对不会签署一笔向Bob发放资金的交易。但Bob也可以否认任何不法行为,并拒绝签署将资金退回Alice的交易。这就是朱迪需要参与的地方。朱迪要决定这两个人中哪一个应该得到这些钱。如果朱迪判决Bob作弊,朱迪愿意与Alice一起签署交易,将钱从代管处寄回Alice。Alice和朱迪的签名符合MULTISIG交易的2/3的要求,而Alice则会收回她的钱。当然,如果朱迪认为Alice在这里是不对的,而Alice只是简单地拒绝付钱,朱迪就能和Bob一起签署一笔交易,把钱交给Bob。所以朱迪决定两种可能的结果。但最好的事情是她不必参与,除非有争议。

绿色地址。另一个很酷的应用是所谓的绿色地址。Alice想支付Bob,而Bob却离线。由于他离线,Bob不能去查看区块链,看Alice发送的交易是否真实的在那里。也有可能鲍伯在线,但没有时间去查看区块链,并等待交易被确认。记住,通常我们希望一个交易处于区块链中,并且在我们相信它处于区块链中之前,需要六个区块来确认,这大概需要一个小时。但是对于一些商品,如食物,Bob不能等待一个小时才交付。如果Bob是卖热狗的街头小贩,Alice不太可能等待一个小时才能收到她的食物。或者也许Bob因为其他原因与互联网根本没有任何连接,因此不能检查区块链。

为了解决这个问题,接受者不必访问区块链却能使用比特币来发送资金,我们必须引入另一个第三方,我们称之为银行(实际上,它可以是一个兑换或任何其他金融中介)。Alice打算和她的银行谈谈,说:“嘿,这是我,Alice。我是你的忠实客户。这是我的卡或身份证。我真的很想在这里支付Bob,你能帮助我吗?”银行会说,“当然。我会从你的帐户中扣除一些钱。并将一笔交易从我的一个绿色地址交给Bob。”

所以,注意到这笔钱直接从银行转到了Bob。当然,这些钱可能改变地址回到银行。但基本上,银行从银行控制的地址向Bob付款,我们称此地址为绿色地址。此外,银行保证不会再花这笔钱。所以一旦Bob看到这笔交易是由银行签署的,如果他相信银行的担保不会双重支付这笔资金,他可以接受这笔在区块链中确认的钱最终将是他的。

注意,这不是比特币强制担保。这是一个现实世界的保证,为了使这个系统发挥作用,Bob不得不相信,现实世界中的银行关心他们的声誉,不会因为这个原因而花费双倍的钱。银行可以说:“你可以看看我的历史交易记录。我使用这个绿色地址很长一段时间了,我从来没有双重支付过。所以我今后也不太可能这样做。”因此,Bob不再不得不信任他一无所知的Alice。相反,他信任银行,相信他们不会将发给他的钱双重支付。

当然,如果银行双重支付,人们就会停止信任它的绿色地址。事实上,实施绿色地址的两个最著名的在线服务是Instawallet和Mt. Gox,并且都最终崩溃了。今天,绿色地址并没有得到太多的使用。当这个想法首次提出时,它作为一种更快速地进行付款并且无需访问区块链的方式,产生了很大的兴奋。然而,现在人们对这个想法变得非常谨慎,担心这对银行的信任过高。

有效的微额支付。比特币脚本的第三个例子是高效的微额支付。Alice是一个不断为Bob提供的一些服务进行支付的客户。例如,Bob可能是Alice的无线服务提供商,并要求她为在她手机上谈论的每分钟支付一小笔费用。

每分钟都为Alice的通话创建一个比特币交易将无法正常运行。这将造成太多交易,且交易费用累加。如果这些交易中的每一笔交易的价值与交易费用一一对应,Alice将为此付出相当高的代价。

我们希望最终能将所有这些小额支付合并成一笔大的支付。事实证明,有一个很好的方法可以做到这一点。我们从MULTISIG交易开始,Alice将需要花费最大的金额,该支付输出要求Alice和Bob签署以释放硬币。现在,在Alice使用该服务的第一分钟之后,或者Alice第一次需要进行小额支付时,她会签署一笔交易使用那些发送到MULTISIG地址的硬币,该交易将支付一个单位给Bob,并将其余部分退回给Alice。在使用该服务的下一分钟之后,Alice签署另一笔交易,此次向Bob支付两个单位,并将剩余部分交给自己。请注意,这些只是由Alice签名,尚未由Bob签名,也不会被发布到区块链。随着Alice每分钟都使用该服务,她继续将这些交易发送给Bob。最后,Alice将完成使用该服务,并告诉Bob:“我已经完成了,请切断我的服务。”此时,Alice将停止签署额外的交易。听到这话,Bob会说“太好了”。 我会断开您的服务,并把您发送给我的最后一个交易签署,将其发布到区块链上。”由于每笔交易都向Bob支付了一点费用,而Alice稍微少了一点,Bob兑换的最后一笔交易完全是支付他所提供的服务的,其余款项则退还给Alice。Alice签署的所有交易都不会出现在区块链上。Bob不必签署他们,他们只是会被丢弃。

从技术上讲,这些交易都是双重支付的。之所以,不同于我们专门设法避免双重支付的绿色地址的情况,在一个强有力的保证下,通过这种小额支付协议,我们实际上产生了大量潜在的双重支付。但实际上,如果双方都正常运作,Bob永远不会签署任何交易,除了最后一个交易,在这种情况下,这个区块链实际上并不会看到双重支付的尝试。

还有另一个棘手的细节:如果Bob从不签署最后一笔交易呢?他可能会说:“我很高兴让硬币永远躺在托管那里”,在这种情况下,也许硬币不会移动,但Alice将失去一开始支付的全部价值。有一个非常聪明的方法来避免我们刚才提到的这个问题,现在我们来解释。

锁定时间。为了避免这个问题,甚至可以在微支付协议开始之前,Alice和Bob都将签署一笔交易,将Alice的所有资金退还给她,但退款在将来某个时候段内被“锁定”。所以,在Alice签名之后,广播之前,首先将资金转入托管的MULTISIG交易中,她将想从Bob那得到这笔退款交易,并一直持续下去。这样做可以保证,如果到达时间t,而Bob没有签署Alice发送的任何小交易,Alice可以发布直接将所有钱直接退还给她的交易。

它被说定到时间t是什么意思呢?回想一下,当我们查看比特币交易中的元数据时,有这样一个lock_time参数,我们并没有解释。它的工作方式是,如果你为锁定时间指定了零以外的任何值,则会告知矿工在指定的锁定时间之前不要发布交易。基于放入区块的时间戳,交易在特定区块号或特定时间点之前将无效。因此,这是一种准备交易的方法,它只有在将来还没有投入的时候才能使用。它作为Alice的安全阀,在微支付协议中工作的相当不错,Alice知道如果鲍勃从不签名,最终她将能够收回她的钱。

希望这些例子告诉你,我们可以用Bitcoin脚本做一些漂亮的事情。我们讨论了三个简单实用的例子,但还有很多其他已研究的。其中的一个是多玩家彩票,一个非常复杂的多步骤协议,大量的交易具有不同的锁定时间,并且人们在欺骗的情况下进行托管。还有一些漂亮的协议使用脚本语言来允许不同的人将他们的硬币结合在一起,并将它们混合在一起,这样很难追踪谁拥有哪个硬币。我们将在第6章中详细介绍。

智能合约。我们在本节中看到的合约的一般术语是智能合约。这些是比特币中我们有一定程度技术执行的合约,然而传统上它们是通过法律或仲裁法院执行的。Bitcoin有一个非常酷的功能,我们可以使用脚本,矿工和交易验证来实现托管协议或微支付协议,而不需要集中的权限。

对智能合约的研究远远超出了我们在本节中看到的应用。今天,有许多类型的智能合同,人们希望能够执行,但不被Bitcoin的脚本语言所支持。或者至少没有人想出创造性的方式去实现它们。正如我们所看到的,有一点创造力,你可以使用Bitcoin脚本做很多事,就像现在一样。

3.4比特币区块

到目前为止,本章中我们已经研究了如何构建和赎回个别交易。但正如我们在第2章中看到的那样,交易被分组成区块。为什么是这样?基本上,这是一个优化。如果矿工不得不单独就每个交易达成共识,那么系统可以接受新交易的比例将会低得多。此外,区块的哈希链比交易的哈希链短得多,因为可以将大量的交易放入每个区块中。这将使得验证区块链数据结构更有效率。

区块链是两个基于哈希的不同数据结构的巧妙组合。第一个是区块的哈希链。每个区块都有区块头,哈希指针指向一些交易数据,以及一个指向序列中前一个区块的哈希指针。第二个数据结构是一个每个区块树,包含该区块中的所有交易。这是一个Merkle树,并允许我们以有效的方式对区块中的所有交易进行摘要。正如我们在第1章中看到的,为了证明一个交易被包含在一个特定的区块中,我们可以提供一个通过树的路径,其长度在区块中的交易数量上是对数的。总而言之,区块由头部数据组成,后面是以树结构排列的交易列表。

图3.8比特币区块链包含两个不同的哈希结构。第一个是将不同区块相互链接的区块哈希链。第二个是每个区块的内部,是区块内的交易Merkle树。

头部主要包含我们上一章中简要讨论的挖掘难题相关的信息,我们将在第5章中重新讨论。回想一下,区块的哈希头部必须以大量的零开始,以使区块有效。头部还包含矿工可以更改的“随机数”、时间戳和“位”,这是该区块被发现难度的指示。头部是挖掘中唯一哈希过的。所以要验证一个区块链,我们需要做的就是查看头部。包含在头部中的唯一交易数据是交易树的根——“mrkl_root”字段。

图3.9 coinbase交易。一个coinbase交易创造新的硬币。它不会兑换以前的输出,且它有一个空的哈希指针来表示这一点。它具有可以包含任意数据的coinbase参数。Coinbase的价值是区块奖励加上此区块中包含的所有交易费用。

区块的另一个有趣的事情是他们在Merkle树中有一个特殊的交易,称为“coinbase”交易。这与Scroogecoin中的CreateCoins类似。因此这就是在比特币中发生的新硬币创造。它看起来像一个正常的交易,但有几点区别:(1)它总是有一个单一的输入和一个单一的输出,(2)输入不会兑换先前的输出,因而包含一个空的哈希指针,因为它是铸造新的比特币而不是消耗现有的硬币,(3)输出的值目前是25个多一点的比特币。输出值是矿工从区块中获得的收入。它由两部分组成:平均开采奖励,由系统设定,每210,000个区块(约4年)减半,以及从每次区块中交易收取的交易费用。4)有一个特殊的完全任意的“coinbase”参数——矿工可以把任何想要的东西放在那里。

有趣的是,在比特币开采的第一个区块中,“coinbase”参数引用了泰晤士报上一个涉及大臣银行的故事。这被解释为比特币创世区块的政治评论动机。这也是第一个区块矿石开采在2009年1月3日之后的一种证明。使用coinbase参数成为矿工支持不同新功能的一种方式。

为了更好地了解区块格式和交易格式,最好的方法是自己探索区块链。有很多网站可以访问这些数据,例如blockchain.info。你可以查看交易的图表,查看哪些交易对其他交易进行赎回,用复杂脚本的脚本查找交易,并查看区块结构,查看区块是如何引用其他区块的。由于区块链是公共数据结构,开发人员已经搭建了漂亮的包装器来以图形的方式进行探索。

3.5比特币网络

到目前为止,我们一直在谈论参与者发布交易的能力,并将其加入区块链,这好像魔法一样的发生。实际上,这是通过比特币网络发生的。它是一个p2p对等网络,它继承了许多从各种其他目的提出的对等网络的想法。在比特币网络中,所有节点都是对等的。这里面没有层次结构,没有特殊的节点或主节点。它运行在TCP上并具有随机拓扑性,其中每个节点与其他随机节点对等。新节点可以随时加入。事实上,事实上,您可以下载Bitcoin客户端,将您的计算机作为一个节点启动,它将具有与Bitcoin网络上其他所有节点相同的权限和功能。

网络随着时间的推移而变化,由于节点的进入和离开而变得非常动态。没有明确的离开网络的方法。相反,如果节点在一段时间内没有被听到——三个小时是硬编码到普通客户端的期限时间——其他节点就开始忘记它。网络以这种方式优雅地处理离线的节点。

回想一下,节点连接到随机节点,并且没有任何类型的地理拓扑。现在你启动一个新的节点并想加入网络。你从一个你知道的节点开始一个简单的消息。这通常称为你的种子节点,并且有几种不同的方法可以通过查找种子节点的列表来尝试连接。你发送一条特殊消息,说:“告诉我你所了解的网络中所有其他节点的地址。”你可以使用你想要了解的新节点重复此过程。然后,你可以选择一个与之对应,你将因此成为Bitcoin网络中功能完整的一员。牵扯到随机性,有几个步骤,理想的结果是你与一个随机的节点集对应。要加入网络,你需要知道的是如何与一个已经在网络上的节点联系。

网络有什么好处?当然是为了维持区块链。所以要发布一个交易,我们想让整个网络听到。这通过简单的洪水算法(有时称为八卦协议)来实现。如果Alice想向Bob支付一些钱,她的客户端会创建它的节点,并将该交易发送给所有与其对等的节点。这些节点中的每一个执行一系列检查以确定是否接受和传达该交易。如果检查通过,节点又将其发送到它的所有对等节点。听到一个交易的节点将它放在他们已经听到但尚未出现在区块链上的交易池中。如果一个节点听到的交易已经在它的交易池,则不会进一步广播它。这样可以确保洪水协议终止,交易不会永远环绕网络。请记住,每个交易都由其哈希唯一标识,因此在池中查找交易很容易。

当节点听到一个新的交易时,他们是如何决定是否应该传播它?这里有四个检查步骤。第一个也是最重要的检查是交易验证——交易必须对当前区块链有效。节点为每个先前赎回的输出运行脚本,并确保脚本返回true。其次,他们检查这里兑换的输出是否还没有被花费。第三,如前所述,它们不会转发已经看到的交易。第四,默认情况下,节点将仅接受并继承基于小白名单脚本的“标准”脚本。

所有这些检查都只是健全检查。运行良好的节点都实现了这些,以使网络保持正常运行,但没有任何规则说节点必须遵循这些具体步骤。由于它是一个对等网络,任何人都可以加入,因此,节点可能会转发双重支出,非标准交易或完全无效的交易。这就是为什么每个节点必须自己检查。

由于网络中存在延迟,因此节点可能会以不同的待处理交易池视图结束。当尝试双重支付时,这变得特别有趣和重要。假设Alice试图向Bob和Charlie支付相同的比特币,并且大致同时发出这两笔交易。一些节点首先听到Alice→Bob的交易,而其他节点首先会听到Alice→Charlie的交易。当一个节点听到这些交易之一时,它会将其添加到它的交易池中,如果它稍后听到另一个交易,它将被认为是一个双重支付。节点将舍弃后一个交易,并且不会传达它或将其添加到其交易池中。因此,节点将暂时不同意将哪些交易放入下一个区块中。这称为比赛条件。

好消息是,这完全没问题。无论谁挖下一个区块,基本上都会打破平局,决定这两桩待处理交易中的哪一个最终将被永久性地丢进一个区块。让我们说Alice→Charlie的交易成为一个区块。当Alice→Bob交易的节点听到有关此区块的信息时,它们将从内存池中删除交易,因为它是双重支付。当Alice→Charlie交易的节点听到有关此区块的信息时,它们会将交易从内存池中删除,因为它已经被嵌入到区块链中。所以一旦这个区块传播到网络就不会再有分歧了。

因为默认行为是让节点挂他们首先听到任何消息,所以网络位置很重要。如果两个冲突的交易或区块在网络中的两个不同位置被公布,那么它们将在整个网络中开始泛洪,并且节点首先看到哪个交易将取决于它在网络中的位置。

当然,这假设每个节点都实现这些逻辑,在这些逻辑中,它们首先保留任何他们听到的内容。但是,没有中央权威机构强制执行这一点,节点可以自由地实现他们想要的任何其他逻辑,以选择哪些交易要保留以及是否要转发交易。我们将在第5章中更加关注采矿者的激励。

边栏零确认交易和替换费。在第2章中,我们研究了零确认交易,收件人一旦在网络上广播就接受该交易。这不是为了针对双重支付的安全设计。但正如我们所看到的那样,在交易冲突的情况下,矿工的默认行为是包含他们首先收到的交易,这使得双重支付针对零确认交易适当的困难。因此,由于方便,零确认交易变得普遍。

自2013年以来,已经有兴趣将默认政策更改为替代费用(RBF),如果他们听到包含较高费用的冲突交易,节点将替换其交易池中的待处理交易。这是矿工的理性行为,至少在短期意义上,它给了他们更好的收费。但是,在实践中,替代费用会使得双重支付针对零确认攻击更加容易。

因此,无论是防止或阻止在RBF世界中的双重支付的技术问题,以及比特币是否应该尽可能支持零确认,还是放弃它的哲学问题,替代收费问题就引起了争议。我们不会深入讨论这场旷日持久的争论,但Bitcoin最近通过了“选择加入”RBF网络,交易可以标记自己(使用序列号字段)有资格使用更高费用的交易来替换。

到目前为止,我们一直在讨论交易的广播。每当矿工找到新的区块时,宣布新区块的逻辑与传播新的交易几乎完全相同,并且都受到相同竞争条件的限制。如果两个有效区块同时开采,则只有其中一个可以包含在长期共识链中。最终将包括哪些区块将取决于其他节点建立在哪个区块上,而不会进入共享链的区块将是孤立的。

验证区块比验证交易更复杂。除了验证标题并确定哈希值在可接受的范围外,节点必须验证区块中包含的每个交易。最后,一个节点只有当区块建立在最长的分支上时,才会转发这个区块,这是基于区块视图(实际上是一个块树)看起来像什么的。这样可以避免分叉的建立。但就像交易一样,节点可以根据需要实现不同的逻辑——它们可能会传达无效的区块或从区块链中较早点构建的区块。这将构建一个分叉,但没关系。该协议被设计为能承受这一点。

图3.10区块传播时间该图显示了一个区块到达网络中各种节点百分比所需的平均时间。

洪水算法的延迟是多少?图3.10中的图表显示新区块传播到网络中每个节点的平均时间。这条线显示了25个、50个和75个百分位数的传播时间。可以看出,传播时间基本上与区块的大小成比例。这是因为网络带宽是瓶颈。较大的区块占用超过30秒,才能传播到网络中的大多数节点。所以这不是一个特别有效的协议。在互联网上,30秒是相当长的时间。在比特币的设计中,拥有一个简单的网络,结构简单,节点相等,可以随进随出,优先于效率。因此,在到达网络中最远的节点之前,区块可能需要经过许多节点。如果网络是为了效率而自上而下地设计,我们可以确保任何两个节点之间的路径很短。

网络大小很难衡量网络有多大,因为它是动态的,且没有中央权威。一些研究人员提出了一些估计。在高端方面,有人说,在某个月份,超过一百万个IP地址将至少暂时作为比特币节点。另一方面,似乎只有大约5000到10,000个节点永久连接,且完全验证他们听到的每个交易。这看起来可能是一个令人惊讶的低数字,但是在这篇文章中,没有证据表明完全验证节点的数量正在上升,事实上它可能正在下降。

存储要求 完全验证节点必须保持永久连接,以便听到所有数据。节点离线的时间越长,重新加入网络需要追赶的就越多。这样的节点还必须存储整个区块链,并且需要良好的网络连接以能够听到每个新的交易并将其转发给对等体。存储需求目前低于数十个GB(参见图3.11),这在单一商用台式机的能力范围内。

图3.11.区块链的大小。完全验证节点必须存储整个块链,截至2014年底,该链为26千兆字节。

最后,完全验证节点必须保持一整套未使用的交易输出集合,这是可以花费的硬币。理想情况下,这应该存储在RAM中,这样当听到网络上新的交易时,节点可以快速查找它试图声明的交易输出,运行脚本,查看签名是否有效,并将交易添加到交易池中。截至2014年年中,有超过4400万的交易在区块链中,其中1200万未使用。幸运的是,它仍然足够小以适应高效数据结构中不到1G的RAM。

轻量节点。与完全验证节点相反,存在轻量级节点,也称为瘦客户端或简单支付验证(SPV)客户端。 实际上,比特币网络上绝大多数的节点都是轻量级节点。 这不同于完全验证节点,因为它们不存储整个区块链。他们只存储需要他们验证的,他们关心的特定交易的部分。如果你使用钱包程序,通常会包含一个SPV节点。该节点下载代表向你的地址付款的区块头和交易。

SPV节点没有完全验证节点的安全级别。由于节点具有区块头,因此可以检查这些区块是否难以挖掘,但不能检查区块中包含的每个交易实际上都是有效的,因为它没有交易历史并且不知道未用的交易输出的集合。SPV节点只能验证实际影响它们的交易。所以他们本质上信任完全验证节点来验证所有其他的交易。这不是一次糟糕的安全交易。他们假设有完全验证节点在做艰苦的工作,如果矿工们挖掘这个区块遇到了麻烦,这是一个非常昂贵的过程,他们可能也做了一些验证,以确保这个区块不会被拒绝。

作为SPV节点的成本节省是巨大的。区块头只有区块链大小的大约1/1000。所以不用存储几十G的字节,只有几十M字节。即使智能手机也可以轻松充当Bitcoin网络中的SPV节点。

由于Bitcoin依赖于一个开放的协议,理想情况下,会有许多不同的实现无缝地交互。这样一来,如果有一个bug在其中,就不太可能使整个网络瘫痪。好消息是协议已成功重新实施。C++和Go中有实现,人们正在处理很多其他的事情。坏消息是,网络上的大多数节点正在运行Bitcoin库,由C ++编写,由Bitcoin Core开发人员维护,其中一些节点正在运行以前未更新的旧版本。无论如何,大多数人正在运行这个普通客户端的一些变体。

3.6限制和改进

最后,我们将讨论Bitcoin协议的一些内置限制,以及改进它们的挑战性。在2009年比特币提出时,任何人真正意识到它可能会发展成为全球重要货币之前,Bitcoin协议中存在许多硬编码的限制。其中包括每个块的平均时间,块的大小,区块中签名操作的数量,以及货币的可分性,比特币的总数和区块奖励结构的限制。

对现有比特币总数的限制以及采矿奖励的结构很可能永远不会改变,因为改变它们的经济影响太大了。矿工和投资者对该系统做了很大的赌注,假设比特币的奖励结构和比特币的有限供应将保持其计划的方式。如果这种情况发生变化,将会对人们带来巨大的经济影响。因此,社会基本上同意这些方面,无论是否被明智地选择,都不会改变。

还有其他一些变化似乎使每个人都更好,因为一些初步的设计选择看起来不太符合事后的观点。其中首要的是影响系统吞吐量的限制。Bitcoin网络进程每秒可以处理多少笔交易?该限制来自区块大小的硬编码限制。每个区块限于一兆字节,大约一百万字节。每个交易至少为250个字节。将1000000划分为250份,我们可以看到每个区块的限制为4000个交易,而且每10分钟发现一个,所以每秒剩下约7笔交易,这就是Bitcoin网络所能处理的。改变这些限制似乎只用调整源代码文件中的某个常量。然而,很难在实践中做出这样的改变,我们将很快解释原因。

那么每秒七笔交易如何?比任何主要信用卡处理器的吞吐量相比都相当低。据说Visa的网络平均每秒处理大约2000笔交易,在繁忙期间每秒能处理10000笔交易。即使是比Visa更新更小的Paypal,在高峰时段每秒可处理100笔交易。 这比Bitcoin多一个数量级。

人们长期担心的另一个限制是比特币的加密算法的选择是固定的。只有几个哈希算法可用,只有一个签名算法ECDSA,在特定的椭圆曲线上,称为secp256k1。有人担心,在比特币的生命周期中,人们希望这将是很长的——这个算法可能会被破解。密码学家可能会想出一个我们没有预见到的巧妙的新攻击,使得算法不安全。哈希函数也是如此;事实上,在过去十年中,哈希函数已经在密码分析方面取得了稳步的进展。包含在比特币中的SHA-1已经有一些已知的加密缺陷,尽管不是致命的。为了改变这一点,我们必须扩展Bitcoin脚本语言来支持新的加密算法。

更改协议。我们如何才能在Bitcoin协议中引入新功能?您可能会认为这很简单-只需发布新版本的软件,并告诉所有节点进行升级。 实际上,这是相当复杂的。在实践中,不可能假设每个节点都将升级。 网络中的某些节点将无法获取新软件或无法及时获取。当一些节点运行旧版本时,大多数节点升级的影响很大程度上取决于软件变更的性质。我们可以区分两种类型的变化:那些会导致硬分叉和那些会导致软分叉。

硬分叉。我们可以做出的一种类型的改变引入了先前被认为无效的新功能。也就是说,新版本的软件将识别区块为有效,而旧软件将拒绝其有效性。现在考虑当大多数节点升级,但有些没有升级时会发生什么。不久之后,最长的分支将包含被旧节点视为无效的区块。因此,旧节点将脱离并使用区块链中的一个分支来排除具有新功能的区块。在升级他们的软件之前,他们会认为他们的(较短)分支是最长的有效分支。

这种变化被称为硬分叉变化,因为它使区块链分裂。根据运行的协议版本,网络中的每个节点都将在其一侧或另一侧。当然,这些分支机构永远不会再加入到一起。这被社区认为是不可接受的,因为旧的节点如果不升级他们的软件,将有效地从比特币网络中切割出来。

软分叉。我们可以对Bitcoin进行的第二种类型的更改是增加使验证规则更严格的功能。也就是说,它们限制一组有效的交易或一组有效的区块,使得旧版本将接受所有区块,而新版本将拒绝某些。这种变化称为软叉,它可以避免硬叉引入的永久分裂。

试想当我们引入一个新的软分叉改变软件版本时会发生什么。运行新软件的节点将执行一些新的更严格的规则。如果大多数节点切换到新软件,这些节点将能够执行新的规则。引入软叉依靠足够的节点切换到新版本的协议,以便他们能够执行新的规则,知道旧节点将无法执行新的规则,因为他们没有被听说过。

老矿工有挖掘到无效区块的风险,因为它们包括的一些交易在新的,更严格的规则下是无效的。但是,旧的节点至少会弄清楚他们的某些区块被拒绝,即使他们不明白原因。这可能会促使他们的运营商升级他们的软件。此外,如果他们的分支被新的矿工超越,老矿工就会转向它。这是因为新矿工认为有效的区块也被老矿工认为是有效的。因此,不会有硬叉;相反,会有很多小的临时的分叉。

通过软叉进行的更改的典型例子是付费到脚本哈希(pay-to-script-hash),我们在本章前面讨论过。Bitcoin协议的第一个版本中没有Pay-to-script-hash。这是一个软叉,因为从旧节点的角度来看,有效的付费到脚本哈希交易仍将正确验证。如旧节点所解释的那样,脚本很简单——它会哈希一个数据值,并检查哈希是否匹配输出脚本中指定的值。旧节点不知道(现在需要)运行值本身的附加步骤,看看它是否是一个有效的脚本。我们依靠新的节点来执行新的规则,即脚本实际上是赎回这个交易。

那么我们可以用软叉添加什么?付费到脚本哈希是成功的。新的加密方案也可以通过软叉添加。我们还可以在Coinbase参数中添加一些额外的元数据,以使其有意义。今天,coinbase参数接受任何值。但是,将来我们可以说,这个coinbase必须有一些具体的格式。提出的一个想法是,在每个新的区块中,coinbase包含整套未使用交易的树的Merkle根。这只会导致软叉,因为旧节点可能可能会挖掘一个受网络拒绝没有新coinbase参数需要的区块,但是他们会赶上并加入网络中正在开采的主链。

其他更改可能需要硬叉。比如向Bitcoin添加新的操作码,更改区块或交易大小的限制或各种错误修复。修复我们之前讨论的错误,MULTISIG指令从堆栈中弹出一个额外的值,都需要一个硬叉来实现。这就是为什么,即使一个令人讨厌的bug,更容易将其放在协议中,让人们围绕它进行工作,而不是对Bitcoin进行硬叉更改。即使他们是好的,硬叉更改也是不太可能在当前比特币的气氛中发生的。但是,从头开始,这些想法已经在替代密码货币中测试过并被证明是成功的。我们将在第10章中讨论更多的细节。

边栏:比特币区块大小的难题。由于比特币日益普及,截止2016年初,区块的1M空间被填满已经变得普遍存在(特别的是因为随机性,一个区块需要超过10分钟才能找到)首先,导致一些交易必须等待一个或多个附加区块以进入区块链。增加区块大小的限制将需要硬叉。

关于是否以及如何解决区块链有限交易带宽的问题已经得到Bitcoin社区足够的关注。这个讨论从多年前开始的,但是在达成共识方面却没有什么进展,且变得更加激进,已升级成马戏团。我们将在第7章讨论比特币的社区、政治和治理。

根据区块大小的解决方案,本章中的一些细节可能会稍微过时。增加Bitcoin交易处理能力的技术细节很有趣,我们鼓励你在线阅读更多。

在这一点上,你应该熟悉Bitcoin的技术机制以及Bitcoin节点的运行方式。但是,人类并不是Bitcoin节点,而你从不会在头脑中运行Bitcoin节点。那么,作为一个人,你如何真正地与这个网络进行互动,使它可以作为一种货币?如何找到一个节点来广播你的交易?比特币如何兑换现金?你如何存储你的比特币?所有这些问题对于构建一个真正能为人们工作的货币至关重要,而不仅仅是软件,我们将在下一章回答这些问题。

进一步阅读

在线资源。在本章中,我们讨论了很多技术细节,你可能会发现很难一次性吸收它们。为了补充本章中的材料,上网查看我们在实践中讨论的一些事情是有用的。有许多网站允许你检查区块和交易,并查看它们的样子。一个这样的“blockchain explorer”网站

一本专注于开发,涵盖了技术细节的比特币书籍(特别是第5、6和7章):

Antonopoulos

, Andreas M. Mastering Bitcoin:unlocking digital cryptocurrencies. O'Reilly

Media,2014.《精通比特币:解锁数字加密货币》

练习

1.交易验证:考虑处理Bitcoin交易所涉及的步骤。哪些步骤在计算上是昂贵的?如果你是实体验证许多交易(例如矿工),你可以构建什么样的数据结构来辅助加快验证?

2.比特币脚本:对于以下问题,你可以自由使用当前禁用的非标准交易和操作代码。你可以使用作为简写来表示推送到堆栈上的数据值。有关快速参考,请参见:https://en.bitcoin.it/wiki/Script。

a.为一个交易写比特币ScriptPubKey脚本,可以由提供1764平方根的

任何人进行兑换。

b.编写相应的ScriptSig脚本来兑换你的交易。

c.假设你想发布一个新的RSA保理商挑战,通过发布一个交易,任何

一个可以影响1024位RSA号码的人都可以赎回它(RSA数字是两

个大的、秘密的素数的乘积)。你会遇到什么困难?

3.比特币脚本II:Alice的背包,担心她包含私钥的设备被盗。所以她想把自己的比特币以一个只有通过知道密码才兑换的方式存储。因此,她将它们存储在以下ScriptPubKey地址中:

OP_SHA1

<0x084a3501edef6845f2f1e4198ec3a2b81cf5c6bc>

OP_EQUALVERIFY

a.编写一个可以成功兑换此交易的ScriptSig脚本。[提示:只应该一行长。]

b.解释为什么使用密码保护比特币不是一种安全的方法。

c.能否通过使用Pay-to-Script-hash(P2SH)确定你验证的安全问题来实施它?为什么能或为什么不能?

4.比特币脚本III

a.编写一个ScriptPubKey,它需要演示SHA-256碰撞才能兑换。

b.(困难)写一个相应的ScriptSig,这将成功地兑换这个交易。

5.燃烧和编码

a.有哪些方法来燃烧比特币,即使一个交易无法弥补?其中哪一个可以证明燃烧,即说服任何观察者,没有人可以兑换这样的交易?

b.有哪些将任意数据编码到区块链的方法?其中哪一种会导致烧焦的比特币?

[提示:你对交易“out”字段的内容有了比最初出现时有更多的控制权。]

c.一个用户将一些JavaScript代码编码到区块链中。这样做可能有个什么动机?

6.绿色地址:绿色地址的一个问题是,比特币系统本身没有对双重支付的处罚。为了解决这个问题,你决定设计一个名为“GreenCoin”的altcoin,内置绿色地址支持。任何试图从被指定为“绿色”的地址(或交易产出)中双重支付的行为,都必须以矿工可以强制执行的方式进行经济处罚。为GreenCoin提出了可能的设计。

7.SPV证明:假设商家Bob运行轻量级客户端,并从可信来源接收区块链的当前头。

a.Bob的客户提供什么信息来证明他们向Bob支付的费用已经包含在区块链中?假设Bob需要6个确认。

b.估计这个证明需要多少字节。假设每个区块中有1024个交易。

8.添加新功能:评估是否可以使用硬叉或软叉添加以下新功能:

a.添加新的OP_SHA3脚本指令

b.禁用OP_SHA1指令

c.每个矿工的要求包括每个块中未使用的交易输出(UTXO)的Merkle根

d.要求所有交易的输出按照升序排列

9.更多分叉

a.最突出的比特币硬分叉是由0.8版bug引起的一个暂时分叉。当分叉被解决时,有多少区块被丢弃?

b.最突出的比特币软分叉是添加付费脚本哈希(pay-to-script-hash)。有几个区块因为它变成了孤区块?

c.当比特币客户端检测到链条已经分叉时,它会进入“安全模式”。你可以使用什么启发式来检测这个?

Web note ad 1