- 10.5 打包交易至区块
- 10.5.1 创币交易
- 10.5.2 Coinbase奖励与矿工费
- 10.5.3创币交易的结构
- 10.5.4 Coinbase数据
10.5 打包交易至区块
验证交易后,比特币节点会将这些交易添加到自己的内存池中。内存池也称作交易池,用来暂存尚未被加入到区块的交 易记录。与其他节点一样,Jing的节点会收集、验证并传递新的交易。而与其他节点不同的是,Jing的节点会把这些交 易整合到一个候选区块中。
让我们继续跟进,看下Alice从Bob咖啡店购买咖啡时产生的那个区块。Alice的交易在区块 277,316。为了演示本章中提到的概念,我们假设这个区块是由Jing的挖矿系统挖出的,并且继续跟进Alice的交易,因 为这个交易已经成为了新区块的一部分。
Jing的挖矿节点维护了一个区块链的本地副本。当Alice买咖啡 的时候,Jing节点的区块链已经收集到了区块277,314,并继续监听着网络上的交易,在尝试挖掘新区块的同时,也监 听着由其他节点发现的区块。当Jing的节点在挖矿时,它从比特币网络收到了区块277,315。这个区块的到来标志着终 结了产出区块277,315竞赛,与此同时也是产出区块277,316竞赛的开始。
在上一个10分钟内,当Jing的节点正在寻找区块277,315的解的同时,它也在收集交易记录为下一个区块做准备。目前 它已经收到了几百笔交易记录,并将它们放进了内存池。直到接收并验证区块277,315后,Jing的节点会检查内存池中 的全部交易,并移除已经在区块277,315中出现过的交易记录,确保任何留在内存池中的交易都是未确认的,等待被记 录到新区块中。
Jing的节点立刻构建一个新的空区块,做为区块277,316的候选区块。称作候选区块是因为它还没有包含有效的工作量证明,不是一个有效的区块,而只有在矿工成功找到一个工作量证明解之后,这个区块才生效。
现在,Jing的节点从内存池中整合到了全部的交易,新的候选区块包含有418笔交易,总的矿工费为0.09094925个比特 币。你可以通过比特币核心客户端命令行来查看这个区块,如例10-3所示:
例10-3 使用命令行检索区块277,316
$ bitcoin-cli get blockhash 277316
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
$ bitcoin-cli getblock 0000000000000001b6b9a13b095e96db41c4a928b97ef2d9\44a9b31b2cc7bdc4
{
"hash" : "0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
"confirmations" : 35561,
"size" : 218629,
"height" : 277316,
"version" : 2,
"merkleroot" : "c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e",
"tx" : [
"d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
"b268b45c59b39d759614757718b9918caf0ba9d97c56f3b91956ff877c503fbe",
... 417 more transactions ...//这一行显示交易数量
],
"time" : 1388185914,
"nonce" : 924591752,
"bits" : "1903a30c",
"difficulty" : 1180923195.25802612,
"chainwork" : "000000000000000000000000000000000000000000000934695e92aaf53afa1a",
"previousblockhash" : "0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569"
}
10.5.1 创币交易
区块中的第一笔交易是笔特殊交易,称为创币交易或者coinbase交易。这个交易是由Jing的节点构造并用来奖励矿工们所做的贡献的。
注意:当块277,316开采时,每个块的奖励是25比特币。 此后,已经过了一个“减半”时期。 2016年七月份的奖励为12.5比特币,2020年达到210000区块时,将再次减半。
Jing的节点会创建“向Jing的地址支付25.09094928个比特币”这样一个交易,把生成交易的奖励发送到自己的钱包。Jing挖出区块获得的奖励金额是coinbase奖励(25个全新的比特币)和区块中全部交易矿工费的总和。
如 例10-4所示:coinbase交易
$ bitcoin-cli getrawtransaction d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f 1
{
"hex" : "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f03443b0403858402062f503253482fffffffff0110c08d9500000000232102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac00000000",
"txid" : "d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
"version" : 1,
"locktime" : 0,
"vin" : [
{
"coinbase" : "03443b0403858402062f503253482f",
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 25.09094928,
"n" : 0,
"scriptPubKey" : {
"asm" : "02aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21OP_CHECKSIG",
"hex" : "2102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac",
"reqSigs" : 1,
"type" : "pubkey",
"addresses" : ["1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N"
]
}
}
]
}
与常规交易不同,创币交易没有输入,不消耗UTXO。它只包含一个被称作coinbase的输入,仅仅用来创建新的比特 币。创币交易有一个输出,支付到这个矿工的比特币地址。创币交易的输出将这25.09094928个比特币发送到矿工的比 特币地址,如本例所示的1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N。
10.5.2 Coinbase奖励与矿工费
为了构造创币交易,Jing的节点需要计算矿工费的总额,将这418个已添加到区块交易的输入和输出分别进行求和,然 后用输入总额减去输出总额得到矿工费总额,公式如下:
Total Fees = Sum(Inputs) - Sum(Outputs)
在区块277,316中,矿工费的总额是0.09094925个比特币。
紧接着,Jing的节点计算出这个新区块正确的奖励额。奖励额的计算是基于区块高度的,以每个区块50个比特币为开始,每产生210,000个区块减半一次。这个区块高度是277,316,所以正确的奖励额是25个比特币。
详细的计算过程可以参看比特币核心客户端中的GetBlockValue函数,如例10-5所示:
例10-5 计算区块奖励—函数GetBlockValue, Bitcoin Core Client, main.cpp
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params
&
consensusParams){
int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
// Force block reward to zero when right shift is undefined.
if (halvings
>
= 64)
return 0;
CAmount nSubsidy = 50 * COIN;
// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
nSubsidy
>
>
= halvings;
return nSubsidy;
}
变量Subsidy表示初始奖励额,值为 COIN 常量(100,000,000聪)与50的乘积,也就是说初始奖励额为50亿聪。
紧接着,这个函数用当前区块高度除以减半间隔(SubsidyHalvingInterval 函数)得到减半次数(变量 halvings )。每 210,000个区块为一个减半间隔,对应本例中的区块277316,所以减半次数为1。
变量 halvings 最大值64,如果超出这个值,代码算得的奖励额为0。
然后,这个函数会使用二进制右移操作将奖励额(变量 nSubsidy)右移一位(等同与除以2),每一轮减半右移一次。在这个例子中,对于区块277,316只需要将值为50亿聪的奖励额右移一次,得到25亿聪,也就是25个比特币的奖励额。之所以采用二进制右移操作,是因为相比于整数或浮点数除法,右移操作的效率更高。
最后,将coinbase奖励额(变量 nSubsidy )与矿工费(nFee)总额求和,并返回这个值。
注意: 如果Jing的挖矿节点把coinbase交易写入区块,那么如何防止Jing奖励自己100甚至1000比特币? 答案是,不正确的奖励将被其他人视为无效,浪费了Jing用于工作证明的投入。 只有这个区块被大家认可,Jing才能得到报酬。
10.5.3创币交易的结构
经过计算,Jing的节点构造了一个创币交易,支付给自己25.09094928枚比特币。
例10-4所示,创币交易的结构比较特殊,与一般交易输入需要指定一个先前的UTXO不同,它包含一个“coinbase“输 入。在之前的章节中,我们已经给出了交易输入的结构。现在让我们来比较一下常规交易输入与创币交易输入。表10-1给出了常规交易输入的结构,表10-2给出的是创币交易输入的结构。
表10-1 常规交易输入结构
表10-2,coinbase交易输入结构
在Coinbase交易中,“交易哈希”字段32个字节全部填充0,“交易输出索引”字段全部填充0xFF(十进制的255),这两个字段的值表示不引用UTXO。“解锁脚本”由coinbase数据代替,数据可以由矿工自定义。
10.5.4 Coinbase数据
创币交易不包含“解锁脚本“(又称作 scriptSig)字段,这个字段被coinbase数据替代,长度最小2字节,最大100字节。除 了开始的几个字节外,矿工可以任意使用coinbase的其他部分,随意填充任何数据。
以创世块为例,中本聪在coinbase中填入了这样的数据“The Times 03/Jan/ 2009 Chancellor on brink of second bailout for banks“(泰晤士报 2009年1月3日 财政大臣将再次对银行施以援手),表示对日期的证明,同时也表达了对银行系统的不信任。现在,矿工使用coinbase数据实现extra nonce功能,并嵌入字符串来标识挖出它的矿池,这部分内容会在后面的小节描述。
coinbase前几个字节也曾是可以任意填写的,不过在后来的第34号比特币改进提议(BIP34)中 规定了版本2的区块(版本字段为2的区块),这个区块的高度必须跟在脚本操作“push“之后,填充在coinbase字段的起始处。
我们以例10-4中的区块277,316为例,coinbase就是交易输入的“解锁脚本“(或scriptSig)字段,这个字段的十六进制值 为03443b0403858402062f503253482f。下面让我们来解码这段数据。
第一个字节是03,脚本执行引擎执行这个指令将后面3个字节压入脚本栈,紧接着的3个字节——0x443b04, 是以小端格式(最低有效字节在先)编码的区块高度。翻转字节序得到0x043b44,表示为十进制是277,316。
紧接着的几个十六进制数(03858402062)用于编码extra nonce(参见”10.11.1 随机值升位方案”),或者一个随机值,从而求解一个适当的工作量证明。
coinbase数据结尾部分(2f503253482f)是ASCII编码字符 /P2SH/,表示挖出这个区块的挖矿节点支持BIP0016所定义的 pay-to-script-hash(P2SH)改进方案。在P2SH功能引入到比特币的时候,曾经有过一场对P2SH不同实现方式的投票, 候选者是BIP0016和BIP0017。支持BIP0016的矿工将/P2SH/放入coinbase数据中,支持BIP0017的矿工将 p2sh/CHV 放入他们的coinbase数据中。最后,BIP0016在选举中胜出,直到现在依然有很多矿工在他们的coinbase中填 入/P2SH/以表示支持这个功能。
10-6使用了libbitcoin库(在之前“其他替代客户端、资料库、工具包”中提到)从创世块中提取coinbase数据,并显示 出中本聪留下的信息。libbitcoin库中自带了一份创世块的静态拷贝,所以这段示例代码可以直接取自库中的创世块数 据。
例10-6 从创世区块中提取coinbase数据
code/satoshi-words.cpp\[\]
例8-7中,我们使用GNU C++编译器编译源代码并运行得到的可执行文件,
例8-7 编译并运行satoshi-words示例代码
$ # Compile the code
$ g++ -o satoshi-words satoshi-words.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the executable
$ ./satoshi-words
^D��
<
GS
>
^A^DEThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks