您的位置:首页 > 其它

比特币开发者指南(5)--交易

2018-01-16 17:02 986 查看

交易

交易让用户花费satoshis(聪)。每个交易都由几个部分构成,这些部分既可以实现简单的直接付款,也可以实现复杂的交易。本节将介绍每个部分,并演示如何一起使用它们来构建完整的事务。

为了保持简单,本节假设币基交易不存在。币基交易只能由Bitcoin 矿工创建,它们是下列许多规则的例外。不要对每条规则考虑币基例外,请读本指南中区块链一节中的币基交易部分。



上图显示了比特币交易的主要部分。每个交易至少有一个输入和一个输出。每个输入花费来源于先前的输出的satoshis。然后,每个输出等待未花费的交易输出UTXO,直到稍后另外一个输入花费它。当比特币钱包告诉你你有10000聪,实际上意味着你有10000聪在一个或多个UTXO等待被花费。

每个交易都有四字节交易版本号作为前缀,它告诉Bitcoin节点和矿工使用哪些规则进行验证它。这样,开发人员可以为将来的交易创建新规则,而不会使以前的交易无效。



输出根据其在交易中的位置具有隐含的索引号 - 第一个输出是输出零。输出也有多少satoshis(聪)中的一个数额,它支付给条件pubkey脚本。任何满足pubkey脚本的条件的人可以花费支付的satoshis的数额。

输入使用交易标识符(txid)和输出索引号码(通常称为“vout” 输出向量),来识别要花费的特定输出。它还有一个签名脚本,来提供满足pubkey脚本中的条件的数据参数。(序列号和locktime是相关的,将在后面的小节中一起讨论。)

下图帮助说明如何使用这些功能,展示了Alice用于向Bob发送一个交易的工作流程,以及Bob稍后用于支付的工作流程。Alice和Bob都使用最常见的标准Pay-To-Public-Key-Hash(P2PKH)交易类型。P2PKH让Alice花费satoshis到一个典型的比特币地址,然后让Bob稍后用简单密钥对使用这些satoshis。



Creating A P2PKH Public Key Hash To Receive Payment

在Alice创建第一个交易之前,Bob必须先生成一个私有/公开的密钥对。比特币使用secp256k1的椭圆曲线数字签名算法(ECDSA);secp256k1私钥是256位随机数据。该数据的副本被确定地转换为secp256k1 公钥。因为以后可以可靠地进行转换,所以不需要存储公钥。

然后公钥(pubkey)经过密码哈希运算。此pubkey哈希运算也可以在以后可靠地重复,因此也不需要存储。哈希运算缩短并混淆了公钥,使人工转录更容易,并提供安全性,防止从公钥重建私钥这样的意外问题。

Bob向Alice提供了pubkey哈希值。Pubkey哈希值几乎总是被编码为Bitcoin 地址,它们是包含地址版本号,哈希值,来捕获打字错误的checksum错误检测校验base58编码字符串。地址可以通过任何介质传输,包括防止与之通信的接收者花销的单向介质,并可以进一步编码为另外一种格式,例如包含比特币URI的OR码。

一旦Alice拥有地址并将其解码为标准哈希值,就可以创建第一个交易。她创建了一个包含指令的标准的P2PKH交易输出,这个指令允许任何人花费这个输出,若他们能证明他们控制着相应于Bob哈希过的公钥的私钥。这些指令称为pubkey脚本或scriptPubKey。

爱丽丝广播该交易,并将其添加到区块链。网络将它归类为未花费的交易输出UTXO,Bob的钱包软件也将其显示为可花费的数额。

一段时间后,Bob决定花费UTXO,他必须创建一个输入,该输入引用Alice创建的有交易标识符txid的交易和有输出索引的特定输出。然后他必须创建一个签名脚本 - 满足Alice在之前输出pubkey脚本里列出的条件的一组数据参数。签名脚本也称为scriptSigs。

Pubkey脚本和签名脚本根据条件逻辑组合secp256k1公钥和签名,创建一个可编程认证机制。



Unlocking A P2PKH Output For Spending

对于P2PKH风格的输出,Bob的签名脚本将包含以下两个数据:

1. 他的完整(unhashed)公钥,所以pubkey脚本可以检查它的哈希值与Alice提供的pubkey哈希值是否相同。

2. 通过ECDSA密码公式制作的secp256k1签名将特定交易数据(如下所述)与Bob的私钥组合。这使得pubkey脚本能验证Bob拥有创建公钥的私钥。

Bob的secp256k1签名不仅证明Bob控制着他的私钥,也使得他的交易的非签名脚本部分防止被篡改,便于Bob安全地将他们广播到P2P网络。



Some Things Signed When Spending An Output

如上图所示,Bob签名的数据包括之前交易的txid,之前输出的pubkey脚本,Bob创建让下一个接收者花费交易输出的pubkey脚本,satoshis的金额。实质上,除了保存完整的公钥和secp256k1签名的任何签名脚本之外,整个交易都被签名。

在Bob把他的签名和公钥放到签名脚本之后,Bob通过P2P网络将该交易广播给Bitcoin矿工。每个节点和矿工在进一步广播或试图将其加入一个包含交易的新块之前独立地验证交易。


P2PKH脚本验证

验证过程需要评估签名脚本和pubkey脚本。在P2PKH输出中,pubkey脚本是:



花费者的签名脚本被评估并作为脚本的开头。在P2PKH事务中,签名脚本包含secp256k1签名(sig)和完整的公钥(pubkey),创建以下连接:



脚本语言是一种类似Forth基于堆栈的语言,故意设计为无状态,而不是图灵完备的。无状态确保一旦交易被添加到块链,就没有任何条件使其永久不可用。图灵不完备(特别是缺少循环或gotos)使脚本语言灵活性更低,更可预测,大大简化了安全模型。

为了测试交易是否有效,从Bob的签名脚本开始到Alice的pubkey脚本的末尾,签名脚本和pubkey脚本操作一次执行一个条目。下图显示了标准P2PKH公钥脚本的评估过程;下图是对该过程的描述。



P2PKH Stack Evaluation

将签名(来自Bob的签名脚本)添加(压入)一个空堆栈。因为它只是数据,除了将它添加到堆栈之外什么都不做。公钥(也来自签名脚本)被压入到签名之上。

从Alice的pubkey脚本,执行OP_DUP操作。OP_DUP将当前位于其顶部的数据的副本压入堆栈,在这种情况下创建一个Bob提供的公钥的副本。

接下来执行的操作OP_HASH160将当前位于其上的数据即Bob的公钥的哈希值压入堆栈 。这创建了一个Bob的公钥的散列。

然后,Alice的pubkey脚本将Bob在第一个交易时给她的pubkey哈希值压入。这样,在堆栈顶端有两个Bob的pubkey哈希值。

现在变得有趣,Alice的pubkey脚本执行OP_EQUALVERIFY,OP_EQUALVERIFY相当于执行OP_EQUAL,后跟OP_VERIFY(未显示)。

OP_EQUAL(未显示)检查堆栈顶部的两个值;在这种情况下,它检查从Bob提供的完整的公钥生成的pubkey哈希值是否等于当Alice创建事务#1时提供的pubkey哈希值 。OP_EQUAL弹出(从堆栈顶部移除)要比较的两个值,并用比较结果替换它们:0(false)
fafb
或1(true)。

OP_VERIFY(未显示)检查堆栈顶部的这个值。如果值为false,则会立即终止评估,并且交易验证失败。否则,它从堆栈中弹出true值。

最后,Alice的pubkey脚本执行OP_CHECKSIG,它检查Bob提供的签名与Bob提供的现在认证的公钥是否匹配。如果签名与公钥匹配,并是由所有需要签名的数据生成的,则OP_CHECKSIGtrue 压入堆栈的顶部。

评估pubkey脚本后,如果false不在堆栈的顶部,则交易是有效的(只要没有其他问题)。


P2SH脚本

Pubkey脚本是由对脚本所做的不太感兴趣的消费者创建的。接收者关心脚本条件,如果他们想要,他们可以要求消费者使用特定的公钥脚本。不幸的是,自定义的pubkey脚本不如短的比特币地址方便,并且稍后讨论的在BIP70支付协议普及实现之前,没有标准的方式在程序之间相互通信。

为了解决这些问题,在2012年创建了pay-to-script-hash(P2SH)事务,让一个发起人创建一个包含第二个脚本的哈希的pubkey脚本redeem脚本。

基本的P2SH工作流程如下图所示,看起来与P2PKH工作流程几乎相同。Bob用任何他想要的脚本创建一个兑换脚本,将兑换脚本哈希,并将兑换脚本哈希提供给Alice。Alice创建一个包含Bob的redeem脚本哈希的P2SH样式的输出。



Creating A P2SH Redeem Script And Hash

当Bob想要花费输出时,他提供签名以及有完整序列化redeem脚本。P2P网络确保完整的redeem脚本哈希值和Alice放在输出的脚本哈希值相等; 然后如果是主pubkey脚本的话,它处理输出redeem脚本, 如果redeem脚本不返回false的话让Bob花费输出。



Unlocking A P2SH Output For Spending

redeem脚本的哈希值具有与pubkey哈希相通的属性 - 可以将其转换为标准Bitcoin地址格式,只有一个小改变以区别标准的地址。这使得获取一个P2SH样式的地址就像获取P2PKH样式地址一样简单。该哈希还混淆了redeem 脚本中的任意公钥,因此P2SH脚本与P2PKH pubkey哈希一样安全。


标准交易

在比特币早期版本中发现几个危险的错误之后,添加了一个测试,它只接受来自网络的交易,如果他们的pubkey脚本和签名脚本匹配一小组相信为安全的模板,并且如果其余的交易没有违反确保良好的网络行为的另一个小规则集。这就是IsStandard()测试,通过测试的交易称为标准交易。

非标准交易 - 测试失败当不使用默认的Bitcoin Core设置时可能会被节点接受。如果它们被包含在区块中,它们也将避免IsStandard测试并被处理。

除了通过广播有害的交易来攻击Bitcoin更加困难,标准交易测试也有助于防止用户今天创建交易,而使添加未来的新交易功能更加困难。例如,如上所述,每个交易包括版本号 - 如果用户开始任意地改变版本号,则作为引入向后不兼容特征的工具将变得无用。

从Bitcoin Core 0.9开始,标准的pubkey脚本类型是:


支付公钥哈希(P2PKH)

P2PKH是用于将交易发送到一个或多个Bitcoin 地址的最常见的pubkey脚本形式。




支付脚本哈希(P2SH)

P2SH用于将交易发送到脚本哈希。每个标准的pubkey脚本可以用作P2SH redeem脚本,但实际上只有multisig pubkey脚本有意义的是,直到更多的交易类型成为标准。




Multisig

虽然现在在mutlisig交易中广泛使用P2SH multisig脚本,但是在UTXO被花费之前,这个基本的脚本可用于要求多个签名。

在multisig pubkey脚本中,称为m-of-n, m是需要匹配公钥的签名的最小数量,n是提供的公钥数量。m和n应该是操作码 OP_1到OP_16,对应于期望的号码。

由于必须和原始Bitcoin实现中的一个错误保持兼容性,OP_CHECKMULTISIG比m多消耗一个值,因此签名脚本的secp256k1签名列表必须以一个无用的占位的额外值OP_0开始。

签名脚本的签名顺序必须根据公钥出现在pubkey脚本或redeem脚本的顺序对应。有关详细信息,请参阅OP_CHECKMULTISIG中的说明。



虽然它不是一个单独的交易类型,但它是一个具有2-of-3的P2SH multisig:




Pubkey

Pubkey 输出是P2PKH pubkey脚本的简化形式,但它们不像P2PKH那样安全,因此一般在新的交易不使用了。




Null数据

