Skip to content

Latest commit

 

History

History
338 lines (158 loc) · 53.8 KB

di-liu-zhang-jiao-yi.md

File metadata and controls

338 lines (158 loc) · 53.8 KB

6.1 简介

比特币交易是比特币系统中最重要的部分。根据比特币系统的设计原理,系统中任何其他的部分都是为了确保比特币交 易可以被生成、能在比特币网络中得以传播和通过验证,并最终添加入全球比特币交易总账簿(比特币区块链)。比特 币交易的本质是数据结构,这些数据结构中含有比特币交易参与者价值转移的相关信息。比特币区块链是全球复式记账 总账簿,每个比特币交易都是在比特币区块链上的一个公开记录。在这一章,我们将会剖析比特币交易的多种形式、所包含的信息、如何被创建、如何被验证以及如何成为所有比特币交 易永久记录的一部分。当我们在本章中使用术语“钱包”时,我们指的是构建交易的软件,而不仅仅是密钥的数据库。

6.2交易细节

在[第二章比特币概述]中,我们查看了Alice曾经在Bob的咖啡店使用区块浏览器(Alice与Bob's Cafe的交易)支付咖啡的交易。

区块浏览器应用程序显示从Alice的“地址”到Bob的“地址”的交易。 这是一个非常简化的交易中包含的内容。 实际上,正如我们将在本章中看到的,所显示的大部分信息都是由区块浏览器构建的,实际上并不在交易中。

6.2.1交易 - 幕后细节

在幕后,实际的交易看起来与典型的区块浏览器提供的交易非常不同。 事实上,我们在各种比特币应用程序用户界面中看到的大多数高级结构实际上并不存在于比特币系统中。

我们可以使用Bitcoin Core的命令行界面(getrawtransaction和decodeawtransaction)来检索Alice的“原始”交易,对其进行解码,并查看它包含的内容。 结果如下:Alice的交易被解码后是这个样子:{"version": 1,"locktime": 0,"vin": [



{


  "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",


  "vout": 0,


  "scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",


  "sequence": 4294967295


}

],"vout": [



{


  "value": 0.01500000,


  "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"


},


{


  "value": 0.08450000,


  "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",


}

]}

您可能会注意到有关此交易的一些事情,主要是缺少的事情! Alice的地址在哪里?Bob的地址在哪里? Alice“0.1”输入在哪里? 在比特币,没有硬币,没有发件人,没有收件人,没有余额,没有帐户,没有地址。 所有这些东西都是在更高层次上构建的,为了使用者的利益,使事情更容易理解。

你可能还会注意到很多奇怪和不可破译的字段和十六进制字符串。 不必担心,本章将详细介绍这里所示的各个字段。

6.3交易的输入输出

比特币交易中组成区块链的基础是交易输出。 交易输出是不可分割的比特币货币块,记录在块上,并被整个网络识别为有效。 比特币完整节点跟踪所有可用的和可花费的输出,称为未用量的交易输出,或UTXO。 所有UTXO的集合被称为UTXO集,目前有数百万个UTXO。 当新的UTXO的创建,UTXO集就会变大,当UTXO被消耗时,UTXO集会随着而缩小。 在UTXO集中,每个交易代表的一个变化(状态转换)。当我们说用户的钱包已经“收到”比特币时,我们的意思是,钱包已经检测到可以使用的UTXO,而且这些UTXO是被该钱包密钥控制的。因此,用户的比特币“余额”是用户钱包可以花费的所有UTXO和可能分散在数百个交易和数百个块中的总和。 “一个用户的比特币余额”,这个概念是一个通过比特币钱包应用创建的派生之物。比特币钱包通过扫描区块链并 聚合所有属于该用户的UTXO来计算该用户的余额。大多数钱包维护着数据库或使用数据库服务来存储所有UTXO的快速参考集,这些UTXO由用户所有的密钥进行控制花费行为。一个UTXO可以是一“聪”的任意倍(整数倍)。就像美元可以被分割成表示两位小数的“分”一样,比特币可以被分割成表示八位小数 的“聪”。尽管UTXO可以是任意值,但只要它被创造出来了,就像不能被切成两半的硬币一样不可再分了。如果一个 UTXO比一笔交易所需量大,它仍会被当作一个整体而消耗掉,但同时会在交易中生成零头。例如,你有20比特币的 UTXO并且想支付1比特币,那么你的交易必须消耗掉整个20比特币的UTXO并且产生两个输出:一个是支付了1比特币 给接收人,另一个是支付19比特币的找零到你的钱包。这样的话,大部分比特币交易都会产生找零。想象一下,一位顾客要买1.5元的饮料。她掏出她的钱包并努力从所有硬币和钞票中找出一种组合来凑齐她要支付的1.5 元。如果可能的话,她会选刚刚好的零钱(比如一张1元纸币和5个一毛硬币)或者是小面额的组合(比如3个五毛硬 币)。如果都不行的话,她会用一张大面额的钞票,比如5元纸币。如果她把过多的钱,比如5元,给了商店老板,她会 拿到3.5元的找零,并把找零放回她的钱包以供未来使用。类似的,一笔比特币交易可以有任意数值,但必须从用户可用的UTXO中创建出来。用户不能再把UTXO进一步细分,就 像不能把一元纸币撕开而继续当货币使用一样。用户的钱包应用通常会从用户可用的UTXO中选取多个可用的个体来拼 凑出一个大于或等于一笔交易所需的比特币量。就像现实生活中一样,比特币应用可以使用一些策略来满足付款需要:组合若干小的个体,算出准确的找零;或者使用 一个比交易值大的个体然后进行找零。所有这些复杂的、由可支付的UTXO完成的组合,都是由用户的钱包自动完成, 并不为用户所见。只有当你以编程方式用UTXO来构建原始交易时,这些才与你有关。被交易消耗的UTXO被称为交易输入,由交易创建的UTXO被称为交易输出。通过这种方式,一定量的比特币价值在不同 所有者之间转移,并在交易链中消耗和创建UTXO。一笔比特币交易通过使用所有者的签名来解锁UTXO,并通过使用新 的所有者的比特币地址来锁定并创建UTXO。对于输出和输入链来说,有一个例外,它是一种特殊的交易类型,称为Coinbase交易。这是每个区块中的首个交易。 这种交易存在的原因是作为对挖矿的奖励而产生全新的可用于支付的比特币给“赢家”矿工。这也就是为什么比特币可以 在挖矿过程中被创造出来,我们将在挖矿这一章进行详述。提示输入和输出,哪一个是先产生的呢?先有鸡还是先有蛋呢?严格来讲,先产生输出,因为可以创造新比特币的 coinbase交易没有输入,但它可以无中生有地产生输出。

6.3.1 交易输出

每一笔比特币交易创造输出,输出都会被比特币账簿记录下来。除特例之外(见“数据输出(OP_RETURN操作 符)”),几乎所有的输出都能创造一定数量的可用于支付的比特币,也就是UTXO。这些UTXO被整个网络识别,并且 所有者可在未来的交易中使用它们。追踪UTXO的方式是通过UTXO集(UTXOset)中的每一个全节点比特币客户端实现的。 新的交易从UTXO集中消耗(支付)一个或多个输出。

交易输出包含两部分:▷ 一定量的比特币,被命名为“聪”,是最小的比特币单位; ▷ 确定花费输出所需条件的加密难题(cryptographic puzzle)这个加密难题也被称为锁定脚本(locking script), 见证脚本(witness script),脚本公钥 (scriptPubKey)。个交易脚本语言会在后面121页的“交易脚本和脚本语言”一节中详细讨论。现在,我们来看看Alice的交易(之前的章节“交易 - 幕后”),看看我们是否可以找到并识别输出。 在JSON编码中,输出位于名为vout的数组(列表)中:"vout": [{



"value": 0.01500000,


"scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY


OP_CHECKSIG"

},{



"value": 0.08450000,


"scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",

}]如您所见,交易包含两个输出。 每个输出都由一个值和一个加密难题来定义。 在Bitcoin Core显示的编码中,该值显示在以bitcoin为单位,但在交易本身中,它被记录为以satoshis为单位的整数。 每个输出的第二部分是设定支出条件的加密难题。 Bitcoin Core将其显示为scriptPubKey,并向我们展示了一个人性化的脚本表示。

稍后将在脚本构造(Lock + Unlock)中讨论锁定和解锁UTXO的主题。 在ScriptPubKey中用于编辑脚本的脚本语言在Transaction Scripts和Script Language中讨论。 但在我们深入研究这些话题之前,我们需要了解交易投入和产出的整体结构。

6.3.1.1交易序列化 - 输出

当交易通过网络传输或在应用程序之间交换时,它们被序列化。 序列化是将数据结构的内部表示转换为可以一次发送一个字节的格式(也称为字节流)的过程。 序列化最常用于编码通过网络传输或用于文件中存储的数据结构。 交易输出的序列化格式显示在下表中。

大多数比特币库和框架不会在内部将交易存储为字节流,因为每次需要访问单个字段时,都需要复杂的解析。为了方便和可读性,比特币库将交易内部存储在数据结构(通常是面向对象的结构)中。

从交易的字节流表示转换为库的内部数据结构表示的过程称为反序列化或交易解析。转换回到字节流以通过网络传输,用于散列或存储在磁盘上的过程称为序列化。大多数比特币库具有用于交易序列化和反序列化的内置函数。

看看是否可以从序列化的十六进制形式手动解码Alice的交易,找到我们以前看到的一些元素。包含两个输出的部分在下面中会突出显示:

这里有一些提示:突出显示的部分有两个输出,每个序列化本节之前所述。0.015比特币的价值是1,500,000 satoshis。 这是十六进制的16 e3 60。在串行化交易中,值16 e3 60以小端(最低有效字节优先)字节顺序进行编码,所以它看起来像60 e3 16。scriptPubKey的长度为25个字节,以十六进制为19。

6.3.2交易输入

交易输入将UTXO(通过参考)标记为将被消费,并通过解锁脚本提供所有权证明。要构建一个交易,一个钱包从它控制的UTXO中选择足够的价值来进行请求的付款。 有时一个UTXO是足够的,其他时候不止一个。 对于将用于进行此付款的每个UTXO,钱包将创建一个指向UTXO的输入,并使用解锁脚本解锁它。让我们更详细地看一下输入的组件。 输入的第一部分是通过参考在块链中记录UTXO的交易散列和序列号来指向UTXO的指针。 第二部分是解锁脚本,钱包构造以满足在UTXO中设置的支出条件。 大多数情况下,解锁脚本是一个数字签名和公钥,证明了比特币的所有权。 但是并不是所有的解锁脚本都包含签名。 第三部分是序列号,稍后再讨论。

考虑我们在之前章节交易幕后提到的例子。 交易输入是一个名为vin的数组(列表):"vin": [{



"txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",


"vout": 0,


"scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",


"sequence": 4294967295

}]如您所见,列表中只有一个输入(因为一个UTXO包含足够的值来进行此付款)。 输入包含四个元素:引用包含正在使用的UTXO的交易的交易ID一个输出索引(vout),用于标识来自该交易的哪个UTXO被引用(第一个为零)一个scriptSig,满足UTXO上的条件,解锁它用于支出序列号(稍后讨论)

在Alice的交易中,交易ID是:7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18输出索引0(即由该交易创建的第一个UTXO)。解锁脚本由Alice的钱包构建,首先检索引用的UTXO,检查其锁定脚本,然后使用它来构建必要的解锁脚本以满足此要求。

看一下输入,你可能已经注意到,除了对包含它的交易的引用之外,我们不了解这个UTXO的任何内容。我们不知道它的价值(多少satoshi金额),我们不知道锁定脚本设置支出的条件。要查找此信息,我们必须通过检索基础交易来检索引用的UTXO。请注意,由于输入值未明确说明,因此我们还必须使用引用的UTXO来计算在此交易中支付的费用(参见后面交易费用章节)。

这不仅仅是Alice的钱包需要检索输入中引用的UTXO。一旦将该交易广播到网络,每个验证节点也将需要检索交易输入中引用的UTXO,以验证该交易。交易本身似乎不完整,因为它们缺乏语境。他们在输入中引用UTXO,但是没有检索到UTXO,我们无法知道输入的值或其锁定条件。当编写比特币软件时,无论何时解码交易以验证它或计算费用或检查解锁脚本,您的代码首先必须从块链中检索引用的UTXO,以构建上下文隐含但不存在于输入的UTXO引用。例如,要计算费用支付的金额,您必须知道输入和输出值的总和。但是,如果不检索输入中引用的UTXO,则不知道它们的值。因此,在单个交易中计算费用的简单操作实际上涉及多个交易的多个步骤和数据。

我们可以使用与比特币核心相同的命令序列,就像我们在检索Alice的交易(getrawtransaction和decodeawtransaction)时一样。因此,我们可以在前面的输入中引用UTXO,并查看:Alice的UTXO从以前的交易中引用的输入"vout": [{



 "value": 0.10000000,


 "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"

}]我们看到这个UTXO的值为0.1BTC,并且它具有包含“OP_DUP OP_HASH160 ...”的锁定脚本(scriptPubKey)。提示为了充分了解Alice的交易,我们必须检索引用以前的交易作为输入。 检索以前的交易和未花费的交易输出的函数是非常普遍的,并且存在于几乎每个比特币库和API中。

6.3.2.1交易序列化--交易输入

当交易被序列化以在网络上传输时,它们的输入被编码成字节流,如下表所示

与输出一样,我们来看看我们是否可以从序列化格式的Alice的交易中找到输入。 首先,输入解码:"vin": [{



"txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",


"vout": 0,


"scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",


"sequence": 4294967295

}]现在,我们来看看我们是否可以在下面中识别这些字段,并以十六进制表示法表示:

提示:交易ID以反转字节顺序序列化,因此以(十六进制)18开头,以79结尾输出索引为4字节组,容易识别scriptSig的长度为139个字节,或8b为十六进制序列号设置为FFFFFFFF,便于识别

6.3.3 交易费

大多数交易包含交易费,这是为了在网络安全方面给比特币矿工一种补偿。费用也可以作为一个安全机制本身,使经济上不利于攻击者通过交易来淹没网络。在挖矿一章中,对于挖矿、费用和矿工得到的 奖励,有更详细的讨论。这一节解释交易费是如何被包含在日常交易中的。大多数钱包自动计算并计入交易费。但是, 如果你编程构造交易,或者使用命令行接口,你必须手动计算并计入这些费用。交易费可当作是为了包含(挖矿)一笔交易到下一个区块中的一种鼓励,也可当作是对于欺诈交易和任何种类的系统滥 用,在每一笔交易上通过征收一笔小成本的税而造成的一种妨碍。交易费被挖出这个区块的矿工得到,并且记录在这个 交易的区块链中。交易费基于交易的尺寸,用千字节来计算,而不是比特币的价值。总的来说,交易费基于市场所设置,生效于比特币网 络中。矿工依据许多不同的标准,按重要性对交易进行排序,这包括费用,并且甚至可能在某种特定情况下免费处理交 易。交易费影响处理优先级,这意味着有足够费用的交易会更可能地被包含在下一个挖出的区块中;与此同时,交易费不足或者没有交易费的交易可能会被推迟,基于尽力而为的原则在几个区块之后被处理,甚至可能根本不被处理。交易 费不是强制的,而且没有交易费的交易也许最终会被处理,但是,包含交易费将提高处理优先级。随着时间的过去,交易费的计算方式和交易费在交易优先级上的影响一直在发展。起初,交易费是网络中的一个固定常 数。渐渐地,交易费的结构被放宽了,以便被市场基于网络容量和交易量而强制影响。自从至少2016年初以来,比特币的能力限制已经造成交易之间的竞争,从而导致更高的费用,并有效地使自由交易成为过去。 零费用或非常低的费用交易很少被开采,有时甚至不会在网络上传播。在Bitcoin Core中,费用转移策略由minrelaytxfee选项设置。 当前默认的minrelaytxfee是每千字节0.00001比特币或者millibitcoin的1%。 因此,默认情况下,费用低于0.0001比特币的交易被视为免费的,只有在存储空间不满时才会处理; 否则,它们被丢弃。 比特币节点可以通过调整minrelaytxfee的值来覆盖默认费用策略。任何创建交易的比特币服务(包括钱包,交易所,零售应用程序等)都必须实施动态费用。动态费用可以通过第三方费用估算服务或内置的费用估算算法来实现。如果您不确定,请从第三方服务开始,如果您希望删除第三方依赖项,您应当有设计和部署自己的算法的经验。

费用估算算法根据“竞争”交易提供的能力和费用计算适当的费用。这些算法的范围从简单(最后一个块中的平均值或中位数)到复杂(统计分析)。他们估计必要的费用(以字节为单位),这将使得交易具有很高的可能性被选择并包含在一定数量的块内。大多数服务为用户提供高,中,低优先费用的选择。高优先级意味着用户支付更高的费用,但交易可能会被包含在下一个块中。中低优先级意味着用户支付较低的交易费用,但交易可能需要更长时间才能确认。

许多钱包应用程序使用第三方服务进行费用计算。一个流行的服务是http://bitcoinfees.21.co,它提供了一个API和一个可视图,显示了satoshi / byte中不同优先级的费用。

提示静态费用在比特币网络上不再可行。 设置静态费用的钱包将导致用户体验不佳,因为交易往往会被“卡住”,并保持未确认。 不了解比特币交易和费用的用户被“卡住”交易感到沮丧,因为他们认为他们已经失去了资金。

下面费用估算服务bitcoinfees.21.co中的图表显示了10个satoshi / byte增量的费用的实时估计,以及每个范围的费用交易的预期确认时间(分钟和块数)。 对于每个费用范围(例如,61-70 satoshi /字节),两个横条显示过去24小时(102,975)中未确认交易的数量(1405)和交易总数,其费用在该范围内。 根据图表,此时推荐的高优先费用为80 satoshi / byte,这可能导致交易在下一个块(零块延迟)中开采。 长远来看,中间交易规模为226字节,因此交易规模的建议费用将为18,080 satoshis(0.00018080 BTC)。

费用估算数据可以通过简单的HTTP REST API(https://bitcoinfees.21.co/api/v1/fees/recommended)来检索。 例如,在命令行中使用curl命令:$ curlhttps://bitcoinfees.21.co/api/v1/fees/recommended

{"fastestFee":80,"halfHourFee":80,"hourFee":60}API返回一个具有当前费用估计的JSON对象,以最快的确认(最快的Fee),在三个块(halfHourFee)和六个块(hourFee)内确认,以每个字节为单位。

6.3.4 把交易费加到交易中

交易的数据结构没有交易费的字段。相反地,交易费通过所有输入的总和,以及所有输出的总和之间的差来表示。从所 有输入中扣掉所有输出之后的多余的量会被矿工收集走。交易费被作为输入减输出的余量:交易费 = 求和(所有输入) - 求和(所有输出)对于交易来说,这是一个很让人摸不着头脑的元素,但又是很重要的问题。因为如果你要构造你自己的交易,你必须确 认你没有疏忽地包含了一笔少于输入的、量非常大的费用。这意味着你必须计算所有的输入,如果必要的话进行找零, 不然的话,结果就是你给了矿工一笔可观的劳动费!举例来说,如果你消耗了一个20比特币的UTXO来完成1比特币的付款,你必须包含一笔19比特币的找零回到你的钱 包。否则,那剩下的19比特币会被当作交易费,并且会被挖出你的交易到一个区块中的矿工收走。尽管你会受到高优先 级的处理,并且让一个矿工喜出望外,但这很可能不是你想要的。提示如果你忘记了在手动构造的交易中增加找零的输出,系统会把找零当作交易费来处理。“不用找了!”也许不是你想要的结果。让我们来看看在实际中它如何工作,重温一下Alice在咖啡店的交易。Alice想为咖啡支付0.015比特币。为了确保这笔交 易能立即被处理,Alice想支付一笔交易费,比如说0.001。这意味着总交易成本会变成0.016。因此她的钱包需要凑齐 0.016或更多的UTXO。如果需要,还要加上找零。我们假设他的钱包有一个0.2比特币的UTXO可用。他的钱包就会消耗 掉这个UTXO,创造一个新的0.015的输出给Bob的咖啡店,另一个0.184比特币的输出作为找零回到Alice拥有的钱包, 并留下未分配的0.001比特币内含在交易中。现在让我们换个例子。Eugenia,我们在菲律宾的儿童募捐项目主管,完成了一次为孩子购买教材的筹款活动。她在世 界范围内接收到了好几千个小数额的捐款,总额是50比特币。所以她的钱包塞满了非常小的UTXO。现在她想用比特币 从本地的一家出版商购买几百本的教材。现在Eugenia的钱包应用想要构造一个单笔大额付款交易,它必须从可用的、由很多小数额构成的大的UTXO集合中寻 求钱币来源。这意味着交易的结果是从上百个小数额的UTXO中作为输入,但只有一个输出用来付给出版商。输入数量 这么巨大的交易会比一千字节要大,也许总尺寸会达到两至三千字节。结果是它需要更高的交易费来满足0.0001比特币的网络费。Eugenia的钱包应用会通过测量交易的大小,乘以每千字节需要的交易费,来计算适当的交易费。很多钱包会通过多付 交易费的方式来确保大交易被立即处理。高交易费不是因为Eugenia付的钱很多,而是因为她的交易很复杂并且尺寸很 大——交易费是与参加交易的比特币值无关的。

6.4比特币交易脚本和脚本语言

Bitcoin交易脚本语言,称为脚本,是一种类似Forth的逆波兰表达式堆栈执行语言。 如果听起来不知所云,是你可能还没有学习20世纪60年代的编程语言,但是没关系,我们将在本章中解释一下。 放置在UTXO上的锁定脚本和解锁脚本都以此脚本语言编写。 当一笔比特币交易被验证时,每一个输入值中的解锁脚本被与其对应的锁定脚本同时 (互不干扰地)执行,从而查看这笔交易是否满足使用条件。脚本是一种非常简单的语言,被设计为在范围上有限制,并且可以在一系列硬件上执行,可能与嵌入式设备一样简单。 它需要最少的处理,并且不能做许多现代编程语言可以做的花哨的事情。 为了用于验证可编程货币,这是一个认真考虑的安全功能。如今,大多数经比特币网络处理的交易是以“Alice付给Bob”的形式存在的。同时,它们是以一种称为“P2PKH”(Pay-toPublic-Key-Hash)脚本为基础的。然而,通过使用脚本来锁定输出和解锁输入意味着通过使用编程语言,比特币交易可以包含无限数量的条件。当然,比特币交易并不限于“Alice付给Bob” 的形式和模式。这只是这个脚本语言可以表达的可能性的冰山一角。在这一节,我们将会全面展示比特币交易脚本语言的各个组成部 分;同时,我们也会演示如何使用它去表达复杂的使用条件以及解锁脚本如何去满足这些花费条件。提示比特币交易验证并不基于一个不变的模式,而是通过运行脚本语言来实现。这种语言可以表达出多到数不尽的条 件变种。这也是比特币作为一种“可编程的货币”所拥有的权力。

6.4.1 图灵非完备性

比特币脚本语言包含许多操作,但都故意限定为一种重要的方式——没有循环或者复杂流控制功能以外的其他条件的流 控制。这样就保证了脚本语言的图灵非完备性,这意味着脚本的复杂性有限,交易可执行的次数也可预见。脚本并不是 一种通用语言,施加的这些限制确保该语言不被用于创造无限循环或其它类型的逻辑炸弹,这样的炸弹可以植入在一笔 交易中,通过引起拒绝服务的方式攻击比特币网络。受限制的语言能防止交易激活机制被人当作薄弱环节而加以利用。

6.4.2 非主权验证

比特币交易脚本语言是无国家主权的,没有国家能凌驾于脚本之上,也没有国家会在脚本被执行后对其进行保存。所以 需要执行脚本的所有信息都已包含在脚本中。可以预见的是,一个脚本能在任何系统上以相同的方式执行。如果您的系 统对一个脚本进行验证,可以确信的是每一个比特币网络中的其他系统也将对其进行验证,这意味着一个有效的交易对 每个人而言都是有效的,而且每一个人都明白这一点。这种对于结果的可预见性是比特币系统的一项重要良性特征。

6.4.3 脚本创建(锁定与解锁)

比特币的交易验证引擎依赖于两类脚本来验证比特币交易:一个锁定脚本和一个解锁脚本。锁定脚本是一个放在一个输出值上的“障碍”,同时它明确了今后花费这笔输出的条件。由于锁定脚本往往含有一个公钥 (即比特币地址),在历史上它曾被称作一个脚本公钥代码(scriptPubKey)。由于认识到这种脚本技术存在着更为宽泛的可能性,在本书中,我们将它称为一个“锁定脚本”。在大多数比特币应用源代码中,脚本公钥代码便是我们所说的锁定脚本。您还将看到被称为验证脚本(witness script)的锁定脚本(参见[segwit]章节)或更一般地作为加密难题或者加密拼图(cryptographic puzzle)。 这些术语在不同的抽象层次上都意味着同样的东西。解锁脚本是一个“解决”或满足被锁定脚本在一个输出上设定的花费条件的脚本,同时它将允许输出被消费。解锁脚本是 每一笔比特币交易输出的一部分,而且往往含有一个被用户的比特币钱包(通过用户的私钥)生成的数字签名。由于解 锁脚本常常包含一个数字签名,因此它曾被称作ScriptSig。你也会看到解锁脚本被称为见证人witness(参见[segwit]章节)。在大多数比特币应用的源代码中,ScriptSig便是我们所说的解锁脚本。考虑到更宽泛的锁定脚本要求,在本书中,我们将它称为“解锁脚本”。但并非所有解锁脚本都一定会包含签 名。每一个比特币客户端会通过同时执行锁定和解锁脚本来验证一笔交易。每个输入都包含一个解锁脚本,并引用了之前存在的UTXO。 验证软件将复制解锁脚本,检索输入引用的UTXO,并从该UTXO复制锁定脚本。 然后依次执行解锁和锁定脚本。 如果解锁脚本满足锁定脚本条件,则输入有效(请参阅单独执行解锁和锁定脚本部分)。 所有输入都是独立验证的,作为交易总体验证的一部分。请注意,UTXO被永久地记录在块链中,因此是不变的,并且不受在新交易中引用的失败尝试的影响。 只有正确满足输出条件的有效交易才能将输出视为“已用”,并从未用空的交易输出集(UTXO set)中删除。下图是解锁和锁定最常见类型的比特币交易(对公钥哈希的付款)的脚本的示例,显示了在脚本验证之前从解锁和锁定脚本的并置产生的组合脚本 。

6.4.3.1脚本执行堆栈

Bitcoin的脚本语言被称为基于堆栈的语言,因为它使用称为堆栈的数据结构。堆栈是一个非常简单的数据结构,可以被视为一叠卡片。一个栈允许两个操作:push和pop。 Push在堆栈顶部添加一个项目。 Pop从堆栈中删除顶级项。堆栈上的操作只能作用于堆栈上的最上面的项目。堆栈数据结构也称为后进先出或“LIFO”队列。脚本语言通过从左到右处理每个项目来执行脚本。数字(数据常量)被推到堆栈上。操作员从堆栈中推送或弹出一个或多个参数,对其进行操作,并可能将结果推送到堆栈上。例如,OP_ADD将从堆栈中弹出两个项目,添加它们,并将结果推送到堆栈上。条件运算符评估一个条件,产生一个TRUE或FALSE的布尔结果。例如,OP_EQUAL从堆栈中弹出两个项目,如果它们不相等,则它们将为TRUE(TRUE由数字1表示),否则为FALSE(由零表示)。比特币交易脚本通常包含条件运算符,以便它们可以产生表示有效交易的TRUE结果。

6.4.3.2一个简单的脚本

现在让我们将关于脚本和堆栈的内容应用到一些简单的例子中。在下图中,脚本“ 2 3 OP_ADD 5 OP_EQUAL ”演示了算术加法操作符 OP_ADD ,该操作符将两个数字相加,然后把结果推送到 堆栈, OP_EQUAL 是验算之前的两数之和是否等于 5 。为了简化起见,前缀OP_在一步步的演示示例过程中将被省略。有关可用脚本操作符和函数的更多详细信息,请参见[tx_script_ops]。

虽然大多数的解锁脚本都指向一个比特币地址或公钥,因而如果想要使用资金则需验证所有权,但脚本本身并不需要如 此的复杂。任何解锁和锁定脚本的组合如果结果为真,则为有效。前面被我们用于说明脚本语言的简单算术运算同样也 是一个有效的锁定脚本,该脚本能用于锁定交易输出。使用部分算数运算示例脚本作用锁定脚本:3 OP_ADD 5 OP_EQUAL该脚本能被以解锁脚本为输入的一笔交易所满足,解锁脚本为:2验证软件将锁定和解锁脚本组合起来:2 3 OP_ADD 5 OP_EQUAL正如在图6-4中所看到的,当脚本被执行时,结果是OP_TRUE,从而使得交易有效。不仅该笔交易的输出锁定脚本有 效,同时UTXO也能被任何知晓这个运算技巧(知道是数字2)的人所使用。如果堆栈顶部的结果显示为真(标记为{0×01}),即为任何非零值或脚本执行后堆栈为空情形,则交易有效。如 果堆栈顶部的结果显示为假(0字节空值,标记为{})或脚本执行被操作符禁止,如OP_VERIFY、 OP_RETURN,或有条件终止如OP_ENDIF,则交易无效。详见附录相关内容。以下是一个稍微有些复杂的脚本,它用于计算 2+7-3+1 。注意,当脚本包含多个操作符时,堆栈允许一个操作符的结果 作用于下一个操作符。2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL请试着用纸笔自行演算脚本,当脚本执行完毕时,你会在堆栈得到正确的结果。

6.4.3.3单独执行解锁和锁定脚本

在先前的比特币客户端中,解锁和锁定脚本是以连锁的形式存在的,并且是被依次执行的。出于安全因素考虑,在2010 年比特币开发者们修改了这个特性——因为存在“允许异常解锁脚本推送数据入栈并且污染锁定脚本”的漏洞。在当今的 比特币世界中,这两个脚本是随着堆栈的传递被分别执行的,后续将会详细介绍。首先,使用堆栈执行引擎执行解锁脚本。如果解锁脚本在执行过程中未报错(没有悬空操作符),主堆栈(非其它堆 栈)将被复制,然后脚本将被执行。如果采用从解锁脚本处复制而来的数据执行锁定脚本的结果为真,那么解锁脚本就 成功地满足了锁定脚本所设置的条件,因而,该输入是一个能使用该UTXO的有效授权。如果在执行完组合脚本后的结 果不是真,那么输入就不是有效的,因为它并未能满足UTXO中所设置的使用该笔资金的条件。

6.4.4 P2PKH(Pay-to-Public-Key-Hash)

比特币网络上的大多数交易都是P2PKH交易,此类交易都含有一个锁定脚本,该脚本由公钥哈希实现阻止输出功能,公 钥哈希之后即为广为人知的比特币地址。由P2PKH脚本锁定的输出可以通过键入公钥和由相应私钥创设的数字签名得以解 锁。参见数字签名ECDSA相关内容。例如,我们可以再次回顾一下Alice向Bob咖啡馆支付的案例。Alice下达了向Bob咖啡馆的比特币地址支付0.015比特币 的支付指令,该笔交易的输出内容为以下形式的锁定脚本:OP_DUP OP_HASH160<Cafe Public Key Hash>OP_EQUAL OP_CHECKSIG脚本中的 Cafe Public Key Hash 即为咖啡馆的比特币地址,但这个地址不是基于Base58Check编码的。事实上,大多数 比特币地址都显示为十六进制码,而不是大家所熟知的以1开头的基于Bsase58Check编码的比特币地址。锁定脚本的解锁版脚本是:<Cafe Signature><Cafe Public Key>将两个脚本结合起来可以形成如下有效组合脚本:<Cafe Signature><Cafe Public Key>OP_DUP OP_HASH160<Cafe Public Key Hash>OP_EQUAL OP_CHECKSIG只有当解锁版脚本与锁定版脚本的设定条件相匹配时,执行组合有效脚本时才会显示结果为真(Ture)。即只有当解锁 脚本得到了咖啡馆的有效签名,交易执行结果才会被通过(结果为真),该有效签名是从与公钥哈希相匹配的咖啡馆的 私钥中所获取的。图6-5和图6-6(分两部分)显示了组合脚本一步步检验交易有效性的过程。

6.5数字签名(ECDSA)

到目前为止,我们还没有深入了解“数字签名”的细节。在本节中,我们将研究数字签名的工作原理,以及如何在不揭示私钥的情况下提供私钥的所有权证明。比特币中使用的数字签名算法是椭圆曲线数字签名算法或ECDSA。 ECDSA是用于基于椭圆曲线私钥/公钥对的数字签名的算法,如椭圆曲线章节所述。 ECDSA由脚本函数OP_CHECKSIG,OP_CHECKSIGVERIFY,OP_CHECKMULTISIG和OP_CHECKMULTISIGVERIFY使用。每当你看到锁定脚本时,解锁脚本都必须包含一个ECDSA签名。

数字签名在比特币中有三个目的(见下面的侧栏)。首先,签名证明,私钥的所有者,暗示资金所有者已经授权支出这些资金。其次,授权证明是不可否认的(不可否认性)。第三,签字证明交易(或交易的具体部分)在签字之后没有也不能被任何人修改。

请注意,每个交易输入都是独立签署的。这是至关重要的,因为签名和投入都不得属于或由同一“所有者”应用。事实上,一个名为“CoinJoin”的特定交易方案使用这个事实来创建多方交易来保护隐私。注意每个交易输入和它可能包含的任何签名完全独立于任何其他输入或签名。 多方可以协作构建交易,并且只签一个输入。维基百科的“数字签名”定义数字签名是用于证明数字消息或文档的真实性的数学方案。 有效的数字签名给予接收者理由认为该消息是由已知的发送者(认证)创建的,即发送方不能拒绝发送消息(不可否认),并且消息在传输(完整性)中未被更改。来源:https://en.wikipedia.org/wiki/Digital_signature*

6.5.1数字签名如何工作

数字签名是由两部分组成的数学方案。 第一部分是使用私钥(签名密钥)从消息(交易)创建签名的算法。 第二部分是允许任何人验证签名的算法,给定消息和公钥。

6.5.1.1创建数字签名

在比特币的ECDSA算法的实现中,被签名的“消息”是交易,或更确切地说,交易中数据的特定子集的散列(参见签名哈希类型(SIGHASH))。 签名密钥是用户的私钥。 结果是签名:((Sig = F_ {sig}(F_ {hash}(m),dA)))这里的:dA是签名私钥m是交易(或其部分)Fhash是散列函数Fsig是签名算法Sig是结果签名ECDSA数学的更多细节可以在ECDSA Math章节中找到。函数Fsig产生由两个值组成的签名Sig,通常称为R和S:Sig = (R, S)现在已经计算了两个值R和S,它们使用称为分辨编码规则的国际标准编码方案或DER将其序列化为字节流。

6.5.1.2签名序列化(DER)

我们来看看Alice再次创建的交易。 在交易输入中有一个解锁脚本,其中包含Alice的钱包中的以下DER编码签名:3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301该签名是Alice的钱包生成的R和S值的序列化字节流,以证明她拥有授权花费该输出的私钥。 序列化格式由以下九个元素组成:0x30表示DER序列的开始0x45 - 序列的长度(69字节)0x02 - 一个整数值0x21 - 整数的长度(33字节)R-00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb0x02 - 接下来是一个整数0x20 - 整数的长度(32字节)S-4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813后缀(0x01)指示使用的散列的类型(SIGHASH_ALL)

看看您是否可以使用此列表解码Alice的序列化(DER编码)签名。 重要的数字是R和S; 数据的其余部分是DER编码方案的一部分。

6.5.2验证签名

要验证签名,必须具有签名(R和S),序列化交易和公钥(对应于用于创建签名的私钥)。基本上,签名的验证意味着“只有生成此公钥的私钥的所有者才能在此交易上产生此签名。”签名验证算法采用消息(交易的一部分或其部分的散列),签名者的公钥和签名(R和S值),如果签名对该消息和公钥有效,则返回TRUE。

6.5.3签名哈希类型(SIGHASH)

数字签名被应用于在比特币的情况下是交易本身的消息。签名意味着签字人对特定交易数据的承诺。在最简单的形式中,签名适用于整个交易,从而提交所有输入,输出和其他交易领域。但是,签名只能提交到一个交易中的一个子集的数据,这对于我们将在本节中看到的一些场景是有用的。

比特币签名具有指示交易数据的哪一部分包含在使用SIGHASH标志的私钥签名的哈希中的方式。 SIGHASH标志是附加到签名的单个字节。每个签名都有一个SIGHASH标志,该标志可以不同于一个输入到另一个输入。具有三个签名输入的交易可以具有不同SIGHASH标志的三个签名,每个签名(提交)交易的不同部分。

记住,每个输入可能在其解锁脚本中包含一个签名。因此,包含多个输入的交易可以具有不同的SIGHASH标志的签名,这些标志在每个输入中提交交易的不同部分。还要注意,比特币交易可能包含来自不同“所有者”的输入,他们只能在部分构建(和无效)的交易中签署一个输入,与他人协作收集所有必要的签名以进行有效的交易。如果您想到多个参与者在比特币网络之外进行协作并更新部分签名的交易,则许多SIGHASH标志类型才有意义。有三个SIGHASH标志:ALL,NONE和SINGLE,如下表所示。

另外还有一个修饰符标志SIGHASH_ANYONECANPAY,它可以与前面的每个标志组合。 当设置ANYONECANPAY时,只有一个输入被签名,其余的(及其序列号)打开以进行修改。 ANYONECANPAY的值为0x80,并通过按位OR运算,得到如下所示的组合标志

SIGHASH标志在签名和验证期间应用的方式是进行交易的副本,并且其中的某些字段被截断(设置为零长度并清空)。 生成的交易被序列化。 SIGHASH标志被添加到序列化交易的结尾,并且结果被哈希。 哈希本身是被签名的“消息”。 根据使用的SIGHASH标志,交易的不同部分将被截断。 所得到的散列取决于交易中数据的不同子集。 通过将SIGHASH作为哈希前的最后一步,签名也提交SIGHASH类型,因此不能更改(例如,由矿工)。提升所有SIGHASH类型对应交易nLocktime字段(请参阅[transaction_locktime_nlocktime]部分)。 此外,SIGHASH类型本身在签名之前附加到交易,因此一旦签名就不能修改它。在Alice的交易(参见序列化签名(DER)的列表)的例子中,我们看到DER编码签名的最后一部分是01,这是SIGHASH_ALL标志。 这会锁定交易数据,因此Alice的签名正在提交所有输入和输出的状态。 这是最常见的签名形式。

我们来看看其他一些SIGHASH类型,以及如何在实践中使用它们:ALL | ANYONECANPAY这种构造可以用来做“众筹”的交易,试图筹集资金的人可以用单笔输出来构建一个交易,单笔输出将“目标”金额付给众筹发起人。这样的交易显然是无效的,因为它没有输入,但是现在其他人可以通过添加自己的输入作为捐赠来修改它们,他们用ALL | ANYONECANPAY签署自己的输入,除非收集到足够的输入以达到输出的价值,交易无效,每次捐赠是一项“质押”,直到募集整个目标金额才能由募款人收取。

NONE该结构可用于创建特定数量的“承载支票”或“空白支票”。它提交输入,但允许输出锁定脚本被更改。任何人都可以将自己的比特币地址写入输出锁定脚本并兑换交易。然而,输出值本身被签名锁定。

NONE | ANYONECANPAY这种构造可以用来建造一个“吸尘器”。在他们的钱包中拥有微小UTXO的用户无法花费这些费用,因为手续费用超过了这些微小UTXO的价值。借助这种类型的签名,灰尘UTXO可以为任何人捐赠,以便随时随地收集和消费。

有一些修改或扩展SIGHASH系统的建议。作为Elements项目的一部分,一个这样的提案是Blockstream的Glenn Willen的Bitmask Sighash模式。这旨在为SIGHASH类型创建一个灵活的替代品,允许“任意的,可重复使用的输入和输出位掩码”来表示“更复杂的合同预付款方案,例如已分配的资产交换中的变更签名的报价”。

注意您不会在用户的钱包应用程序中看到作为选项提供的SIGHASH标志。 除了少数例外,钱包构造了P2PKH脚本,并使用SIGHASH_ALL标志进行签名。 要使用不同的SIGHASH标志,您必须编写软件来构造和签署交易。 更重要的是,SIGHASH标志可以被专用的比特币应用程序使用,从而实现新颖的用途。

6.5.4ECDSA数学

如前所述,签名由数学函数Fsig创建,该函数产生由两个值R和S组成的签名。在本节中,我们更详细地查看函数Fsig。签名算法首先生成一个临时(临时)私有公钥对。 在涉及签名私钥和交易哈希的变换之后,该临时密钥对用于计算R和S值。临时密钥对基于随机数k,用作临时私钥。 从k,我们生成相应的临时公钥P(以P = k * G计算,与派生比特币公钥相同);参见[pubkey]部分)。 数字签名的R值则是临时公钥P的x坐标。从那里,算法计算签名的S值,使得:其中:k是临时私钥R是临时公钥的x坐标dA是签名私钥m是交易数据p是椭圆曲线的主要顺序验证是签名生成函数的倒数,使用R,S值和公钥来计算一个值P,该值是椭圆曲线上的一个点(签名创建中使用的临时公钥):

其中:R和S是签名值Qa是Alice的公钥m是签署的交易数据G是椭圆曲线发生器点如果计算点P的x坐标等于R,则验证者可以得出结论,签名是有效的。请注意,在验证签名时,私钥既不知道也不显示。ECDSA的数学很复杂,难以理解。 网上有一些很棒的指南可能有帮助。 搜索“ECDSA解释”或尝试这个:http://bit.ly/2r0HhGB

6.5.5随机性在签名中的重要性

如我们在ECDSA Math中所看到的,签名生成算法使用随机密钥k作为临时私有/公钥对的基础。 k的值不重要,只要它是随机的。如果使用相同的值k在不同的消息(交易)上产生两个签名,那么签名私钥可以由任何人计算。在签名算法中重用相同的k值会导致私钥的暴露!警告如果在两个不同的交易中,在签名算法中使用相同的值k,则私钥可以被计算并暴露给世界!

这不仅仅是一个理论上的可能性。我们已经看到这个问题导致私人密钥在比特币中的几种不同实现的交易签名算法中的暴露。人们由于无意中重复使用k值而将资金窃取。重用k值的最常见原因是未正确初始化的随机数生成器。

为了避免这个漏洞,业界最佳实践是不用生成熵的随机数生成k,而是使用与交易数据本身种子相关的确定性随机过程。这确保每个交易产生不同的k。在互联网工程任务组发布的RFC 6979中定义了k的确定性初始化的行业标准算法。

如果您正在实现一种用于在比特币中签署交易的算法,则必须使用RFC 6979或类似的确定性随机算法来确保为每个交易生成不同的k。

6.6比特币地址,余额和其他摘要

我们开始本章,发现交易看起来与“幕后”非常不同,而不是如何在钱包,块链浏览器和其他面向用户的应用程序中呈现。 来自前几章的许多简单而熟悉的概念,如比特币地址和余额,似乎在交易结构中不存在。 我们看到交易本身并不包含比特币地址,而是通过锁定和解锁比特币离散值的脚本进行操作。 这个系统中的任何地方都不存在余额,而每个钱包应用程序显着地显示了用户钱包的余额。

现在我们已经探讨了一个比特币交易中实际包含的内容,我们可以检查更高级别的抽象是如何从交易的看似原始的组件派生出来的。我们再来看看Alice的交易是如何在一个受欢迎的块浏览器(前面章节Alice与Bob's Cafe的交易)中呈现的。

在交易的左侧,该块资源管理器将Alice的比特币地址显示为“发送者”。其实这个信息本身并不在交易中。当块链接浏览器检索到交易时,它还检索在输入中引用的先前交易,并从该旧交易中提取第一个输出。在该输出内是一个锁定脚本,将UTXO锁定到Alice的公钥哈希(P2PKH脚本)。块链浏览器提取公钥哈希,并使用Base58Check编码对其进行编码,以生成和显示表示该公钥的比特币地址。

同样,在右侧,区块浏览器显示了两个输出;第一个到Bob的比特币地址,第二个到Alice的比特币地址(作为更改)。再次,为了创建这些比特币地址,块链浏览器从每个输出中提取锁定脚本,将其识别为P2PKH脚本,并从内部提取公钥。最后,块链浏览器重新编码了使用Base58Check的公共密钥哈希生成和显示比特币地址。

如果您要点击Bob的比特币地址,则块链接浏览器将显示Bob的比特币地址的余额。

块状资源管理器显示Bob的比特币地址的余额。但是比特币系统中却没有一个“余额”的概念。相反,这里显示的值由块链浏览器构成如下。为了构建“总接收”数量,块链浏览器首先解码比特币地址的Base58Check编码,以检索在地址中编码的Bob的公钥的160位哈希值。然后,块链浏览器将搜索交易数据库,使用包含Bob公钥密钥的P2PKH锁定脚本寻找输出。通过总结所有输出的值,浏览器可以产生接收的总值。

构建当前余额(显示为“最终余额”)需要更多的工作。块链接浏览器保留当前未用的UTXO集合的输出的单独数据库。为了维护这个数据库,块浏览器必须监视比特币网络,添加新创建的UTXO,并且实时删除已经被使用的UTXO,因为它们出现在未经确认的交易中。这是一个复杂的过程,这取决于在传播时跟踪交易,并保持与比特币网络的共识,以确保遵循正确的链。有时,blockchain的浏览器不同步,从UTXO集的角度来看是不完整或不正确的。从UTXO集合,块链浏览器总结了引用Bob的公钥哈希的所有未使用输出的值,并产生向用户显示的“最终余额”数目。为了产生这一个形象,通过这两个“余额”,块浏览器必须对数十,数百甚至数十万笔交易进行索引和搜索。总之,通过钱包应用程序,块链浏览器和其他比特币用户界面呈现给用户的信息通常由更高层次的抽象组成,这些抽象是通过搜索许多不同的交易,检查其内容以及操纵其中包含的数据而导出的。通过将类似于从一个发送者到一个收件人的银行支票的比特币交易的这种简单的视图呈现,这些应用程序必须抽象许多底层细节。他们主要关注常见的交易类型:每个输入上具有SIGHASH_ALL签名的P2PKH。因此,虽然比特币应用程序可以以易于阅读的方式呈现所有交易的80%以上,但有时候由于偏离了规范的交易而陷入困境。包含更复杂的锁定脚本,或不同SIGHASH标志,或多个输入和输出的交易显示了这些抽象的简单性和弱点。

每天都有数百个不包含P2PKH输出的交易在块上被确认。 blockchain浏览器经常向他们发出红色警告信息,表示无法解码地址。以下链接包含未完全解码的最新的“奇怪交易”:https://blockchain.info/strange-transactions。

正如我们将在下一章中看到的,这些并不一定是奇怪的交易。它们是包含比常见的P2PKH更复杂的锁定脚本的交易。我们将学习如何解码和了解更复杂的脚本及其支持的应用程序。