玩币族移动版

玩币族首页 > 币圈百科 >

Block交易解析

  Block数据解析介绍了Block的各个组成部分:魔法数、块大小、块头部、交易个数、交易,阅读本文建议先阅读下。

  比特币交易的整个流程涉及到secp256k1加密,比特币地址的生成,数字签名校验,比特币的脚本系统,本文将详细介绍比特币地址的生成、数字签名过程以及脚本系统。至于secp256k1加密,它实际上是上述几个内容的基石要讲清楚需要大量的篇幅,考虑到这并非本系列文章的重点所以暂时略过。

  交易

  回顾一下交易的结构

  输入

输出

  交易哈希

  交易哈希和块头部哈希类似都不存在于块数据中,而是根据本身的结构进行双重sha256得来的。在比特币客户端使用getrawtransaction获取某个交易的块数据

  对结果进行进行双重sha256,以一个PHP代码为例

  比特币脚本系统

  对于整个交易而言版本号,输入个数,输出个数都是好理解的,不好理解的是交易输入和输出中的脚本。

  交易6737e1355be0566c583eecd48bf8a5e1fcdf2d9f51cc7be82d4393ac9555611c的输出脚本有两个

  其中第一个输出用作了交易

  4c2f4eb2923e8a7e524c25819a51caa76788317a7deea6dc1351f44fb0a51189的输入

  输出脚本的作用是规定交易输出能被使用的条件,输出脚本有很多种,常见的类似于上图中这样的脚本

  OP_DUP OP_HASH160 7e3f939e8ded8c0d93695310d6d481ae5da39616 OP_EQUALVERIFY OP_CHECKSIG

  这个脚本的意思是7e3f939e8ded8c0d93695310d6d481ae5da39616所对应的私钥的拥有者只要用私钥进行签名就可以使用这个输出当做别的交易的输入了。还有些别的脚本的意思可以是几个私钥对输出进行签名输出才能被使用,本文只讨论最普通的转账交易输出脚本。

  那么上面这个脚本带OP_(OP是Operate的简称)开头的字段都是什么意思呢 这里需要提下比特币的脚本系统。

  查阅Wiki中关于脚本的说明,通过Wiki可知这些操作的意思分别是:

  OP_DUP:复制栈顶的值,并将复制的值压入栈中

  OP_HASH160:弹出栈顶的一个值,对栈顶的值先进行sha256哈希,再进行ripemd-160哈希,然后将哈希后的值压入栈中

  OP_EQUALVERIFY:弹出栈顶的两个值,如果栈顶两个值相等则不操作,否则将false插入栈中

  OP_CHECKSIG:弹出栈顶的值,进行交易的校验,如果成功返回true,否则返回false,并将值压入栈中

  你可以查看Wiki中的这个例子去了解脚本执行的流程或者通过一个脚本浏览器查看脚本执行的过程。

  我们看到脚本刚开始的时候出现sig和pubKey,先不要关心他们是什么,一会见分晓。

  那么输出脚本中7e3f939e8ded8c0d93695310d6d481ae5da39616也就是pubKeyHash又是什么东西呢 实际上是发送地址1PGa6cMAzzrBpTtfvQTzX5PmUxsDiFzKyW对应的公钥的一个hash值,所以我们还需要了解比特币地址的产生过程。

  比特币地址

  比特币的地址有主网络地址和测试网络地址,对应的公钥还有压缩和非压缩之分,本文不展开讨论里面的区别,一般主网络比特币地址的产生过程如下:

  1. 准备一个ECDSA私钥

  9d153b3cdbe727e0e8524ff041d59ef00089b9d0266b02c4ce4edc8233c6d7de

  2. 通过secp256k1得到公钥pubKey

  02b8caae0de72e5d1904366d3393b4f81d2504da3ab2906c440deab0c442461846

  3. 对第2步的结果进行sha256得到

  29b90acf830a15eb7f6aabe2fb84259adfa6ffe77ad98e93489fec81c93aece4

  4. 对第3步的结果进行ripemd160得到pubKeyHash

  7e3f939e8ded8c0d93695310d6d481ae5da39616

  5. 对第4步的结果加上一个00前缀

  0007e3f939e8ded8c0d93695310d6d481ae5da39616

  6. 对第5步的结果进行sha256得到

  ba14334aac55b8d8a3e6f8cc665a85a23db7cab5983f94a76cc3abd01f226262

  7. 对第6步的结果进行sha256得到

  e95d48de407d58294e658062f7e6bd9b04a113554eec2a91faf3b2dcd173c51c

  8. 取第7步结果的前6位得到校验码

  e95d48de

  9. 将第8步的校验码加到pubKeyHash的后面

  0007e3f939e8ded8c0d93695310d6d481ae5da39616e95d48de

  10. 将第9步的结果进行base58编码得到address

  1CWYJZ4vSoemSCrfBvXreqEtojEeCUeKw3

  至此我们基本了解了交易中的输出脚本的意思,为什么交易输出脚本需要携带输出地址对应的公钥hash呢 为什么不是别的什么东西 也许你已经有了答案,那就是在这个交易输出能被当做新的交易输入的时候需要输出地址对应的私钥进行签名,也就是向外界宣布这个交易输出就是属于我这个地址的,我可以使用它因为我通过私钥签名证明了。

  签名

  前面提到了脚本栈中的sig和pubKey,这两个东西从哪里来 这就要看输入脚本,我们看4c2f4eb2923e8a7e524c25819a51caa76788317a7deea6dc1351f44fb0a51189的输入脚本

  我们可以发现02b8caae0de72e5d1904366d3393b4f81d2504da3ab2906c440deab0c442461846正好是地址1CWYJZ4vSoemSCrfBvXreqEtojEeCUeKw3对应的公钥。

  比特币客户端提供了三个用于构造交易的rpc接口,分别是:

  createrawtransaction:构造交易

  signrawtransaction:给createrawtransaction构造的交易签名

  sendrawtransaction:发送签名后的交易

  其中signrawtransaction就是为了给输入脚本生成sig,我们先要看createrawtransaction是怎样构造交易的。

  以交易6737e1355be0566c583eecd48bf8a5e1fcdf2d9f51cc7be82d4393ac9555611c第0个输出为输入,往地址1PGa6cMAzzrBpTtfvQTzX5PmUxsDiFzKyW转入0.00015BTC的交易为例。

  版本号:目前都是1

  输入个数:1个

  输出个数:1个

  锁定时间:0

  第1个输入构造

  上一个交易哈希:6737e1355be0566c583eecd48bf8a5e1fcdf2d9f51cc7be82d4393ac9555611c

  上一个交易的输出位置:0

  输入脚本长度:

  输入脚本:

  序列号:4294967295也就是0xFFFFFF

  第1个输出构造

  输出值:0.00015

  输出脚本:

  OP_DUP OP_HASH160 f444a269154eb560bebd424c2b406f1789ed49d6 OP_EQUALVERIFY OP_CHECKSIG

  上面这些操作符的值分别是

  OP_DUP:0x76

  OP_HASH160:0xa9

  由于f444a269154eb560bebd424c2b406f1789ed49d6的长度是20个字节,所以前面还要添加没有代号的压栈操作0x14(就是十进制的20,比特币脚本规定0x01-0x4b之间的操作符都是压栈操作,表示将符号后面长度为符号值大小的字节压入栈中)

  OP_EQUALVERIFY:0x88

  OP_CHECKSIG:0xac

  所以输出脚本在块中的实际值是76a914f444a269154eb560bebd424c2b406f1789ed49d688ac

  输出脚本长度:25

  现在整个交易就剩下输入脚本长度和输入脚本尚不知道,由于知道输入脚本就能知道输入脚本长度,所以实际上就只缺一个输入脚本就能完成我们的交易构造了。

  输入脚本是由sig和pubKey构成的,在比特币地址生成过程中我们得到地址

  1CWYJZ4vSoemSCrfBvXreqEtojEeCUeKw3对应的pubKey是

  02b8caae0de72e5d1904366d3393b4f81d2504da3ab2906c440deab0c442461846

  sig的构造

  1. 先以上一个交易

  (6737e1355be0566c583eecd48bf8a5e1fcdf2d9f51cc7be82d4393ac9555611c)的输出(也就是第0个)脚本(76a914f444a269154eb560bebd424c2b406f1789ed49d688ac)填充输入脚本,同时设置输入脚本长度为25。为什么要把上一个交易输出的拷贝当做新交易输入的临时脚本呢 这么做其实并没有什么原因,换做别的比如字符串”arbitrary string”也是可以的,只不过最开始是这么做的所以就这么做了。

  2. 对第1步得到的临时交易进行序列化,所谓的序列化就是按照比特币块数据构造的规则把某些字段扩展到固定长度的字节(比如交易版本号),把某些字段进行10进制到16进制的转换(比如把输入脚本长度25变成0x19),把某些字段进行大端到小端的转换(比如输出值,脚本是不需要的),或者同时对某些字段进行多个上述操作。

  版本号:0x01000000

  输入个数:0x01

  第1个输入构造

  上一个交易哈希:0x1c615595ac93432de87bcc519f2ddffce1a5f88bd4ec3e586c56e05b35e13767

  上一个交易的输出位置:0x000000

  输入脚本长度:0x19

  输入脚本:0x76a9147e3f939e8ded8c0d93695310d6d481ae5da3961688ac

  序列号:0xffffffff

  输出个数:0x01

  第1个输出构造

  输出值:0x983a000000000000

  输出脚本长度:0x19

  输出脚本:0x76a914f444a269154eb560bebd424c2b406f1789ed49d688ac

  锁定时间:0x00000000

  合起来就是

  01000000011c615595ac93432de87bcc519f2ddffce1a5f88bd4ec3e586c56e05b35e137670000001976a9147e3f939e8ded8c0d93695310d6d481ae5da3961688acffffffff01983a0000000000001976a914f444a269154eb560bebd424c2b406f1789ed49d688ac00000000

  3. 在第2步序列化结果的后面加上SIGHASH_ALL(0x00000001),SIGHASH_ALL是一种签名类型,可以暂时不管,我将在后续比特币脚本应用系列文章中详细讲述不同脚本应用的技术原理。

  4. 对上述结果进行双重sha256,得到

  5a4c7364a59b9ed458437f899d3fb74ee4cb0f71820e78549cca98d1058ec143

  5. 将第4步的结果用私钥进行签名(私钥签名的具体过程我将在后续secp256k1在比特币的应用中详细介绍)得到sig

  30440220169eca170796b07d8f1f1f5a16c64fb0b5dd395cb2853d627b4d0e2bfeece4cb02203a1ae3b989e417806730ec12fc7706d1d250293dae88b70cb84aa1456e2ef59d01

  需要注意的是由于签名过程中会使用随机数,所以每次签名的结果都是不一样的。

  有了sig和pubKey,签名后的交易输入脚本就呼之欲出了

  输入脚本=sig的长度+sig+pubKey的长度+pubKey

  所以输入脚本是:

  4730440220169eca170796b07d8f1f1f5a16c64fb0b5dd395cb2853d627b4d0e2bfeece4cb02203a1ae3b989e417806730ec12fc7706d1d250293dae88b70cb84aa1456e2ef59d012102b8caae0de72e5d1904366d3393b4f81d2504da3ab2906c440deab0c442461846

  输入脚本长度是:0x6a

  用签名的输入脚本替换临时的输入脚本后我们得到签名后的交易

  01000000011c615595ac93432de87bcc519f2ddffce1a5f88bd4ec3e586c56e05b35e13767000000006a4730440220169eca170796b07d8f1f1f5a16c64fb0b5dd395cb2853d627b4d0e2bfeece4cb02203a1ae3b989e417806730ec12fc7706d1d250293dae88b70cb84aa1456e2ef59d012102b8caae0de72e5d1904366d3393b4f81d2504da3ab2906c440deab0c442461846ffffffff01983a0000000000001976a914f444a269154eb560bebd424c2b406f1789ed49d688ac00000000

  你可以使用sendrawtransaction把这个交易发送到网络上,但是会被网络拒绝因为块链上已经有一个交易把输入用掉了。

  关于整个过程的代码可以参考一个php写的bitcoin库bitcoin-lib-php。

  总结

  Block系列就写到这了,大体介绍了一般的块和交易的构造以及生成过程,有些细节没有写到比如coinbase交易与一般的交易的区别,大家可以自己去研究研究。后面我会先写点secp256k1的东西帮助理解整个的签名校验,然后是脚本应用的技术细节。

  参考

  https://en.bitcoin.it/wiki/Category:Technical

  http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html

  http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx

知识: Block