Null数据交易类型默认在Bitcoin Core 0.9.0中继承并开采,并且后来添加任意的数据到全节点不需要在他们的UTXO数据库中存储的一个证实不可花费的pubkey脚本中。因为不能自动修剪UTXO数据库的交易,最好使用null数据交易;然而如果可能的话通常将数据存储在交易之外更好。

共识规则允许null数据输出达到提供它们所遵循的所有其他共识规则的最大允许10000字节的pubkey脚本的大小,其他规则例如没有任何数据推送大于520字节。

Bitcoin Core 0.9.x到0.10.x默认情况下,在单个数据推送中中继和挖掘最多40个字节的null数据交易,并且只有一个null数据输出,它正好支付0 satoshis。



Bitcoin Core 0.11.x将此默认值增加到80个字节,其他规则保持不变。

Bitcoin Core 0.12.0默认中继和挖掘null数据输出最多可以有83个字节,可以包含任意数量的数据推送,只要不超过总字节数限制。但是仍然只有一个null数据输出,它仍然必须正确地支付0 satoshis。

Bitcoin Core配置选项-datacarriersize 允许您设置要传送或挖掘的null数据输出中的最大字节数。


非标准化交易

如果你使用了非标准的pubkey脚本在一个输出中,使用默认Bitcoin Core设置的节点和矿工不会接受,广播或挖掘你的交易。当您尝试将交易广播到运行默认设置的节点时,您将收到错误。

如果您创建redeem脚本,将其进行哈希放入P2SH输出中的一个哈希值,则网络只会看到这个哈希值,因此它不管redeem脚本在说什么就接受输出有效。这允许支付给非标准脚本,并且从Bitcoin Core 0.11起,几乎针对所有有效的redeem脚本。有个例外是那些使用未分配的NOP操作码的脚本;这些操作码被保留用于将来的软分叉,并且只能由不遵循标准内存池规则的节点进行中继或开采。

注意:标准交易旨在保护和帮助网络,而不会阻止您发生错误。很容易创建标准交易,把未花费的satoshis发送给他们。

从Bitcoin Core 0.9.3,标准交易也必须符合以下条件:

交易必须完成:它的locktime必须在过去(或小于等于当前块高),或它里面的所有序列号都是0xffffffff。
交易必须小于100,000字节。这大约是一个典型的单输入单输出的P2PKH交易的200倍。
每个交易的签名脚本必须小于1,650字节。这个足够大来允许使用压缩公钥的P2SH 15-of-15 multisig交易。
需要超过3个公共密钥的Bare(非P2SH)multisig事务当前是非标准的。
交易的签名脚本只能将数据压入到脚本评估堆栈。除了仅将数据压入到堆栈的操作码之外,它不能压入新的操作码。
交易不能包含比输入少1/3的satoshis的输出。Bitcoin Core节点当前默认的中继费一个P2PKH或P2SH输出 546satoshis。有个例外,标准null数据输出必须接收0 satoshis。


签名哈希类型

OP_CHECKSIG从每个签名中提取一个非堆栈参数,使得签名者可以决定交易的哪些部分需要签名。由于签名保护交易的这些部分不被修改,这使得签名者有选择地让其他人修改其交易。

要签署的各种选项称为签名哈希类型。目前有三种基本的SIGHASH类型:
SIGHASH_ALL,默认签名所有输入和输出,保护除签名脚本之外的所有内容不受修改。
SIGHASH_NONE签名所有输入,不签名任何输出,运行任何人更改satoshis的取消,除非有使用其他签名标记的签名保护输出。
SIGHASH_SINGLE与输入索引好相同的唯一的输出被签名,确保没有人能修改交易的属于你的部分,到但是运行其他签名者修改交易的属于他们的部分。相应的输出必须存在,或者值“1”将被签名,打破了安全性方案。这个输入以及其他输入都包含在签名中。其他输入的序列号不包括在签名中,可以更新。

基本类型可以使用SIGHASH_ANYONECANPAY(任何人都可以支付)标志来修改,于是创建三种新的组合类型:

SIGHASH_ALL|SIGHASH_ANYONECANPAY签名这个输入及其所有输出,允许任何人添加或删除其他输入,以便任何人能贡献额外的satoshis,但是他们不能改名他们发送satoshis的数量和去向。
SIGHASH_NONE|SIGHASH_ANYONECANPAY仅签名这个输入,任何人添加或删除其他输入或输出,以便得到这个输入的副本的任何人想花的话就去花费它。
SIGHASH_SINGLE|SIGHASH_ANYONECANPAY签名一个输入和它相应的输出。允许任何人添加或删除其他输入。

因为每个输入就要被签名,所以一个有多个输入的交易包含多个签名哈希来签名交易的不同部分。例如使用NONE签名的单输入交易有能被把它添加到区块链的公开修改的输出。另一方面,如果两输入交易有一个输入用NONE签名和一个输入用 ALL签名,ALL签名者可以选择在哪花费satoshis而不需要咨询NONE签名者,但是没有其他人可以修改交易。

锁定时间和序列号

所有签名哈希类型都签名的一个东西就是交易的locktime。(在Bitcoin Core源代码中称为nLockTime)locktime表示交易可以添加到块链的最早时间。

Locktime允许签名者创建时间锁定的交易,这将只会在将来生效,使签名者有机会改变主意。

如果任何签名者改变主意,他们可以创建一个新的非locktime的交易。新交易使用被用于locktime交易输入的相同的一个输出作为它的一个输入。如果新交易在时间锁过期之前被加入区块链,那么这个locktime交易时将失效。

当时间锁快到期时候需要特别小心。P2P网络允许区块时间比真实时间提前两小时,因此一个locktime交易可以比它的时间锁正式过期最多提前两个小时加入区块链。此外,块不是以确定的时间间隔创建,因此任何尝试取消有价值的交易都应在时间锁到期前几个小时来实施。

以前版本的Bitcoin Core提供了一个功能,防止交易签名者使用上述方法来取消时间锁定的交易,但禁用此功能的必要部分以防止拒绝服务攻击。该系统的遗留是每个输入中的四字节序列号。该序列号旨在允许多个签名者去更新交易;当他们更新完交易,他们统一将每个输入的序列号设置为四字节无符号最大值(0xffffffff),允许这个交易被加入一个区块,甚至当它的时间锁还没过期时候。

即使在今天,将所有序列号设置为0xffffffff(Bitcoin Core中的默认值)仍然可以禁用时间锁定,因此,如果要使用locktime,至少需要一个输入必须有序列号低于最大值。由于序列号不被网络用于任何其他目的,将任何序列号设置为零足以启用locktime

Locktime本身是一个无符号的4字节整数,可以通过两种方式进行解析:

如果小于500万,则将locktime解析为块高度。该交易可以添加到任何具有该高度或更高的块。

如果大于等于500万,则使用Unix纪元时间格式解析locktime(从1970-01-01T00:00 UTC到目前为止已经过去的秒数,当前超过13.95亿)。交易可以被添加到块时间大于locktime的任何块。

交易费及其变动

交易根据签名交易的总字节大小支付交易费。基于在挖出区块中的当前空间需求来计算每字节的交易费,需求增加,交易费增加。交易费是给比特币矿工的,因此最终送达每个矿工去选择他们接受的最小交易费。

这就是所谓的高优先级交易的概念,它们花费长期没挪用的satoshis。

过去,这些“优先”交易往往免除正常的费用要求。在Bitcoin Core 0.12之前,每个块的50KB将被保留用于这些高优先级事务,但是默认情况下现在设置为0KB。在优先级区域之后,所有交易都将按照每字节的费用进行优先排序,同时按顺序添加更高费用的交易,直到所有可用空间都被填满。

从Bitcoin Core 0.9开始,在网络之间广播事务有最低费用(目前为1000 satoshis)。任何仅支付最低费用的交易应该准备好等待很长时间才能在块中有足够的备用空间来包含它。请参阅验证支付部分去明白为什么这很重要。

由于每个交易都花费未消费的交易输出UTXOs,并且一个UTXO只能被花费一次,UTXOs的全额必须被花费或作为交易费支付给矿工。很少有人有与他们想支付的数额完全相符的UTXOs,因此多数交易都包含一个找零输出。

