比特币开发者指南

查找有关Bitcoin协议和相关规范的详细信息。

BETA:本文档没有被Bitcoin专家广泛审查,因此可能包含许多错误。请使用左下角菜单中的编辑链接来帮助我们改进。点击此处关闭此免责声明。X

开发者指南旨在提供您了解Bitcoin所需的信息,并开始构建基于Bitcoin的应用程序,但它不是规范为了充分利用本文档,您可能需要从或从预编译的可执行文件安装当前版本的Bitcoin Core。

有关Bitcoin开发的问题最好在比特币开发社区中询问。与Bitcoin.org的文档相关的错误或建议可以作为问题提交或发布到比特币文档邮件列表

在以下文档中,一些字符串已被缩短或折叠:“[...]”表示删除了额外的数据,并且在下面继续以单个反斜杠“\”结尾的行。如果将鼠标悬停在段落上,交叉引用链接将显示为蓝色。如果将鼠标悬停在交叉引用链接上,则该术语的简要定义将显示在工具提示中。

区块链

区块链提供了比特币的公共帐本,这是一个有序有时间戳的交易记录。该系统用于防止重复花费(双花)和修改先前的交易记录。

Bitcoin 网络中的每个完整节点独立地存储仅包含由节点验证过的区块链当多个节点在其块链中都具有相同的时,它们被认为达成了共识这些节点遵循维持共识验证规则被称为共识规则本节将介绍Bitcoin Core使用的许多共识规则

区块链概述

Block Chain Overview

上图显示了区块链的简化版本。一个或多个新交易的被收集到一个区块的交易数据部分中。每个交易数据的副本将被哈希, 这些哈希值两两配对,再被hash,直到得到最顶层的一个hash值,这就是一棵merkle树

merkle树根存储在这个区块标头中。每个还存储着先前块的标头的哈希值,这样将链接在一起。这样可以确保事务无法修改,而不修改记录该事务的块以及所有后续

交易记录也被链接在一起。比特币钱包软件给人的感觉是从钱包发送或接收多少聪(satoshi,单位)的比特币,但实际上比特币真的是在交易中流转。每个交易花费先前在一个或多个较早的交易中接收到的多少聪的比特币,因此一个交易的输入是先前交易的输出

Transaction Propagation

单个交易可以创建多个输出,就像发送到多个地址时的情况一样,但是特定交易的每个输出只能是在区块链中用作输入一次。任何后续的引用都禁止重复花费 - 尝试花费相同的多少聪的比特币两次。

输出绑定到交易标识符(TXID)上,这是验证过的交易的哈希值。

因为特定交易的输出只允许花费一次,那所有包含在区块链上的交易输出可以归类为未花费的交易输出(UTXOs)或已花费的交易输出要想付款有效,只能使用未花费的交易输出UTXOs作为输入

先不讲比币基交易(后面会详述),若一个交易的输出超过交易的输入,这个交易将会被拒绝--但是若是输入超出输出,这个差值可以作为交易费奖励给创建这个区块的比特币矿工例如,在上图中,每个交易相比它相关的交易输入花费了10000,实际上就是支付了10000交易费

工作量证明(POW)

区块链是有由网络中对等的节点共同维护的,所以比特币要求每个区块需要一定工作量投入确保想篡改历史区块的不诚实节点比想在区块链上创建新区块的诚实节点需要付出更大代价。

链接区块在一起使得在不修改所有后面的区块情况下不可能修改此区块中包含的交易。结果,修改特定区块的成本随着添加到区块链中的每个新区块而增加,放大了工作量证明的效果。

比特币中使用的工作量证明利用了密码散列的显式随机特性。一个好的加密哈希算法将任意数据转换成看似随机的数字。如果以任何方式修改数据并且重新得到哈希值,则会产生一个新的看似随机的数字,因此无法修改数据以使哈希数值可被预测。

为了证明你做了一些额外的工作来创建一个区块,你必须创建一个不超过某个值的区块头的散列。例如,如果最大可能的哈希值为2256 - 1,则可以证明您通过产生哈希值来两两组合最多尝试次数小于2255

在上面给出的例子中,平均每个其他尝试都会产生一个成功的哈希。您甚至可以估计给定哈希尝试将生成低于目标阈值的数字的概率。比特币假设一个线性概率,它使得目标阈值越低,平均需要更多的哈希尝试。

新的会被添加到块链,仅当它们的哈希难度至少与共识协议所期望的难度值相当。每2,016 个比特币网络会使用存储在每个块头中的时间戳来计算生成上一个2,016个的第一个区块和最后一个区块之间经过的秒数。这个理想值为1,209,600秒(两周)。

(注意:Bitcoin Core实现中的一个时间戳错误导致每2016块更新难度值仅从2015 开始,产生轻微的偏移。)

因为每个块头必须哈希到低于目标阈值的值,并且因为每个块链接到前一个块,平均要求扩散一个更改过的区块哈希算力与整个比特币网络在初始块的创建时间和当前时间之间所消耗的一样多。只有您获得了大部分网络的哈希功能,您才可以根据交易历史可靠地执行这种51%的攻击(尽管如此,应该注意的是,即使小于50%哈希算力仍然有很好的机会进行这种攻击)。

块头提供了几个易于修改的字段,例如专用的nonce字段,因此获取新的哈希不需要等待新的交易到来。此外,只有80字节的块头被散列以用于工作量证明,因此在中包含大量的交易数据不会因有额外I/O减慢求哈希速度,添加附加交易数据只需要重新计算merkle树中的祖先哈希值。

块高和分叉

任何算出块头的哈希值低于目标阈值的比特币矿工可以将整个添加到块链上(假设有效)。这些通常由它们的块高度 定位的 - 即它们与第一个比特币之间的块数量块0,最通常称为创世块)。例如,2016是可以首先调整难度值的地方。

Common And Uncommon Block Chain Forks

多个区块可能有相同的区块高度,当2个或多个矿工在大致相同时间内产生合适新区块的时候这很常见。这在块链中产生了一个明显的分叉,如上图所示。

矿工同时在区块链尾部产生新区块,每个节点独立选择接受哪个区块在没有其他考虑的情况下,如下所述,节点通常使用他们看到的第一个

最终,一个矿工产生另一个,它只附加在一个竞争同时开采的中。这使得的那侧比另一侧更强。假设一个分叉仅包含有效区块,正常节点通常紧接着最难的链来增长,抛弃那些属于短分叉过期区块过期块有时也称为孤儿或孤儿块,但是这些术语也用于没有已知父的真孤儿块

如果不同的矿工工作在相反的目的,例如一些矿工努力扩展块链,同时其他矿工正在尝试发起51%的攻击来修改交易历史,则长期分叉是可能的。

由于在区块链 的分叉中,多个可以具有相同的高度区块高度不应该被用作全局唯一标识符。反而,通常由它们的块头的哈希引用(通常以字节顺序颠倒,十六进制形式)。

交易数据

每个必须包含一个或多个交易。这些交易中的第一个必须是一个币基交易,也称为生成交易,它应该收集并花费块奖励(包括补贴以及由中包含的交易支付的任何交易费用)。

币基交易未花费的交易输出UTXO有一个特殊的条件,它至少需要100个产生之后才能使用(用作输入)。这暂时阻止矿工花费从一个分叉上以后可能被淘汰的区块(因此币基交易也被删除)上花费交易费区块奖励

区块不需要包括任何非币基交易,但是矿工几乎总是包含额外的交易,以便于获得他们的交易费

包括币基交易在内的所有交易都以二进制原始交易格式编码入

原始交易格式被散列为交易标识符(txid)。通过这些txidmerkle树是通过将每个txid与另一个txid配对,然后将它们组合在一起构成的。如果有奇数个txids,那么没有合作伙伴的txid会使用自己的副本进行散列。

所产生的哈希本身每个都与另一个哈希配对,并且散列在一起。没有伙伴的哈希求自身的hash值。该过程重复,直到只剩下一个散列,这就是merkle根

例如,如果交易仅是加入(不是求哈希),那么一个五个交易的merkle树将如下图所示:

       ABCDEEEE .......Merkle root
      /        \
   ABCD        EEEE
  /    \      /
 AB    CD    EE .......E is paired with itself
/  \  /  \  /
A  B  C  D  E .........Transactions

根据在简单支付验证(SPV)子节的讨论, merkle树允许从一个完全分片块头和一系列中间hash值得到的merkle根来验证区块中的一个交易。完整的分片不需要都被信任:伪造块头是昂贵的,中间哈希值不能被伪造或者会验证失败。

例如,为了验证交易D被添加到区块SPV客户端除了merkle根之外只需要C,AB和EEEE散列的副本;客户不需要知道任何其他交易。如果这个中的五个交易都处于最大值,下载整个将需要超过500000个字节,但是下载三个散列加上块头只需要140个字节。

注意:如果在相同的块中找到相同的txids,则可能merkle树与由于markle树实现的不平衡导致有部分或全部重复项被删除的块,发生了碰撞(重复的孤儿哈希)。由于使用相同的txids的单独交易是不合实际的,但这不会对诚实软件造成负担,但是必须检查的无效状态是否被缓存;否则,删除重复项的有效可以具有相同的merkle根哈希,但被缓存的无效结果拒绝,导致例如CVE-2012-2459的安全漏洞。

共识规则更改

为了维护共识,所有全部节点使用相同的共识规则验证然而,有时共识规则要被改变以引入新功能或阻止网络被滥用。当新规则实施时,可能会有一段时间,当未升级的节点遵循旧规则并且升级节点遵循新规则时,导致两种可能的方式打破共识

  1. 遵循新共识规则被升级后节点接受,但被未升级的节点拒绝。例如,在中使用新的交易功能:升级节点了解该功能并接受该功能,但未升级的节点拒绝因为它违反了旧的规则。

  2. 违反新的共识规则被升级后节点拒绝,但被未升级的节点接受。例如,在中使用了滥用交易功能:升级后节点拒绝它,因为它违反了新规则,但未升级的节点接受它,因为它遵循旧的规则。

第一种情况,被未升级的节点拒绝,那些从其他未升级节点获取区块链数据的挖矿软件拒绝和那些从升级过的节点获取数据的挖矿软件在同一条链上创建。这将创建永久性发散的链 - 一个用于未升级的节点,一个链接用于升级节点-- 这被称为硬分叉

Hard Fork

第二种情况,被升级后的节点拒绝,如果升级后的节点控制着主要的hash速率则可能阻止区块链永久发散。这是因为,在这种情况下,未升级节点和升级过的节点一样接受相同的快,所以升级后的节点可以创建一个更强的链,也被未升级节点所接受。这被称为软分叉

Soft Fork

虽然一个分叉实际上是区块链上发散,共识规则的改变经常被描述为它们潜在地创建一个硬分叉或者软分叉。例如,“将的大小增加到1 MB以上需要一个硬分叉“。在这个例子中,一个实际的块链 分叉不是必需的 - 但这是一个可能的结果。

共识规则的改变可能会以各种方式激活更改。在Bitcoin的前两年,中本聪同过发布立刻强制使用新规则的向后兼容的客户端改变执行了几次软分叉。已经通过标记日激活了多个软分叉,例如BIP30,其中新规则开始在预设时间或块高度强制执行。通过标记日激活的分叉被称为用户激活软分叉,因为他们取决于足够多的用户节点在标记日之后强制使用新规则。

稍后软分叉等待多数哈希速率(通常为75%或95%)来显示他们对执行新共识规则准备就绪。一旦信令阈值已经通过,所有节点将开始执行新规则。这样的分叉被称为矿工激活软分叉(MASF),因为它们依赖于矿工进行激活。

资源: BIP16,BIP30BIP34被实现为可能导致软分叉。BIP50描述了一个意外的硬分叉和一个蓄意的硬分叉可以通过临时降级升级后节点的能力来解决。来自Gavin Andresen的文档概述了如何实施未来的规则更改

检测分叉

未升级的节点可以在两种类型的分叉中使用和分发不正确的信息,从而出现可能导致财务损失的几种情况。特别地,未升级的节点可以中继和接受被升级的节点认为无效的交易,因此永远不会成为普遍认可的最佳区块链的一部分。未升级的节点可能拒绝中继那些已经或者即将添加到最佳区块链里面的区块或者交易,因此它们提供不完整的信息。

比特币核心包括通过查看块链工作量证明检测硬分叉如果一个未升级的节点接收到的区块链头显示至少比最佳链的工作量多6个区块,这个节点就会在getinfo RPC里报一个错误,并在设置的情况下执行-alertnotify命令。这会提醒操作者未升级的节点不能切换到可能的最佳区块链。

完整节点也可以检查区块和交易的版本号。如果当前几个区块中的区块或者交易的版本号比这个节点使用的版本号高,可以假设它没有使用当前的共识规则。Bitcoin Core 0.10.0如果设置了会通过getinfo RPC-alertnotify命令报告此情况。

无论哪种情况,区块和交易数据都不能被信任如果它们来自于一个明显不使用当前共识规则的节点。

连接到全节点的SPV客户端通过连接多个全节点,确保他们在同一个链上具有相同的块高,或者根据账户传输时延和过期与否增减几个区块,能够测到一个可能的硬分叉。如果存在分歧,客户端可以使用较弱的链从节点断开连接。

SPV客户端还应该监控块和交易的版本号增加,以确保它们用当前的共识规则处理接收到的交易。

交易

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

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

The Parts Of A Transaction

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

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

Spending An Output

输出根据其在交易中的位置具有隐含的索引号 - 第一个输出输出零。输出也有多少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脚本是:

OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG

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

<Sig> <PubKey> OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG

脚本语言是一种类似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)或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脚本形式

Pubkey script: OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
Signature script: <sig> <pubkey>

支付脚本哈希(P2SH)

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

Pubkey script: OP_HASH160 <Hash160(redeemScript)> OP_EQUAL
Signature script: <sig> [sig] [sig...] <redeemScript>

Multisig

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

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

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

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

Pubkey script: <m> <A pubkey> [B pubkey] [C pubkey...] <n> OP_CHECKMULTISIG
Signature script: OP_0 <A sig> [B sig] [C sig...]

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

Pubkey script: OP_HASH160 <Hash160(redeemScript)> OP_EQUAL
Redeem script: <OP_2> <A pubkey> <B pubkey> <C pubkey> <OP_3> OP_CHECKMULTISIG
Signature script: OP_0 <A sig> <C sig> <redeemScript>

Pubkey

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

Pubkey script: <pubkey> OP_CHECKSIG
Signature script: <sig>

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。

Pubkey Script: OP_RETURN <0 to 40 bytes of data>
(Null data scripts cannot be spent, so there's no signature script.)

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源代码中称为nLockTimelocktime表示交易可以添加到块链的最早时间。

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。

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

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

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

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

交易可扩展性

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

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

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

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

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

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

最佳做法进一步规定,如果交易确实似乎从网络中消失,需要重新发行,则会以无效的丢失交易方式重新发行。始终工作的一种方法是确保重新发放的支付花费与输入所使用的丢失交易的所有相同的输出

合约

合约是使用去中心化的比特币系统来执行财务协议的交易。通常可以制定比特币合约,以尽量减少对外部代理的依赖,例如法院系统,这显著降低了与金融交易中未知实体交易的风险。

以下小节将描述已经使用的各种比特币合约。因为合约是与真人打交道,而不仅仅是交易,它们在特定故事格式框架之中。

除了下述的合约类型,还提出了许多其他合约类型。其中几个收集在Bitcoin Wiki的合约页面

托管和仲裁

Charlie客户想从Bob商人购买一种产品,但他们都不信任对方,所以他们使用合同帮助确保Charlie获得商品,Bob获得付款。

一个简单的合同可以说Charlie会把satoshis花费到某个输出,只有Charlie和Bob都签名了这个输入才能花费它。这意味着鲍勃不会得到报酬,除非查理得到他的商品,但查理无法获得商品和保留他的付款。

如果有争议,这个简单的合约没有什么帮助,所以Bob和Charlie征求了Alice仲裁员的帮助来创建一个托管合约Charlie花费这些satoshis到一个输出,这个输出只有三人中的两人都签名输入的话才能被花费。现在如果一切正常Charlie可以支付给Bob,如果有问题的话Bob可以退款,或者如果有争议的话爱丽丝可以仲裁并决定谁应该得到satoshis

要创建一个多重签名multisig输出,他们每个给他人一个公钥然后,Bob创建以下P2SH multisig redeem脚本

OP_2 [A's pubkey] [B's pubkey] [C's pubkey] OP_3 OP_CHECKMULTISIG

(将公钥压入堆栈的Opcodes未显示。)

OP_2OP_3将实际数字2和3压入堆栈上。OP_2指定需要2个签名,OP_3指定需要提供3个未哈希过的公钥。这是一个2-of-3 multisig pubkey脚本,更一般地称为m-of-n pubkey脚本(m是需要的最小匹配的签名,n是提供公钥的数量)。

Bob向Charlie发送redeem脚本,Charlie检查确定他的公钥和Alice的公钥然后,他将redeem 脚本求哈希,来创建P2SH redeem脚本,并向其支付satoshisBob看到付款被添加到块链并运送商品。

不幸的是,商品在运输途中受到轻微破坏。Charlie想要一个完整的退款,但Bob认为10%退款是足够的。他们转向Alice来解决这个问题。Alice要求Charlie的照片证据,以及Bob创建查理检查过的redeem脚本的副本。

在查看证据后,Alice认为40%的退款是足够的,所以她创建并签署两个输出的交易,一个花费satoshis到Bob的公开密钥,剩下的40%花到Charlie的公钥

签名脚本中,Alice把她的签名和Bob创建的未哈希序列化的兑换redeem的副本放入其中。她向Bob和Charlie提供了不完整交易的副本。他们中的任何一个可以通过添加他的签名来完成它,以创建以下签名脚本

OP_0 [A's signature] [B's or C's signature] [serialized redeem script]

(将签名redeem脚本压入堆栈的Opcodes未显示。OP_0是原始实现中一个错误的解决方法,必须保留兼容性。请注意,签名脚本必须以与redeem脚本中出现的相应的公钥相同的顺序提供签名有关详细信息,请参见OP_CHECKMULTISIG中的说明。)

当交易被广播到网络时,每个节点根据Charlie先前支付的P2SH输出检查签名脚本,确保redeem脚本和之前提供的redeem脚本哈希值相匹配。然后评估将两个签名用作输入数据的兑换脚本假设兑换脚本验证成功,两个交易输出显示在Bob和Charlie的钱包中作为可支付余额。

然而,如果Alice创建并签名了一个交易,他们都不会同意,例如将所有的satoshis支付给她自己,Bob和Charlie可以找到一个新的仲裁员,并签署一个支付satoshis的交易到另一个2-of-3 mutlisig redeem脚本哈希, 这个包括一个新的仲裁员的公钥。这意味着鲍勃和查理从来不需要担心他们的仲裁员偷他们的钱。

资源: BitRated在GNU AGPL授权网站上使用HTML/JavaScript提供了一个multisig仲裁服务接口。

微支付渠道

Alice还为Bob兼职审核论坛帖子。每当有人发帖到Bob的论坛,Alice都要浏览它,确保它不会令人反感或垃圾帖子。但Bob经常忘记付钱给她,所以Alice要求在她批准或拒绝的每个帖子后立即支付。鲍勃说他不能这么做,因为数百笔小额付款会在交易费用中花费成千上万的satoshis,所以Alice建议使用微支付渠道

Bob向Alice询问她的公钥,然后创建两个交易。第一笔交易支付100 millibitcoins到一个P2SH输出,它的2-of-2 multisig redeem 脚本需要Alice和Bob都签名。这就是债券交易。广播此交易将让Alice持有millibitcoins质押币,所以Bob现在将交易私有,并创建第二个交易。

第二个交易花费所有第一个交易的millibitcoins(减去交易费)在由locktime强制执行的24小时延时后返回给Bob。这是退款交易。Bob不能自己签名退款交易,所以他把它交给Alice签字,如下图所示。

Micropayment Channel Example

Alice检查退款交易的locktime是未来24小时,签名,并将其副本返回给Bob。然后,她询问Bob进行债券交易,并检查退款交易是否支付债券交易的输出她现在可以将债券交易广播到网络,以确保鲍勃必须等待时间锁定到期,然后再进一步消费他的millibitcoinsBob迄今为止还没有花费任何东西,除了可能还有一个小的交易费,他将能够在24小时内广播一个完整的退款退款交易。

现在,当Alice做值1 millibitcoin的工作时,她要求Bob创建并签署新版本的退款交易。交易的第二版将1 millibitcoin花费到Alice,另一个99返回给Bob;它没有锁定时间,所以Alice只要她想要就可以签署并花费它。(但她不会立即这样做)

Alice和Bob重复这些工作和付款步骤,直到Alice完成一天的工作,或者直到时间锁定即将到期。Alice签署了退款交易的最终版本,并将其广播,支付给自己,并将余下的余额退还给Bob。第二天,当Alice开始工作时,他们创建一个新的微支付渠道

如果Alice在时间锁定到期之前未能广播退款事务的新版本,则Bob可以广播第一个版本并收到一个完整的退款这是微支付渠道最适合小额付款的一个原因 - 如果Alice的互联网服务在时间锁定到期时间断了几个小时,她可能会被骗走付款。

交易可扩展性,在上面交易部分讨论过,是限制微支付渠道的价值的另一个原因。如果有人使用交易可扩展性来破坏两个交易之间的链接,即使没有完成任何工作,Alice也可以保留Bob的100 millibitcoins质押币。

对于较大的付款,比特币交易费用占交易总额的百分比非常低,因此使用立即广播的单独交易来保护付款更有意义。

资源: bitcoinj Java库提供了一组完整的微支付功能,一个示例实现,以及一个教程,全部基于Apache许可证。

CoinJoin混币技术

Alice很担心她的隐私。她知道每个交易都被添加到公共块链中,所以当Bob和Charlie付钱时,他们可以轻松跟踪satoshis来了解她支付过的Bitcoin 地址,支付多少钱,还有她还剩多少satoshis

Alice不是一个犯罪分子,她只是希望对获知她花费及她剩余的satoshis合理地拒绝,所以她在她的电脑上启动了Tor匿名服务,并以"AnnoGirl"登陆到IRC聊天室。

同样“Nemo”和“Neminem”也在聊天室,他们共同地同意将satoshis彼此转移,除了他们之外,没有人可以肯定谁控制这些satoshis但是他们面临两难的境地:谁先将他们的satoshis转移到另外两个假名的人之一?CoinJoin风格的合约如下图所示,使得这个决定变得容易:他们创造了一个单一的交易,同时进行所有的支出,确保没有人能够窃取他人的satoyis

Example CoinJoin Transaction

每个参与者都可以查看他们可以花费的100 millibitcoins的UTXOs的集合信息。然后,他们每个都会生成一个全新的公钥,并向协调人提供UTXO细节和pubkey哈希在这种情况下,协调者是AnonGirl;她将每个UTXO的交易创建到三个相等大小的输出一个输出转到每个参与者pubkey哈希

AnonGirl然后使用SIGHASH_ALL签名输入,以确保没有人可以更改输入输出详细信息。她向Nemo提供部分签名的交易,他们以相同的方式签署他的输入,并将其传递给同样的方式签署的Neminem。Neminem然后将交易广播到P2P网络,将所有millibitcoins混合在一个交易中。

如图所示,除了AnonGirl,Nemo和Neminem之外,任何人都无法肯定谁收到哪个输出,所以他们可以不可追踪地花费他们的输出。

现在当Bob或Charlie尝试通过块链跟踪Alice的交易时,他们还将看到Nemo和Neminem所做的交易。如果Alice做了更多的混币交易,Bob和Charlie可能不得不猜测几十个或几百个人中哪些实际上是由爱丽丝制造的交易。

Alice's satoshis的完整历史仍然在块链中,所以一个确定的调查员可以和AnonGirl CoinJoined的人交谈,找出她的最终来源satoshis,可能会将AnonGirl显露为Alice。但是,对于随便查看区块链历史信息的人来说,爱丽丝获得了合理的可否认性。

上文所述的CoinJoin技术使参与者花费少量satoshis来支付交易费一种替代技术,购买者CoinJoin,实际上可以保存他们satoshis,同时提高他们的隐私。

AnonGirl等待IRC聊天室,直到她想要购买。她宣布她打算使用satoshis,等待有人想要从另一个商家进行购买。然后,它们以与之前相同的方式组合它们的输入,但将输出设置为单独的商户地址,所以没有人能够仅从块链历史单独指出其中一个人从商人那里买了什么。

由于他们不得不支付交易费来进行购买,因此AnonGirl和她的共同消费者不需要支付任何额外费用,但是由于它们通过组合多个交易减少开销,节省了字节数,他们可能能够支付较小的集合交易费,从而为他们致以节省少量的satoshis

资源:在撰写本文时,去中心化的CoinJoin的Alpha版本的实现是CoinMux,仍然是Apache许可。

钱包

比特币钱包可以指代钱包程序或钱包文件。电子钱包程序创建公共密钥接收satoshis,并使用相应的私钥花费satoshis 。电子钱包文件存储私钥和(可选的)与钱包程序的交易相关的其他信息。

钱包程序和钱包文件在下面的不同小节中介绍,本文档试图澄清我们在谈论是钱包程序或钱包文件。

钱包软件

允许satoshis的接收和支出是钱包软件的唯一基本特征,但是特定的钱包程序不需都要做这两件事情。两个钱包程序可以一起工作,一个程序分发公钥以便接收satoshis,另一个程序签名交易来花费那些 satoshis

钱包程序还需要与P2P网络进行交互,以从块链获取信息并广播新的交易。然而,分发公钥或签名交易的程序就不需要与P2P网络本身进行交互。

这使我们有一个钱包系统的三个必要但可分离的部分:一个公钥分发程序,签名程序和联网程序。在下面的小节中,我们将介绍这些部分的常见组合。

注意:我们讲通常是分发公钥在许多情况下,将分发P2PKH或P2SH哈希来代替公钥,实际的公钥仅在它们控制的输出被花费时候分发。

全服务的钱包

最简单的钱包是一个执行所有三个功能的程序:它产生私钥,导出相应的公钥,根据需要帮助分发公钥,监视花费在公钥上的输出,创建并签名花费那些输出的交易,并广播签名过的交易。

Full-Service Wallets

在撰写本文时,几乎所有流行的钱包可用作全服务钱包

全方位服务钱包的主要优点是易于使用。一个程序执行用户需要接收的所有东西,并花费satoshis

全服务钱包的主要缺点是它们在连接到互联网的设备上存储私钥这种设备被入侵是常见的事情,因特网连接使得容易将私钥从被感染的设备发送给攻击者。

为了防盗,许多钱包程序为用户提供可选项加密包含私钥钱包文件。当私钥不被使用时,它可以提供保护,但是它不能防止捕获密钥的攻击或从内存中读取解密密钥。

只签名钱包

为了提高安全性,可以通过在更安全的环境中运行的单独的钱包程序来生成和存储私钥这些仅签名的钱包与与对等网络交互的网络钱包配合使用。

典型的只签名钱包软件使用确定的密钥产生器(稍后描述)去创建父私钥公钥,它们可以创建新的子私钥公钥。

Signing-Only Wallets

首次运行时,只签名的钱包创建一个父私钥,并将相应的父公钥传送到联网的钱包。

联网的钱包使用父公钥导出子公钥,可选的帮助分发它们,或者监控花费在那些公钥上的输出,为花费那些输出创建未签名的交易,并传输未签名的交易到只签名钱包。

通常用户使用只签名钱包有机会看到未签名交易的细节特别是输出细节。

在可选的检查步骤之后,只签名钱包使用父私钥导出合适的子私钥,并签名那些交易,发送签名过的交易给联网的钱包。

然后联网的钱包将签名过的交易广播到P2P网络上。

以下小节描述了只签名钱包的两个最常见的变体:离线 钱包和硬件钱包

离线钱包

一些完整的全服务钱包程序可以作为两种独立的钱包运行:一个程序实例作为只签名钱包(通常称为离线钱包),另外一个程序实例联网钱包(通常称为在线钱包或只观看钱包)。

离线钱包如此命名,因为它打算在不连接到任何网络的设备上运行,大大减少了攻击次数。如果是这种情况,通常由用户使用可移动介质(如USB驱动器)处理所有数据传输。用户的工作流程如下:

  1. (离线)禁用设备上的所有网络连接,并安装钱包软件。在离线模式下启动钱包软件,以创建父私钥和公钥父公钥复制到可移动媒体。

  2. (在线)将钱包软件安装在另一台设备上,该设备联网,并从可移动媒体导入父公钥像使用全服务钱包一样,分发公钥来接收付款。当准备花费satoshis时,填写输出细节,并将钱包生成的未签名交易保存到可移动媒体。

  3. (离线)在脱机实例中打开未签名的事务,查看输出详细信息,确保他们将正确的金额用于正确的地址这可以防止在线钱包上的恶意软件欺骗用户签署支付给攻击者的交易。经过审查,将交易签名并将其保存到可移动媒体上。

  4. (在线)在在线实例中打开签名的交易,以便将其广播到P2P网络

脱机钱包的主要优点是可以大大提高全服务钱包的安全性。只要离线钱包不会受到损害(或有缺陷),用户在签署之前审核所有外发交易,即使在线钱包被破坏,用户的satoshis也是安全的。

离线钱包的主要缺点是麻烦。为了安全最大化,它们要求用户仅将一个设备专用于离线任务。离线钱包必须在资金花费时启动,用户必须将数据在在线设备和离线设备中来回复制。

硬件钱包

硬件钱包是专用于运行只签名钱包的设备。他们的奉献使他们能够消除设计用于一般用途的操作系统中存在的许多漏洞,从而允许他们直接与其他设备通信,以便用户不需要手动传输数据。用户的工作流程如下:

  1. (硬件)创建父私钥和公钥将硬件钱包连接到网络设备,以便它可以获取父公钥

  2. (网络)正如使用全服务钱包一样,分发公钥来接收付款。当准备花费satoshis时,填写交易详情,连接硬件钱包,然后点击支出。联网的钱包将自动发送交易详细信息到硬件钱包

  3. (硬件)在硬件钱包的屏幕上查看交易详情。某些硬件钱包可能会提示输入密码或PIN码。硬件钱包将事务签名并上传到联网的钱包

  4. (网络化)联网的钱包从硬件钱包接收签名的交易,并将其广播到网络

硬件钱包的主要优点是可以大大提高全服务钱包的安全性,而且不需要比离线钱包更麻烦。

硬件钱包的主要缺点是麻烦。即使麻烦小于离线钱包,用户仍然必须购买硬件钱包设备,并在他们需要使用签名钱包签名交易时候随身携带。

一个额外的(希望是暂时的)缺点是,在撰写本文时,流行的钱包程序很少支持硬件钱包 - 虽然几乎所有流行的钱包程序已宣布有意外去至少支持一种硬件钱包型号

仅分发钱包

钱包程序在难以安全的环境中运行(如Web服务器)可以设计为只分发公钥(包括P2PKH或P2SH地址 )而没有更多功能。设计这些极简钱包有两种常见的方法:

Distributing-Only Wallets

  • 使用多个公开密钥地址预填充数据库,然后使用其中一个数据库条目根据请求分发pubkey脚本地址。为了避免密钥重用,Web服务器应该跟踪使用的密钥,永远不会用完公钥。通过使用父公钥,可以使其更容易,如下一个方法中所建议的那样。

  • 使用父公钥创建子公钥为了避免密钥重用,必须使用一种方法来确保相同的公钥不分发两次。这可以是分配的每个密钥的数据库条目或指向密钥索引号码的递增指针。

这两种方法都不会增加大量的开销,特别是如果数据库被用于关联输入的付款和一个单独的公钥来跟踪付款的时候。有关详细信息,请参阅支付流程部分。

钱包文件

比特币钱包核心就是私钥的集合。这些集合以数字方式存储在文件中,甚至可以物理存储在纸张上。

私钥格式

私钥是用于从特定的地址解锁satoshis在比特币中,标准格式的私钥只是一个256位数字,在值的范围是:

0x01 and 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4140, 几乎覆盖 2256-1个数值的整个范围.该范围由比特币使用的secp256k1 ECDSA加密标准决定。

钱包导入格式(WIF)

为了使私钥的复制不容易出错,可以使用钱包导入格式WIFWIF私钥上使用base58Check编码,大大降低了复制错误的几率,这和标准比特币地址很类似 。

  1. 拿出一个私钥

  2. mainnet 地址之前添加一个0x80字节或testnet 地址添加一个0xef字节。

  3. 如果它与压缩的公钥一起使用(在后面的小节中描述),则会添加一个0x01字节。如果与未压缩的公钥一起使用,则不附加任何内容。

  4. 在扩展密钥上执行SHA-256哈希。

  5. 对SHA-256哈希的结果执行SHA-256哈希。

  6. 取第二个SHA-256哈希的前四个字节;这是校验和。

  7. 从第2点的扩展密钥的末尾添加第5点的四个校验和字节。

  8. 使用Base58Check编码将结果从字节字符串转换为Base58字符串。

该过程很容易可逆,使用Base58解码功能,并删除填充。

迷你私钥格式

Mini 私钥格式是一种用于在30个字符内编码私钥的方法,可以将密钥嵌入在小物理空间中,例如物理比特币令牌,抗损害的QR码。

  1. 迷你密钥的第一个字符是'S'。

  2. 为了确定迷你私钥是否格式正确,在私钥中添加了一个问号。

  3. 计算SHA256哈希。如果产生的第一个字节为“00”,则格式良好。这个关键的限制是错误检测机制。用户使用随机数强制穷举,直到产生格式正确的迷你私钥

  4. 为了导出完整的私钥,用户只需要使用原始迷你私钥的单个SHA256哈希。这个过程是单向的:从派生密钥很难计算出迷你私钥格式。

由于与'l'的视觉相似性,许多实现不允许迷你私钥中使用字符'1'。

资源:创建和兑现这些密钥的常用工具是Casascius Bitcoin Address Utility

公钥格式

比特币ECDSA公钥表示在secp256k1中定义的特定椭圆曲线(EC)上的一个点。在其传统的未压缩形式中,公钥包含一个标识字节,一个32字节的X坐标和一个32字节的Y坐标。下面非常简化的图表显示了比特币在连续数字字段上使用的椭圆曲线y 2 = x 3 + 7上的这一点。

Point On ECDSA Curve

Secp256k1实际上是一个大素数的modulos坐标,尽管原理是相同的,但是它产生非连续整数的字段和明显不太清楚的图。

通过去除Y坐标,可以实现公钥大小几乎减少50%,而不会改变任何基本法则。这是可能的,因为沿曲线上只有两个点共享任何特定的X坐标,因此32位Y坐标可以用一个位来代替,表示点在图示中出现的是顶部还是底部。

创建这些压缩的公钥不会丢失任何数据 - 只需要少量的CPU来重建Y坐标和访问未压缩的公钥官方的secp256k1文档中描述了未压缩和压缩的公钥,并在广泛使用的OpenSSL库中默认支持。

因为他们易于使用,并减少了存储每次花费输出的将近一半的链空间,压缩公钥是Bitcoin Core默认的,并是所有比特币软件推荐使用的。

然而,Bitcoin Core在0.6之前使用未压缩的密钥。这造成一些麻烦,因为非压缩密钥的哈希格式不同于压缩密钥的哈希格式,所以同一个密钥要有两个不同的P2PKH地址。这也意味着密钥必须在签名脚本中以正确的格式提交,能匹配上一个先前输出的 pubkey脚本中的哈希。

因此,Bitcoin Core使用几个不同的标识符字节来帮助程序识别如何使用密钥:

  • 私钥意味着在被Base-58编码前使用添加了0x01的压缩公钥。(参见上面的私钥编码部分。)

  • 未压缩的公钥以0x04开头; 压缩的公开密钥以0x03或0x02开头,取决于它们是否大于或小于曲线的中点。这些前缀字节都在官方的secp256k1文档中定义使用。

分层确定性密钥创建

分层确定性密钥创建和传输协议(HD协议)极大地简化了钱包的备份,无需相同钱包的多个程序之间重复通信,允许创建可以独立运作的子帐户,即使子帐户遭到入侵,也能使每个父账户监控或控制其子帐户,并将每个账户划分为完全访问和受限访问部分,可以允许不受信任的用户或程序接收或监视付款但不能花费它们。

HD协议利用ECDSA公钥创建函数point(),其占用一个大整数(私钥),并将其转换为图形点(即公钥):

point(private_key) == public_key

以point()的工作方式,可以通过将现有的父公钥和另外一个从整数值(i)创建的公钥组合起来创建子公钥。这个子公钥与初始父私钥加上i值除去所有Bitcoin软件使用的全局常量p的产生的余数经过point()函数而创建的公钥相同。

point( (parent_private_key + i) % p ) == parent_public_key + point(i)

这意味着两个或多个独立的程序使用一致的整数序列可以从单个父键对创建一系列唯一的子键,而无需任何进一步的通信。此外,分发新公钥的程序可以不得到私钥而接收付款,这样允许公钥分发器工作在一个可能不安全的平台比如公共Web服务器。

子公钥还能通过重复子键推导操作来创建他们自己的子公钥(孙子公钥)。

point( (child_private_key + i) % p ) == child_public_key + point(i)

无论是创建子公钥还是进一步派生的公钥,可预测的整数序列将不如对所有交易使用单个公钥,因为知道一个子公钥的任何人可以找到从相同的父公钥创建的所有其他子公钥相反,可以使用随机种子来确定性地生成整数序列,使得子公钥之间的关系对于没有该种子的任何人都是不可见的。

HD协议使用单个根种子创建具有不可推导的整数值的子,孙和其他派生密钥。每个子密钥还可以从它的父密钥获取一个确定性产生的种子,称为链代码,所以一个链代码的损害不会影响整个层次的整数序列,允许主链代码即使例如基于Web的公钥分发器被hacked还能继续使用。

Overview Of Hierarchical Deterministic Key Derivation

如上所述,HD密钥导出需要四个输入:

  • 父私钥和父公钥是常规的未压缩256位ECDSA密钥。

  • 父链代码是256位的看似随机数据。

  • 索引号是程序指定的32位整数。

在上述图示中所示的正常格式中,将父链代码父公钥和索引号馈送到单向加密散列(HMAC-SHA512),以产生512位的确定性生成但似乎随机的数据。哈希输出右侧的看似随机的256位用作新的子链代码使用散列输出的左侧的看似随机的256位作为与父私钥父公共密钥组合的整数值分别创建子私钥子公钥

child_private_key == (parent_private_key + lefthand_hash_output) % G
child_public_key == point( (parent_private_key + lefthand_hash_output) % G )
child_public_key == point(child_private_key) == parent_public_key + point(lefthand_hash_output)

指定不同的索引号将从相同的父键创建不同的不可链接的子键使用子链代码重复子键的过程将创建不可链接的孙子键。

因为创建子键需要键和链代码,所以键和链代码一起被称为扩展键。扩展私钥及其相应的扩展公钥具有相同的链码(顶级父级)主密钥主链码从随机数据派生,如下所示。

Creating A Root Extended Key Pair

根据128位,256位或512位随机数据​​创建一个根种子这个少于128位的根种子是用户需要备份的唯一数据,以便使用特定设置来导出由特定钱包程序创建的每个密钥。

Warning icon 警告:在撰写本文时,HD钱包程序预计不会完全兼容,因此用户只能使用相同的HD钱包程序,具有与特定根种子相同的HD相关设置。

根种子被散列以创建512位的看似随机数据,从其中创建主专用密钥主链码 ,主扩展私钥)。使用point()主密钥派生主公钥,其中连同主链码,是主扩展公钥扩展键在功能上等同于其他扩展键;只有他们的位置在层次结构的顶部,使他们特别。

固化密钥

固化扩展密钥修复了普通扩展密钥的潜在问题。如果攻击者获得正常的父链接代码父公开密钥,他可以强制所有从其派生的链码如果攻击者也获得了一个子孙,孙子或进一步的私钥,他可以使用链码生成所有扩展的私钥从私钥下降,如下图所示的孙子孙子孙代。

Cross-Generational Key Compromise

也许更糟的是,攻击者可以反转正常的子私钥导出公式,并从子私钥中减去父链代码来恢复父私钥,如上图所示的子代和父代。这意味着从其中获取扩展的公钥和任何私钥的攻击者可以恢复公钥的 私钥和所有键从它下降。

因此,扩展公开密钥链码应该比标准的公钥更好地保护,并且应该建议用户不要导出甚至非扩展的私钥到可能不可信的环境。

这可以通过用强化的密钥导出公式替换正常密钥推导公式来进行一些折衷。

上述部分中描述的正常密钥导出公式将索引号父链代码父公开组合在一起,以创建子链代码和与父私钥组合的整数值来创建子私钥

Creating Child Public Keys From An Extended Private Key

如上所示的硬化公式将索引号父链代码父私钥组合在一起,以创建用于生成子链的数据代码子私钥该公式使得无法创建子公共密钥,而不知道父私钥换句话说,父扩展公钥不能创建硬化的子公共密钥

因此,固化扩展私钥比正常的扩展私钥或更无用,但是固化扩展私钥在多层密钥导出之间创建防火墙保证损害不能发生。因为硬化子扩展公钥不能自己生成孙子链码,父扩展公钥的妥协不能与妥协的孙子私钥来创建孙孙扩展私钥

HD协议使用不同的索引号来指示是否应生成正常或硬化的密钥。索引号从0x00到0x7fffffff(0到2 31 -1)将产生正常的键;索引号从0x80000000到0xffffffff将生成一个硬化密钥。为了简化说明,许多开发人员使用素数符号表示硬化密钥,因此第一个正常密钥(0x00)为0,第一个硬化密钥(0x80000000)为0。

(Bitcoin开发人员通常使用ASCII撇号而不是unicode主要符号,这是我们以后的惯例)

该紧凑描述进一步与由mM前缀的斜杠组合,以指示层次结构和键类型,其中m私钥M公钥例如,m / 0'/ 0/122'是指主私钥的第一个硬化子节点(按索引)的第一个正常子节点(按索引)的第123个硬化私有子节点(按索引号)。以下层次结构说明了主要符号和强化的关键防火墙。

Example HD Wallet Tree Using Prime Notation

遵循BIP32 HD协议的钱包只能创建主私钥m的固化子密钥,以防止受损的子键损害主键。由于主密钥没有正常的孩子,所以在HD钱包中不使用主公钥所有其他键可以有普通的孩子,所以可以使用相应的扩展的公钥

HD协议还描述了扩展公钥和扩展私钥的序列化格式。有关详细信息,请参阅开发者参考的钱包部分BIP32中完整的HD协议规范。

储存根种子

HD协议中的根种子是必须精确备份的128,256或512位随机数据。为了更方便地使用诸如存储或手工复制的非数字备份方法,BIP39定义了一种从伪伪建立512位根种子的方法普通自然语言词汇(助记符)本身是由128到256位的熵创建的,并且可选地受密码保护。

所产生的词数与所用熵的数量有关:

熵位 词数
128 12
160 15
192 18
224 21
256 24

密码短语可以是任何长度的。它只是附加到助记符伪句子,然后使用HMAC-SHA512将助记符和密码两次散列2,048次,从而产生一个看似随机的512位种子。因为散列函数的任何输入都会创建一个看似随机的512位种子,所以没有一个基本的方式来证明用户输入正确的密码,可能允许用户保护种子即使在胁迫下。

有关实现细节,请参阅BIP39

松散密钥钱包

也称为“Just a Bunch Of Keys(JBOK)”的Loose-Key钱包是来自Bitcoin Core客户端钱包的一种形式 。比特币核心客户端钱包将通过伪随机数生成器(PRNG)自动创建100 私钥 / 公钥,以供以后使用。

这些未使用的私钥存储在虚拟“密钥池”中,每当使用以前生成的密钥时,将生成新密钥,确保池保持100个未使用的密钥。(如果钱包被加密,则仅在钱包被解锁时才产生新密钥。)

考虑到备份必须手动运行以保存新生成的私钥,这在备份密钥方面造成了相当大的困难。如果在备份之前生成,使用,然后丢失新的密钥对集,则存储的satoshis可能永远丢失。许多旧式移动钱包遵循类似的格式,但只有根据用户需求生成新的私钥

由于备份麻烦,此钱包类型正在被积极淘汰,不鼓励使用。

支付流程

支付流程包括消费者和接收者为了交换产品或服务而做出和接受付款的步骤。从商业启蒙开始,基本步骤并没有改变,但技术在改变。本节将介绍接收者和消费者如何分别使用比特币来请求和付款,以及如何处理诸如退款循环付费等复杂情况。

Bitcoin Payment Processing

上图显示了从接收者的角度来看,使用比特币进行支付处理,从新订单开始。以下小节将分别介绍三个常见步骤和三个偶然或可选步骤。

值得一提的是,每个步骤都可以通过使用第三方API和服务进行外包。

价格订单

由于satoshis与本国货币(fiat)之间的汇率变动性,许多Bitcoin订单的价格为fiat,但在satoshis ,需要进行价格转换。

汇率数据可以通过货币兑换提供的广泛使用的基于HTTP的API得到。几个组织还聚合来自多个兑换机构的数据,以创建索引价格,也可以使用基于HTTP的API。

任何使用汇率数据自动计算订单总额的应用程序必须采取措施确保报价的价格反映了目前satoshis的一般市值,或者应用程序对于正在销售的产品或服务可以接受太少的satoshis或者,他们可以要求更多的satoshis,吓跑潜在的消费者。

为了确保问题最小化,您的应用程序可能希望从至少两个独立的源收集数据,并将其进行比较,以查看它们有多大差异。如果差异很大,您的应用程序可以进入安全模式,直到人们能够评估情况。

如果汇率正在迅速增加或下降,您可能还希望对您的应用程序进行编程,以进入安全模式,这表明在比特币市场可能会出现问题,这可能使得今天难以支付任何satoshis

汇率不在Bitcoin和相关技术的控制范围之内,因此没有新的或计划中的技术使您的程序更容易将订单总数从fiat转换为satoshis

由于汇率随时间而波动,与fiat挂钩的订单总额必须有过期时间,以防止消费者拖延付款来希望satoshis将降价。最广泛使用的支付处理系统目前在10到20分钟之后到期。

较短的到期期限会增加发票在收到付款之前到期的机会,可能需要手动干预以要求额外付款或发起退款。更长的到期期限增加汇款在收到付款之前显著波动的机会。

要求付款

在请求付款之前,您的应用程序必须创建比特币地址,或从另一个程序(如Bitcoin Core)获取地址比特币地址交易部分中有详细描述。在该部分中也描述了避免多次使用同一个地址的两个重要原因,但第三个原因特别适用于支付请求

对于每个收到的付款,使用一个单独的地址使得确定哪些客户已经支付了他们的支付请求是不重要的。您的应用程序只需跟踪特定的支付请求和其中使用的地址之间的关联,然后扫描块链以获得匹配的交易的地址

接下来的小节将详细描述以下四种兼容方式,以便提供给花费者地址并支付金额。为了提高方便性和兼容性,建议您在付款请求中提供所有这些选项。

  1. 所有钱包软件允许用户粘贴或手动输入地址,并将其纳入付款屏幕。这当然是不方便的 - 但是它有有效的回退选项。

  2. 几乎所有桌面钱包可以与比特币:URIs关联,所以消费者可以点击链接预先填写付款屏幕。这也适用于许多移动钱包,但是通常不支持基于Web的钱包,除非消费者安装浏览器扩展或手动配置URI处理程序。

  3. 大多数移动钱包支持以QR码编码的扫描比特币:URI,几乎所有钱包可以显示它们接受付款。虽然在线订单方便,QR码对于个人购买特别有用。

  4. 最近的钱包更新支持新的支付协议,提供更高的安全性,使用X.509证书对接收者的身份进行身份验证,以及其他重要功能例如退款

Warning icon 警告:必须特别注意避免收到的付款被盗。特别地,私钥不应存储在Web服务器上,支付请求应通过HTTPS或其他安全方法发送,以防止中间人攻击使用攻击者的地址替换比特币地址

纯文本

要直接指定用于复制和粘贴的金额,您必须提供地址,金额和面额也可以指定要约的到期时间。例如:

(注:本节中的所有示例都使用testnet 地址

Pay: mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN
Amount: 100 BTC
You must pay by: 2014-04-01 at 23:00 UTC

指示面额至关重要。在撰写本文时,流行的比特币钱包软件默认为比特币(BTC),毫比特币(mBTC)或微比特币(uBTC,“bits”)。广泛支持每个单位之间的选择,但是其他软件也可以让用户从以下部分或全部选项中选择面额:

比特币 单位(缩写)
1.0 比特币(BTC)
0.01 比特分(cBTC)
0.001 毫比特 (mBTC)
0.000001 微比特(uBTC,“bits”)
0.00000001

比特币:URI

BIP21中定义的比特币: URI方案消除了面额混淆,使消费者免于复制和粘贴两个单独的值。它还允许支付请求向消费者提供一些附加信息。一个例子:

bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=100

只有地址是必需的,如果它是唯一指定的,钱包将预先填写付款请求,然后让付款人输入金额指定的数量始终为十进制比特币(BTC)。

其他两个参数得到了广泛的支持。label参数通常用于将收款人的姓名提供给钱包软件。message参数通常用于描述花费者的支付请求标签和消息通常由销售商的钱包软件存储,但是它们不会添加到实际的交易中,因此其他Bitcoin用户看不到它们。标签和消息都必须是URI编码的

所有四个参数一起使用,具有适当的URI编码,可以在下面的换行示例中看到。

bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
?amount=0.10\
&label=Example+Merchant\
&message=Order+of+flowers+%26+chocolates

可以扩展URI方案,如下面的支付协议部分所示,同时包含新的可选参数和必需的参数。在撰写本文时,除了上述四个之外,唯一广泛使用的参数是支付协议 r参数。

接受任何形式的URI的程序必须在付款之前要求用户获得许可,除非用户明确禁用了提示(对于支付小额支付可能是这样)。

QR码

QR码是个人,图像或视频中交换比特币:URIs的流行方式。大多数移动Bitcoin 钱包应用程序和一些桌面钱包,支持扫描QR码预先填写他们的付款屏幕。

下图显示了在四个不同的纠错水平下,编码为四种不同的比特币QR码的相同的比特币:URIQR码可以包括labelmessage参数和任何其他可选参数 - 但是在这里省略了可选参数以便使QR码小,易于不稳定或低分辨率的移动摄像头扫描。

Bitcoin QR Codes

错误校正与校验和相结合,以确保比特币QR码在数据丢失或意外更改时无法成功解码,因此您的应用程序应根据您可用的空间选择适当的纠错级别显示QR码。当空间有限时,低级别的损坏修正功能很好,四分位数的损坏修正有助于确保在高分辨率屏幕上显示时快速扫描。

付款协议

Bitcoin Core 0.9支持新的支付协议付款协议付款请求添加了许多重要功能:

  • 支持X.509证书和SSL加密,以验证接收者的身份,并帮助防止中间人攻击。

  • 提供有关所需支付的更多细节给消费者。

  • 允许消费者直接向接收者提交交易,而无需通过P2P网络这样可以加快付款处理速度,并使用计划功能(如child-pays-for-parent交易费用)和离线NFC或基于蓝牙的付款。

诸如“mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN”的无意义的地址而不被要求,但要求消费者提供从接收方的X.509证书中支付公用名称(CN)描述,如“www.bitcoin.org”。

要使用支付协议请求付款,您可以使用扩展(但向后兼容)比特币: URI例如:

bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
?amount=0.10\
&label=Example+Merchant\
&message=Order+of+flowers+%26+chocolates\
&r=https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN

除了r之外,上述提供的参数都不是支付协议所必需的,但您的应用程序可能会包含它们与钱包程序的向后兼容性其尚未处理支付协议

r参数指示支付协议感知钱包程序忽略其他参数,并从提供的URL中获取一个PaymentRequest浏览器,QR码阅读器或处理URI的其他程序打开URI上的花费者Bitcoin 钱包程序。

BIP70 Payment Protocol

BIP70BIP71BIP72中深入描述了付款协议开发者示例付款协议子节中提供了一个示例CGI程序和可在付款协议中使用的所有参数的描述。在本小节中,我们将以故事形式简要介绍如何通常使用付款协议

Charlie客户,正在购买由Bob商人运营的网站。查理在购物车中添加了几个物品,并点击了“用比特币结帐”按钮。

Bob的服务器会自动将以下信息添加到其发票数​​据库中:

  • 查理订单的细节,包括订购和运送的商品地址

  • satoshis中的订单总数,可能是通过将fiat中的价格转换为satoshis中的价格而创建的。

  • 该总计将不再可接受的到期时间。

  • Charlie应向其发送付款的pubkey脚本通常这将是一个包含一个唯一的(从未使用过的)secp256k1公钥的P2PKH或者P2SH pubkey脚本

在将所有信息添加到数据库之后,Bob的服务器显示Charlie的比特币: URI以点击付款。

查理点击浏览器中的比特币:URI他的浏览器的URI处理程序将URI发送到他的钱包程序。钱包知道付款协议,因此它解析了r参数,并向该URL发送一个HTTP GET,查找一个PaymentRequest消息。

返回的PaymentRequest消息可能包括私人信息,例如Charlie的邮件地址,但是钱包必须能够访问它而不使用先前的身份验证,例如HTTP Cookie,因此通常使用具有防猜测部件的可公开访问的HTTPS URL。支付请求创建的唯一公钥可用于创建唯一标识符。这就是为什么在上面的示例URI中,PaymentRequest URL包含P2PKH地址https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN

在收到HTTP GET到上面的URL之后,Bob的Web服务器上的PaymentRequest生成CGI程序从URL获取唯一的标识符,并在数据库中查找相应的详细信息。然后,使用以下信息创建一个PaymentDetails消息:

  • satoshispubkey脚本中的订单金额将被支付。

  • 一个包含订购商品列表的备忘录,所以查理知道他付的是什么。它也可能包括查理邮寄地址,所以他可以仔细检查。

  • PaymentDetails消息的创建时间加上它到期的时间。

  • Charlie的钱包应发送完成的交易的网址。

PaymentDetails消息放在PaymentRequest消息中。支付请求允许Bob的服务器使用服务器的X.509 SSL证书签署整个请求。付款协议旨在允许将来允许其他签名方式。)Bob的服务器在向HTTP GET的回复中发送付款请求到Charlie的钱包

Bitcoin Core Showing Validated Payment Request

Charlie的钱包收到PaymentRequest消息,检查其签名,然后从PaymentDetails消息中显示详细信息给Charlie 。Charlie同意支付,所以钱包构造了一个付款给pubkey脚本提供的Bob的服务器。与传统的比特币支付不同,Charlie的钱包不一定会自动将此付款广播到网络相反,钱包构造一个付款消息,并将其作为HTTP POST发送到PaymentDetails消息中提供的URL。其中包括:付款信息包含:

  • Charlie支付Bob的签名交易。

  • 可选备忘录Charlie可以发送给Bob。(不能保证Bob会读它。)

  • 如果他需要返回Charlie的satoshis中的一些或全部,那么Bob可以支付的退款 地址pubkey脚本) 。

Bob的服务器接收到付款消息,验证交易是否向所提供的地址支付请求的金额,然后将交易广播到网络它还使用PaymentACK消息回复HTTP POSTed Payment消息,其中包括Bob的服务器的可选备忘录,感谢Charlie的赞助,并提供有关订单的其他信息,如预计到达日期。

Charlie的钱包看到了PaymentACK,并通知Charlie付款已发送。PaymentACK并不意味着Bob已经验证了Charlie的付款 - 请参阅下面的验证付款部分,但这意味着Charlie可以在交易获得确认时执行其他操作。在Bob的服务器从块链中验证Charlie的事务已经被确认之后,它授权运送Charlie的订单。

在出现争议的情况下,Charlie可以从各种签名或其他证明的信息中生成经密码证明的收据

如果需要发出退款,Bob的服务器可以安全地支付Charlie提供的退款 -to pubkey脚本有关详细信息,请参阅下面的退款部分。

验证付款

交易块链部分所述,将交易广播到网络不能确保接收方得到报酬。一个恶意的支出者可以创建一个支付收款人的交易,另一个交易支付相同的输入回到自己。只有其中一个交易将被添加到块链,没有人可以肯定地说明它将是哪一个。

花费相同输入的两个或多个交易通常被称为双重支出

一旦交易被包含在中,双重支出是不可能的,而不修改块链历史来替换交易,这是相当困难的。使用该系统,Bitcoin协议可以根据需要修改以替换交易的的数量,为每个交易提供更新置信度。对于每个,交易获得一个确认由于修改是相当困难的,所以较高的确认得分表示更大的保护。

0 确认:交易已被广播,但仍未包含在任何中。一般情况下,零价值确认的交易(未经确认的交易)一律不受信任。虽然矿工通常确认他们收到的第一个交易,但欺诈者可能可以操纵网络来包括其交易版本。

1 确认:交易被包含在最新的中,双重花费风险急剧下降。支付足够的交易费用的交易平均需要10分钟才能收到确认然而,最近的被意外地更换,所以双重支出仍然是一个真正的可能性。

2 确认:最近的链接到包含交易的截至2014年3月,两个替换是非常罕见的,并且两个替换攻击是不切实际的,没有昂贵的采矿设备。

6 确认网络花了大约一个小时的时间来保护交易,避免了双重支出,交易是埋在六块之下。即使是一个相当幸运的攻击者也需要大量的网络哈希功率代替六个虽然这个数字有些随意,但处理高价值交易的软件或以其他方式具有欺诈风险的软件,在等待接受付款之前,应至少等待六次确认

Bitcoin Core提供了几个RPC,可以为您的程序提供钱包中的交易或任意交易的确认分数例如,listunspent RPC提供了您可以与确认分数一起使用的每个satoshi的数组。

尽管确认大部分时间都提供了优秀的双重支出保护,但至少有三种情况需要双重支出风险分析:

  1. 在程序或其用户无法等待确认并希望接受未确认付款的情况下。

  2. 在程序或其用户接受高价值交易并且不能等待至少六个确认或更多的情况下。

  3. 在实施bug或长时间攻击Bitcoin的情况下,使系统的可靠性低于预期。

双重花费风险分析的有趣来源可以通过连接大量的比特币对等体获取,以跟踪交易和与每个其他。一些第三方API可以为您提供这种类型的服务。

例如,可以在所有连接的节点中比较未确认的交易,以查看在多个未确认的交易中是否使用任何UTXO,表示双重花费尝试,在这种情况下,支付可以拒绝,直到确认交易也可以通过他们的交易费用进行排名,以估计它们添加到之前的时间。

另一个例子可能是当多个对等体在相同块高度之间报告不同的块头散列时,检测fork如果fork扩展了两个以上的,则程序可以进入安全模式,表示块链可能出现问题。有关详细信息,请参阅检测叉子小节

双重支持保护的另一个好的来源可以是人类智慧。例如,欺诈者可能与合法客户的行为不同,让精明的商家手动将其标记为高风险。您的程序可以提供安全模式,可以在全球或每个客户的基础上停止自动付款验收。

发起退款

偶尔使用您的应用程序的接收者将需要发出退款这样做非常不安全的明显方法是将satoshis返回到他们来的pubkey脚本例如:

这似乎应该起作用,但是Alice正在使用一个集中的多用户网络钱包,它不向每个用户提供唯一地址,所以没有办法知道Bob的退款适用于Alice。现在,退款是对集中的钱包背后的公司的无意捐赠,除非Alice打开支持票,并证明那些satoshis是为她。

这使得接收方只有两种正确的方式来发布退款

注意:如果退款在原始付款完成后很长时间才会直接与投寄人联系是明智的。这允许您确保用户仍然可以访问refund_to 地址的键或键。

支付收入(限制外汇风险)

许多接收者担心,他们的satoshis将在未来比现在更少的价值,称为外汇(外汇)风险。为了限制外汇风险,许多接收方选择在收到新的收款后立即支付新收购的付款。

如果您的应用程序提供此业务逻辑,则需要首先选择输出有几种不同的算法可以导致不同的结果。

  • 一个合并回避算法使得外部人员看到块链数据变得更难以确定接收方获得,花费和保存了多少satoshis

  • 最先进先出(LIFO)算法花费新获得的satoshis,而仍然有双重支出风险,可能会将风险推向别人。这可能对接收者的资产负债表有好处,但可能对他们的声誉不利。

  • 先入先出(FIFO)算法首先使用最早的satoshis,这有助于确保接收者的付款总是确认,尽管这只能在几个边缘情况下实现。

合并回避

当接收器在输出中接收到satoshis时,花费者可以跟踪(以粗略的方式)接收机如何花费那些satoshis但是,只要接收方为每个事务使用唯一地址,花费者就不能自动查看其他消费者向接收方支付的其他satoshis

然而,如果接收方在同一个交易中从两个不同的消费者那里花费了satoshis,则这些消费者中的每一个都可以看到另一个付款人的付款。这被称为合并,接收器合并输出越多,外部人员越容易跟踪接收机具有多少satoshis赚取,消耗和保存。

合并回避表示试图避免在相同的事务中花费不相关的输出对于想要将交易数据保密的人和企业,可能是一个重要的策略。

粗略的合并回避策略是尝试始终以最小的输出来支付您所要求的数量。例如,如果您有四个输出保持,分别为100,200,500和900 satoshis,则您将支付300 satoshis t2 > 500- satoshi 输出这样,只要您的输出比您的帐单大,您可以避免合并。

更高级的合并回避策略在很大程度上取决于支付协议的增强,这将允许付款人通过智能地在多个输出之间分配付款来合并由接收者。

后进先出(LIFO)

输出即使在确认之前也可以一次收到。由于最近的输出双重的最大风险,将它们花在较旧的输出之前,可以让花费者保持较旧的 t> 输出,这些不太可能是双重

LIFO有两个紧密相关的缺点:

在上述任何一种情况下,第二个事务的接收者将看到传入的事务通知消失或变成错误消息。

由于LIFO将次要交易的收件人作为主要交易的收件人,将双重支出风险作为收款人,因此在次级收件人不关心风险时最佳使用,例如交换或其他将要等待六个确认的服务,无论您是否使用旧的输出或新的输出

主要交易受益人的声誉可能受到威胁,例如支付员工时,不应使用LIFO。在这种情况下,最好在使用这些交易进行付款之前等待交易完全验证(参见上述验证子)。

先进先出(FIFO)

最老的输出是最可靠的,因为它们被接收到的时间越长,越多需要修改为双重支出他们。然而,在仅仅几个之后,达到快速递减的回报点。假设攻击者拥有总共网络中的30%哈希功率,则原始的比特币纸张预测攻击者能够修改旧的的机会:

区块 成功修改的机会
5 17.73523%
10 4.16605%
15 1.01008%
20 0.24804%
25 0.06132%
30 0.01522%
35 0.00379%
40 0.00095%
45 0.00024%
50 0.00006%

交易费用时,FIFO确实具有很小的优势,因为较旧的输出可能有资格包含在50,000字节中,用于不需要的运行默认Bitcoin Core代码库的矿工的高优先级事务。但是,由于交易费用如此之低,这并不是一个显着的优势。

FIFO的唯一实际用途是接收者在几个中花费所有或大部分收入,并希望减少他们的付款偶然无效的机会。例如,持有六个确认的每笔付款的接收方,然后以每两小时的时间表向供应商和储蓄账户支付100%的经过验证的付款

重新定期付款

使用分散的比特币不可能实现自动定期付款。即使钱包支持自动发送不可逆付款,用户仍然需要在指定的时间启动程序,或者一直运行不受加密保护。

这意味着自动重复的比特币付款只能由代表其消费者处理satoshis的集中式服务器进行。实际上,希望在法定价格条件下设定价格的接收方也必须让同一集中式服务器选择合适的汇率。

非自动重新结算可以通过信用卡经常性付款变得普遍使用的相同机制来管理:联系付款人并要求他们再次付款 - 例如,通过发送一个PaymentRequest bitcoin: URI

在将来,支付协议和新的钱包功能的扩展可能允许一些钱包程序来管理定期交易的列表。花费者仍然需要定期启动该计划并授权付款 - 但是,通过点击电子邮件发票,可以更容易和更安全地支付费用,从而增加接收方按时获得付款的机会。

操作模式

目前客户端验证块链主要有两种方法:全节点SPV客户端不讨论其他方法,如服务器信任方法,因为它们不被推荐。

全节点

第一个也是最安全的模式是Bitcoin Core,也被称为“厚”或“全链”客户端。该安全模型通过从创世块开始下载并验证一直到最近发现的块来保证块链的有效性。这被称为使用特定高度来验证客户端对网络的视图。

去欺骗一个客户端,对手需要提供一个比当前真实的链更难的完整的替代性的区块链历史记录,这在计算上是昂贵的,因为最具累积工作量的链被定义为是真实的链。由于在链的尖端生成新的块所需的计算难度,欺骗一个完整节点想获得确认非常困难。这种形式的验证对于sybil攻击是高度抵制的 - 只需要一个诚实的网络 节点,以便接收和验证真实块链的完整状态。

Block Height Compared To Block Depth

简化付款验证(SPV)

在原始比特币白皮书里面详述的另外一种方式是在初始同步过程中只下载块头,当需要时候再从完整节点获取交易信息。块链高度线性匹配,每个块头只有80个字节,或每年就4.2MB,而不管的总大小。

如白皮书所述,块头中的merkle跟以及merkle分支可以向SPV客户端验证嵌入在区块链中的一个区块的问题交易。这并不能保证所嵌入的交易的有效性。相反,它展示了执行重复花费攻击所需的工作量。

块链中的深度对应于在特定块之上构建的累积难度SPV客户端知道merkle根和关联的交易信息,并从完整节点请求相应的merkle分支。一旦检索到merkle分支,证明中的交易的存在,则SPV客户端可以查看 深度作为交易有效性和安全性的代理。插入无效交易的恶意节点对用户的攻击成本与构建在该之上的累积难度增长,因为恶意节点需要单独将是挖掘这个伪造的链。

潜在的SPV弱点

如果简单地实现,SPV客户端有一些严重缺陷。

首先,虽然SPV客户端不容易被欺骗,认为交易处于中,但不是这样,反之亦然。完整节点可以简单地忽略,导致SPV客户端认为没有发生事务。这可以被认为是拒绝服务的一种形式。一个缓解策略是连接到一些完整的节点,并将请求发送到每个节点然而,这可以被网络分区或Sybil攻击所击败,因为身份基本上是免费的,并且可能是带宽密集型的。必须注意确保客户端不会与诚实的节点切断连接。

其次,SPV客户端仅从与其拥有的密钥相对应的完整节点请求交易。如果SPV客户端下载所有,然后丢弃不需要的块,这可能会带来极大的带宽。如果他们只是通过特定的事务向块请求完整的节点,那么这样就可以获得完整的节点公共地址 t3 >对应于用户。这是一个很大的隐私泄漏,并允许客户端,用户或地址的服务拒绝服务的策略,这些操作不满足运行完整的节点,以及琐碎的连结资金。客户端可以简单地垃圾邮件发送许多假事务请求,但这在SPV客户端上造成了很大的压力,最终可能会破坏瘦客户端的目的。

为了减轻后一个问题,已经实现了Bloom filter作为一种混淆和压缩数据请求的方法。

布鲁姆过滤器

布鲁姆过滤器是用于测试元素成员资格的空间高效概率数据结构。数据结构以牺牲规定的假阳性率为代价实现了巨大的数据压缩。

一个Bloom filter作为一个n位的数组全部设置为0。选择一组k个随机散列函数,其中每个函数输出1和n范围之间的单个整数。

Bloom filter中添加元素时,元素将分别进行哈希散列,而对于每个k个输出,该索引处的相应的Bloom filter位将被设置为1。

通过使用与之前相同的散列函数来完成Bloom过滤器的查询。如果在bloom filter中访问的所有k个比特被设置为1,则这很可能表明该元素位于该集合中。显然,通过在域中添加其他元素的组合,可以将k个索引设置为1,但参数允许用户选择可接受的假阳性率。

删除元素只能通过废除bloom filter并从头重新创建来完成。

布鲁姆过滤器的应用

而不是将错误的阳性率视为责任,它用于创建一个可调参数,表示所需的隐私级别和带宽权衡。SPV客户端创建他们的布鲁姆过滤器,并使用filterload消息将其发送到完整节点,该消息为需要的交易设置过滤。命令filteradd允许向筛选器添加所需的数据,而不需要发送一个全新的布隆过滤器,而filterclear允许连接恢复为标准发现机制。如果滤波器已经被加载,那么完整的节点将发送一个的修改形式,称为merkle块merkle块只是块头和其中与设置 Bloom filter相关的merkle分支。

SPV客户端不仅可以将交易作为元素添加到过滤器,还可以将公钥签名脚本pubkey脚本等等。这使得P2SH事务查找成为可能。

如果用户更加隐私意识,他可以将布鲁姆过滤器设置为包含更多的误报,牺牲了用于事务发现的额外带宽。如果用户的带宽预算紧张,他可以将假阳性率设置为低,知道这将允许完整的节点清楚了解与他的客户端相关的交易。

资源: BitcoinJ是基于SPV安全模型和布鲁姆过滤器的比特币的Java实现。用于大多数Android 钱包

Bloom filter通过BIP37进行标准化。查看BIP的实现细节。

未来建议

在区块链中有未来的建议,例如未花费交易输出UTXO承诺,为需要的客户找到更满意的中间地带块链的完整副本,或相信大多数连接的节点不在说谎。UTXO承诺将使使用块链中认证的数据结构的非常安全的客户端使用有限量的存储。然而,这些类型的提议是在非常早的阶段,并且将在网络中产生软分叉

在实施这些类型的操作模式之前,应该根据可能的威胁模型,计算和带宽约束以及比特币值的责任来选择模式。

资源: UTXO承诺上的原始线程经过身份验证的前缀树BIP提案

P2P网络

Bitcoin网络协议允许全节点(peers) 共同维护一个进行区块和交易数据交换的点对点的网络。完整节点下载并验证每个和事务,然后再将它们中继到其他节点存档节点是满节点,其存储整个块链,并且可以将历史提供给其他节点修剪节点是不存储整个块链的满节点许多SPV客户端也使用比特币网络协议连接到完整的节点

共识规则不涵盖网络,所以Bitcoin程序可能使用替代网络和协议,例如一些矿工使用的高速块中继网络专用交易信息服务器使用的钱包提供SPV级安全性。

为了提供Bitcoin 对等网络的实际示例,本节使用比特币核心作为代表性的完整节点BitcoinJ作为代表SPV客户端这两个程序都是灵活的,所以只描述默认行为。另外,为了隐私,下面的示例输出中的实际IP地址已被替换为RFC5737保留的IP地址。

同伴发现

当第一次启动时,程序不知道任何活动的完整节点的IP地址为了发现一些IP地址,他们查询一个或多个硬编码到Bitcoin Core和BitcoinJ的DNS名称(称为DNS种子)。对查找的响应应包括可能接受新的传入连接的一个或多个具有完整节点的IP地址的一个或多个DNS A记录例如,使用Unix dig命令

;; QUESTION SECTION:
;seed.bitcoin.sipa.be.	    IN  A

;; ANSWER SECTION:
seed.bitcoin.sipa.be.	60  IN  A  192.0.2.113
seed.bitcoin.sipa.be.	60  IN  A  198.51.100.231
seed.bitcoin.sipa.be.	60  IN  A  203.0.113.183
[...]

Bitotin社区成员维护DNS种子:其中一些提供动态DNS种子服务器,通过扫描,自动获取活动的节点的IP地址 网络;其他人提供手动更新的静态DNS种子,更有可能为非活动的节点提供IP地址在任何一种情况下,如果节点被添加到DNS种子,如果它们运行在默认Bitcoin端口为8333,对于mainnet或18333为testnet

DNS种子结果未通过身份验证,恶意种子运营商或网络 中间人攻击者只能返回,隔离攻击者自己的网络上的程序,并允许攻击者提供虚假事务和因此,程序不应仅仅依靠DNS种子

一旦程序连接到网络,它的对等体可以开始发送addr(地址)消息,IP地址和端口号为网络上的其他对等体,提供了一个完全分散的对等体发现方法。Bitcoin Core在持久的磁盘数据库中保留已知的对等体的记录,通常允许它在后续启动时直接连接到对等体,而不必使用DNS种子。

然而,对等体通常离开网络或更改IP地址,因此程序可能需要在启动成功连接之前启动几个不同的连接尝试。这可能会增加连接到网络所需的时间量,从而迫使用户在发送交易或检查付款状态之前等待。

为了避免这种可能的延迟,BitcoinJ总是使用动态DNS种子来获取被认为是当前活动的节点的的IP地址。Bitcoin Core还尝试在最小化延迟之间取得平衡,并避免不必要的DNS种子使用:如果Bitcoin Core在其对等体数据库中具有条目,则尝试最多11秒连接至少一个,然后倒回种子;如果在该时间内建立连接,则不会查询任何种子。

Bitcoin Core和BitcoinJ也包括一个硬编码的IP地址和端口号的列表,到几十个节点,这些节点在特定版本的软件首次发布的时候是活跃的。如果没有一个DNS种子服务器在60秒内响应查询,Bitcoin Core将开始尝试连接到这些节点,提供自动后备选项。

作为手动后备选项,Bitcoin Core还提供了几种命令行连接选项,包括通过IP地址从特定的节点获取对等体的列表的功能,或者通过IP地址持久连接到特定的节点有关详细信息,请参阅-help文本。BitcoinJ可以编程做同样的事情。

资源: 比特币播种器,程序由Bitcoin Core和BitcoinJ使用的几个种子运行。Bitcoin Core DNS种子策略Bitcoin Core和BitcoinJ使用的IP地址的硬编码列表使用makeseeds脚本生成。

连接到同伴

通过发送包含你的版本号,区块和当前时间的version消息给远处节点来连接同伴远程节点使用自己的版本消息进行响应。然后,节点向另一个节点发送verack消息以指示连接已建立。

一旦连接,客户端可以发送到远程节点 getaddraddr消息来收集其他对等体 T4>。

为了保持与同伴的连接,默认情况下,节点将在30分钟不活动之前向对等体发送消息。如果90分钟没有通过对等体接收到消息,客户端将假定该连接已关闭。

初始块下载

完整节点可以验证未确认的事务和最近开始的之前,必须下载并验证所有 1(在硬编码生成块之后的)到最佳块链的当前尖端。这是初始块下载IBD)或初始同步。

虽然“初始”一词意味着该方法只使用一次,但也可以在需要下载大量的任何时候使用,例如,当先前追加节点已经离线了很长时间。在这种情况下,节点可以使用IBD方法下载自上次上线以来生成的所有

Bitcoin Core使用IBD方法,只要其本地最佳块链上的最后一个块头时间过去24小时以上。比特币核心0.10.0还将执行​​ IBD,如果其本地最佳块链大于144 低于它的本地最佳头链(即,本地块链过去超过约24小时)。

块优先

比特币核心(直到版本0.9.3)使用简单的初始块下载IBD)方法,我们称之为blocks-first目的是从最佳块链顺序下载

Overview Of Blocks-First Method

第一次启动节点时,它的本地最佳块链中仅具有单个 - 硬编码的创世块块0)。节点选择一个称为同步节点的远程对等体 t>,并发送getblocks消息如下所示。

First GetBlocks Message Sent During IBD

getblocks消息散列字段中,这个新的节点发送 hash内部字节顺序中的6fe2 ... 0000)。它还将停止哈希字段设置为全零,以请求最大大小的响应。

getblocks消息收据之后,同步节点取第一个(且仅)哈希,并使用标题哈希查找其的本地最佳块链它发现块0匹配,所以它回复500 库存(对getblocks的最大响应 message)从 1开始。它将这些库存发送到下面所示的inv消息

First Inv Message Sent During IBD

库存网络上的信息的唯一标识符。每个库存包含一个类型字段和对象实例的唯一标识符。对于,唯一标识符是块的 的散列。

库存出现在inv消息中,与块链,所以第一个inv消息包含 1到501的库存(例如,如上图所示, 1的散列为4860 ... 0000)

IBD 节点使用接收的库存 t>从sync 节点请求128 在下面说明的getdata消息中。

First GetData Message Sent During IBD

块首先 节点重要的是请求并发送,因为每个块头引用这意味着IBD 节点无法完全验证,直到其父已被接收为止。阻止由于父节点未被接收而无法验证,称为孤儿块;下面的小节更详细地描述它们。

getdata消息收据之后,同步节点回复每个块请求。每个被放入串行化块格式,并以单独的消息发送。发送的第一个消息(对于 1)如下所示。

First Block Message Sent During IBD

IBD 节点下载每个,验证它,然后请求尚未请求的下一个 ,保持高达128 的队列下载。当它请求了它具有库存的每个时,它向另一个getblocks消息发送到同步节点请求最多500个库存第二个getblocks消息包含多个哈希,如下所示:

Second GetBlocks Message Sent During IBD

收到第二个getblocks消息之后,同步节点搜索其本地的最佳块链对于,匹配消息中的散列之一,按照接收的顺序尝试每个哈希。如果找到匹配的散列,则从那个点开始,以开头的500 库存进行回复。但是如果没有匹配的散列(除了停止散列)之外,它假设只有,两个节点有共同的是块0,所以它从 1(相同的inv消息开始 t>发送inv,见上面的几个插图)。

即使IBD 节点的本地块链,该重复搜索也允许sync 节点发送有用的库存节点的本地块链分支。IBD 节点越来越接近块链的尖端,这种fork检测变得越来越有用。

IBD 节点收到第二个inv消息时,它将请求那些使用getdata消息同步节点将响应消息然后,IBD 节点将使用另一个getblocks消息请求更多的库存循环将重复,直到IBD 节点同步到块链的尖端。此时,节点将接受通过稍后小节中描述的常规广播发送的

块优先的优点和缺点

block-first IBD的主要优点是其简单性。主要缺点是IBD 节点依赖于单个同步节点进行下载。这有几个含义:

  • 速度限制:所有请求都发送到同步节点,所以如果同步节点的上传带宽有限,则 节点将具有较慢的下载速度。注意:如果同步节点脱机,Bitcoin Core将继续从另一个节点下载,但它仍然只能从单个同步节点一次

  • 下载重新启动:同步节点可以向IBD发送非最佳(但有效的)块链 节点。IBD 节点将无法将其识别为非最佳状态,直到初始块下载接近完成,迫使IBD 节点从不同的节点再次重新启动块链下载。Bitcoin Core在开发人员选择的各种块高度中附带了多个块链检查点,以帮助IBD 节点检测到它正在馈送一个替代的块链历史,允许IBD 节点在该过程的前面重新启动它的下载。

  • 磁盘填充攻击:与下载重新启动密切相关,如果同步节点发送非最佳(但有效)的块链,则链将被存储在磁盘上,浪费空间,并可能用无用数据填充磁盘驱动器。

  • 高内存使用:无论是恶意还是意外,同步节点可以发送,从而创建孤儿块,在父母被接受和验证之前无法验证。孤立块存储在内存中,同时等待验证,这可能导致高内存使用。

所有这些问题部分或全部由Bitcoin Core 0.10.0中使用的头 - 第一个IBD方法解决。

资源:下表总结了本小节提及的消息。消息字段中的链接将带您进入该消息的参考页面。

Message getblocks inv getdata block
From→To IBD→Sync Sync→IBD IBD→Sync Sync→IBD
有效载荷 一个或多个散列 最多500 库存(唯一标识符) 一个或多个 库存 一个序列化块

块头优先

Bitcoin Core 0.10.0使用名为headers-first初始块下载IBD)方法。目标是下载最佳头链标头,尽可能地最大限度地验证它们,然后并行下载相应的这解决了旧的block-first IBD方法的几个问题。

Overview Of Headers-First Method

第一次启动节点时,它的本地最佳块链中仅具有单个 - 硬编码的创世块块0)。节点选择一个远程对等体,我们称之为节点,并发送getheaders 消息如下所示。

First getheaders message

getheaders消息散列字段中,新的节点发送 创世块内部字节顺序中的6fe2 ... 0000)。它还将停止哈希字段设置为全零,以请求最大大小的响应。

getheaders消息收据之后,同步节点取第一个(且仅)哈希,并使用标题哈希查找其的本地最佳块链它发现块0匹配,所以它从 1开始的2000 (最大响应)回复。它将这些标题散列在下面所示的标题消息中。

First headers message

IBD 节点可以通过确保所有字段遵循共识规则来部分地验证这些块标题,并且根据nBits字段,低于目标阈值(完全验证仍然需要相应的的所有事务。)