找零输出是花费UXTOs中剩余的satoshis返回到花费者的常规输出。它们可以重复使用与UTXO中使用的相同的P2PKH pubkey哈希或P2SH 脚本哈希,但是由于下一小节,强烈建议将找零输出发送到新的P2PKH或P2SH地址。

避免公钥重用

在交易中,花费者和接收者各自显示交易中使用的所有公钥或地址。这允许任何一个人使用公开块链跟踪涉及其他人相同的公钥或地址的过去和未来交易。

如果经常使用相同的公钥,就像人们将Bitcoin 地址(哈希后公钥)一样,作为静态支付地址一样,其他人可以轻松跟踪该人的接收和消费习惯,包括他们在已知的地址中控制的satoshis数额。

它不一定是这样的。如果每个公钥被使用两次,一次就可以收到付款,并且一次支付这笔款项,用户就可以获得大量的财务隐私。

更好的是,在接受付款时使用新的公钥或唯一地址,创建找零输出时混合使用后面讨论的其他技术,比如CoinJoin或合并规避,使得非常难以使用区块链本身来可靠地跟踪用户如何接收和花费他们的satoshis。

避免密钥重用还可以提供安全性,防止可能允许从公钥(假设)或签名比较来重建私钥(在下面描述的某些情况下,假设更普遍的攻击)。

唯一(非重用)P2PKH和P2SH地址通过保持ECDSA公钥隐藏(哈希后的)直到发送到那些地址的satoshis第一次被花费来保护对抗第一种类型的攻击, 所以这种攻击实际上是无效的,除非他们能一两个小时内重构私钥,这段时间内交易通过区块链得到很好的保护。

唯一(非重用)的私钥通过一个私钥仅生成一个签名来保护免于第二种类型的攻击,所以攻击者永远得不到后续的签名用于基于比较的攻击。现有的基于比较的攻击仅在当前使用熵不足的情况下才能实现,或者当所使用的熵被暴露在某些方面(例如侧信道攻击)时。

因此,为了隐私和安全,我们鼓励您构建应用程序,以避免公钥重用,并在可能的情况下阻止用户重用地址。如果您的应用程序需要提供一个固定的URI来发送付款,请参阅下面的bitcoin:URI部分。

交易可扩展性

Bitcoin没有哪种类型的签名哈希能保护签名脚本,这样给限定的拒绝服务攻击及交易可扩展性留了后门。签名脚本包含secp256k1签名,它不能自签,允许攻击者对事务进行非功能修改,而不会使其无效。例如,攻击者可以向签名脚本添加一些数据,该脚本将在前一个pubkey脚本被处理之前被删除。

虽然这些修改是非功能的,因此它们不会改变交易使用的输入以及它支付的输出 - 它们会改变交易的计算到的哈希值。由于每个交易使用散列作为交易标识符(txid)链接到以前的交易,所以修改的交易将不会有其预期的创建者的txid。

对于设计为立即添加到块链的大多数比特币交易来说,这不是问题。但是,当交易被添加到块链之前,事务的输出被花费时,确实会成为一个问题。

比特币开发人员一直致力于在标准交易类型中减少交易可扩展性,但是完整的修复仍然还在规划阶段。目前,新交易不应该依赖于尚未添加到块链的先前交易,特别是如果大量的satoshis受到威胁。

交易可扩展性也会影响付款跟踪。Bitcoin Core的RPC接口让您可以通过txid跟踪交易,但是如果txid因为交易被修改而改变,那么可能会出现交易已从网络中消失。

交易跟踪的当前最佳做法规定,交易应该由它作为输入的交易输出(UTXOs)进行跟踪,因为它们不能更改而不会使交易无效。

最佳做法进一步规定,如果交易确实似乎从网络中消失,需要重新发行,则会以无效的丢失交易方式重新发行。始终工作的一种方法是确保重新发放的支付花费与输入所使用的丢失交易的所有相同的输出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息