IBD 节点部分验证了块头之后,它可以并行执行两件事:

  1. 下载更多标头 IBD 节点可以发送另一个getheaders消息到同步节点,以请求最佳头链上的下一个2,000 那些可以立即验证,另一个批次重复请求,直到从同步节点收到消息少于2,000个,表示它不再提供在撰写本文时,标题同步可以在少于200次往返,或约32 MB的下载数据中完成。

    一旦IBD 节点收到一个消息,小于2,000 同步节点,它向每个出站对等体发送getheaders消息,以获得最佳头链通过比较响应,可以很容易地确定它已经下载的是否属于其任何出站对等体报告的最佳头链这意味着即使不使用检查点,也将很快发现不诚实的同步节点(只要IBD 节点连接至少一个诚实的对等体;比特币核心将继续提供检查点,以防无法找到诚实的对等体

  2. 下载IBD 节点继续下载 完成下载,IBD 节点将请求并下载每个IBD 节点可以使用从头链计算出的块头来创建消息请求需要库存它不需要从同步节点请求它们 - 可以从任何完整节点 对等体请求它们。(尽管不是全部节点可能存储所有。)这允许它并行获取,并避免将其下载速度限制为单个同步节点的上传速度。

    为了在多个对等体之间传播负载,Bitcoin Core将一次只能从单个对等体请求最多16个结合最多8个出站连接,这意味着headers-first BitCol Core将在IBD期间同时请求最多128个(相同比特币核心从其同步节点请求的最大数字块首先

Simulated Headers-First Download Window

Bitcoin Core的头 - 第一个模式使用1,024- 移动下载窗口来最大限度地提高下载速度。窗口中最低的高度 是待验证的下一个如果在比特币核心准备好验证它之后,尚未到达,Bitcoin Core将等待至少两秒以上,停止节点发送如果尚未到达,Bitcoin Core将与停止的节点断开连接,并尝试连接到另一个节点例如,在上述图示中,如果Node A在至少两秒钟内不发送 3将断开连接。

一旦将IBD 节点同步到块链的末尾,它将接受通过常规发送的 block在后面的小节中描述的广播。

资源:下表总结了本小节提及的消息。消息字段中的链接将带您进入该消息的参考页面。

Message getheaders headers getdata block
From→To IBD→Sync Sync→IBD IBDMany ManyIBD
有效载荷 一个或多个散列 最多2,000个块头 散列的一个或多个 库存 一个序列化块

块广播

矿工发现新的时,使用以下方法之一向对等体广播新的

默认情况下,Bitcoin Core使用标准块中继广播,但它将接受使用上述方法发送的

完整节点使用标准块中继方法验证所接收的,然后将其通告给它们的对等体下面的汇总表突出显示了上述消息的操作(Relay,BF,HF和SPV是指继电器节点,一个节点首标 节点SPV客户端任何使用任何检索方法节点

Message inv getdata getheaders headers
From→To Relay→Any BF→Relay HF→Relay Relay→HF
有效载荷 库存 库存 HF 节点的 最佳头链(BHC)上的一个或多个散列 将HF 节点的 BHC连接到继电器节点的 BHC的最多2,000个
消息 block merkleblock tx  
From→To Relay→BF/HF Relay→SPV Relay→SPV  
有效载荷 序列化格式中的新 新的过滤成merkle块 bloom filter匹配的新的中的序列化事务  

孤儿块

块优先 节点可以下载 - ,其前一个块头散列字段是指块头 节点尚未看到。换句话说,孤儿块没有已知的父(不同于陈旧块,它们已知父,但不属于最佳块链)。

Difference Between Orphan And Stale Blocks

block-first 节点下载孤儿块时,它不会对其进行验证。而是将getblocks消息发送到发送孤儿块节点;广播节点将使用包含任何库存inv消息缺少节点(最多500个);下载节点将使用getdata消息请求;并且广播节点将使用消息发送那些下载节点将验证那些,一旦前一个孤岛块的父节点被验证,它将验证前一个孤儿块

首标 节点通过总是使用getheaders消息请求块头,然后使用getdata消息请求广播节点将发送包含所有块头(最多2,000个)的消息,认为下载节点需要到达最佳头链的尖端;每个将指向其父节点,因此当下载节点收到消息时,不应该是孤儿块 - 所有的父节点应该是已知的(即使它们尚未被验证)。尽管如此,在消息中收到的孤立块,一个头-first 节点将立即丢弃。

然而,孤儿丢弃确实意味着首部 节点将忽略中的矿工发送的孤儿块 未经请求的块推

交易广播

为了将事务发送到对等体,发送inv消息如果接收到getdata响应消息,则使用tx发送事务。接收此事务的对等体也以相同的方式转发该事务,因为它是一个有效的事务。

内存池

完整的对等体可以跟踪有可能被包含在下一个中的未确认事务这对于矿工谁将真正我的某些或所有这些事务至关重要,但对于希望跟踪的任何对等体也是有用的未确认的事务,例如对等体未确认的事务信息提供给SPV客户端

因为未确认的事务在Bitcoin中没有永久状态,Bitcoin Core将它们存储在非持久性内存中,称为内存池或记忆库。对等体关闭时,除了钱包存储的任何事务外,其内存池将丢失。这意味着从未开始的未确认的事务趋向于从网络缓慢消失,因为对等体重新启动,或者当它们清除一些事务以在内存中腾出空间为他人。

开始进入的事务可能会添加到内存池中。如果替换包含它们,这些重新添加的事务可能几乎立即从池中重新删除。在Bitcoin Core中是这样,它从链中逐个删除陈旧块,从尖端(最高)开始。当每个被删除时,其事务将被添加回内存池。在删除所有过期块之后,替换逐个添加到链中,以新提示结尾。当添加每个时,它确认的任何事务都将从内存池中删除。

SPV客户端没有内存池,因为它们不会中继事务。他们无法独立验证交易尚未包含在中,并且仅支持UTXO,因此他们无法知道哪些交易有资格被包含在下一个中。

行为不端的节点

请注意,对于这两种类型的广播,都有机制来惩罚通过发送虚假信息占用带宽和计算资源的行为不正确的对方如果对等体-banscore =&lt; n&gt;阈值以上获得一个banscore,他将被禁止由-bantime = n>,默认值为86,400(24小时)。

警报

Bitcoin Core 0.13.0中删除

Bitcoin Core的早期版本允许开发人员和值得信赖的社区成员发布比特币警报以通知用户关键的网络范围的问题。此消息系统已在Bitcoin Core v0.13.0中退役但是,内部警报,分区检测警告和-alertnotify选项功能仍然存在。

挖矿

挖矿区块链添加新的区块,使交易历史难以修改。当前挖矿 有两种形式:

  • 单独挖矿,就是矿工试图单独生成区块,块的收益和交易费用完全归他自己,允许他收到较大的分成收益(支付之间需要更长时间)。

  • 矿池挖矿,其中矿池资源和其他矿工一块能更快的找到区块,矿池里的矿工共享收益,大致于它们共享的哈希运算能力有关,它们获得较小的分成收益(支付之间所需时间更短)。

单独挖矿

如下图所示,比较典型地,独立矿工使用bitcoind来从网络中获取新交易。他们的挖矿软件周期性地使用getblocktemplate轮询bitcoind,以提供币基交易所需要发送的新的交易列表和公钥。

Solo Bitcoin Mining

采矿软件使用模板(如下所述)构建一个,并创建一个块头然后它将80字节块头以及目标阈值难度设置)一起发送到其挖矿硬件(ASIC)。挖矿硬件遍历块头 nonce的每个可能值,并生成相应的哈希值。

若没有哈希值低于阈值,挖矿硬件就从挖矿软件那得到一个具有新的merkle根的更新过的块头,这个新块头以添加额外的nonce数据到币基交易的币基字段方式创建。

另一方面,如果发现一个哈希低于目标阈值,挖矿硬件会返回一个具有成功nonce的块头给挖矿软件。挖矿软件组合块头和块并把这个完整的块发送给bitcoind,让其在网络中广播以添加到区块链上。

矿池挖矿

矿池挖矿遵循类似的工作流程,如下所示,它允许矿池管理者根据矿工的工作完成情况支付给他们。矿池使用bitcoin的从网路中获取新的交易。使用下面讨论的方法之一,每个矿工的 挖矿软件连接到矿池,并请求构建块头所需的信息。

Pooled Bitcoin Mining

在矿池挖矿中,矿池设定目标阈值设置为比网络难度高几个数量级(更简单)。这导致挖矿硬件返回很多不适合区块链需要的哈希值的块头,但是此哈希值却低于矿池的目标阈值,来证明矿工参与了一部分求解合适哈希值的工作。

然后,矿工发送给矿池一个信息副本,矿池需要验证块头的哈希值低于目标,块头的merkle根所链接的交易块是满足矿池的目标的。(这通常意味着币基交易必须支付给矿池。)

矿工发送到矿池的信息成为共享信息,以此证明矿工参与了部分工作。矿池偶尔会接收到一些共享信息也低于网络目标阈值,那矿池就会发送它们到网络上以便能添加到区块链上。

挖矿的区块奖励和交易费支付给矿池。矿池根据矿工的贡献支付一部分收益给个体矿工。例如,如果池的目标阈值网络 目标阈值低100倍,则100股将需要平均生成以创建成功的,所以池可以为每个收益者支付1/100。不同的池使用基于这种基本共享系统的不同的奖励分配方案。

块原型

无论单独挖矿和矿池挖矿,挖矿软件都需要获取必要信息来构造块头.本小节以线性方式描述了信息的传输和使用。然而,在实际实现中,使用并行线程和队列来保持ASIC哈希运算以最大能力工作。

网络RPC

最简单和最早的方法是现在已经弃用的Bitcoin Core getwork RPC,它为矿工直接构建了一个块头由于标头仅包含一个4字节的随机数,对于大约4个gigahash,许多现代矿工需要制作数十或数百个getwork请求一秒。独立矿工仍然可以在v0.9.5或更低版本上使用getwork,但是今天的大多数矿池都会阻止或禁止使用。

getblocktemplate RPC

一个改进的方法是Bitcoin Core getblocktemplate RPC这为挖矿软件提供了更多信息:

  1. 构造一个支付给矿池或独立矿工的bitcoind钱包的币基交易所需要的信息。

  2. bitcoind或矿池所推荐的交易记录的完整备份,可以用于挖矿软件检查交易,可选地增加额外交易和删除不必要的交易。

  3. 构建下一个块头所需的其他信息:版本,以前的散列,以及比特(目标)。

  4. 池的当前目标阈值用于接受分成收益。(对于独立矿工来说,这就是网络 目标。)

使用收到的交易记录,挖矿软件想币基额外nonce字段里面添加nonce值,并转化所有的交易到一个merkle树,这棵树有一个可以在块头使用的merkle根。每当额外的nonce字段需要改变时,挖矿软件都会重建merkle树的必要部分,并更新时间及块头中的merkle根字段。

像所有bitcoind RPC一样,getblocktemplate也是通过HTTP发送的。为了确保最大工作量,大多数矿工使用HTTP longpoll可以始终保持getblocktemplate请求打开。这允许矿池,一旦矿工在P2P中发布一个新快,尽快推送一个新的getblocktemplate给矿工或矿池希望发送更多的交易记录给挖矿软件。

层次挖矿协议

getblocktemplate的广泛使用的替代方法是层次挖掘协议层次挖矿协议专注于提供给矿工自己构建块头所需的最小信息:

  1. 构建支付矿池的币基交易所需的信息。

  2. 当币基交易更新一个新的额外的nonce值时候需要再求哈希去创建一个新的merkle根的merkle树的一部分。merkle树的其他部分(如果有的话)可以不发送,基于当前交易的规模可以有效限制发送的数据量至多约1千字节。

  3. 所有其他构造下一块的块头所需要的非markle根信息。

  4. 采矿池的当前目标阈值用于接受分成收益。

收到币基交易挖矿软件将一个nonce值添加到币基额外的nonce字段中,求币基交易的哈希并添加到merkle树的接收部分。对整棵树求哈希以创建一个merkle根,树根被添加到接收到的块头里面。每当额外的nonce字段需要改变,挖矿软件对币基交易更新和再求哈希,重建merkle根,更新块头的markle根字段。

与getblocktemplate不同,使用Stratum的矿工无法检查或添加交易到他们目前挖矿的区块。与getblocktemplate不同,Stratum协议直接使用双向TCP套接字,所以矿工当一个新块被广播到P2P网络中时候不需要使用HTTP longpoll来确保它们立刻从矿池获取更新。

资源:GPLv3许可的BFGMiner 采矿软件和AGPLv3许可的Eloipool 采矿池软件在矿工和池中广泛使用。MIT许可的libblkmaker C库和python-blkmaker库可以为您的程序解释GetBlockTemplate。