本文以比特币测试网络(bitcoin-testnet)作为开发试验环境,解析比特币交易(Transaction)的数据结构,结合Node.js为例来说明如何自行组织特定需要的交易数据并签名后广播,最终被矿工节点确认生效。
一、了解比特币的“交易”(Transaction)数据结构
交易(Transaction)是比特币系统的信息载体和最小单元。而块(Block)就是将这若干个这样的“交易”基础单元打包装箱,贴上封条。再按一定的机制和先后顺序将这些块串联起来就构成了区块链(Blockchain)。
对于基于比特币区块链的应用开发,“交易”(Transaction)是最直接用到、最关键的数据结构。除了“交易”外,还需要掌握比特币区块量相 关的一些基础术语的含义,包括钱包的私钥、公钥和地址、区块、区块链等,有不少书籍和网上资料都有比较清晰的描述,这里推荐可以看看这本在线书籍《精通比 特币》(http://zhibimo.com/read/wang-miao/mastering-bitcoin/index.html)。
在这里我们侧重对“交易”(Transaction)的数据结构做一个深入的剖析和了解。
一笔比特币交易是一个含有输入值和输出值的数据结构,该数据结构植入了将一笔资金从初始点(输入值)转移至目标地址(输出值)的代码信息。一笔比特币交易包含一些字段,如下表所示。
大小 字段名称 数据类型 描述
4字节 协议版本 uint32_t 明确这笔交易参照的规则协议的版本号
1-9字节 输入数量 var_int 被包含的输入交易的数量
不定 输入列表 tx_in[] 一个或多个输入交易构成的数组
1-9字节 输出数量 var_int 被包含的输出交易的数量
不定 输出列表 tx_out[] 一个或多个输出交易构成的数组
4字节 锁定时间 uint32_t 一个UNIX时间戳或区块号
注:锁定时间字段定义了能被加到区块链里的最早的交易时间。在大多数交易里,它被缺省设置成0,用来表示立即执行。如果锁定时间大于0并且小于5 亿,就被视为区块高度,意指在这个指定的区块高度之前,该交易没有被包含在区块链里。如果锁定时间大于5亿,则它被当作是一个Unix纪元时间戳(从 1970年1月1日以来的秒数),并且在这个指定时点之前,该交易不会被包含在区块链里。锁定时间的使用相当于将一张纸质支票的生效时间予以后延。
基于区块链技术的应用开发,实际上主要就是在交易的输出数据结构上做文章,来承载具体的业务逻辑,比如像ODIN开源项目就是将标识属性数据按一定格式嵌入到比特币的多重交易输出数据块中。
二、交易记录的实例解析
下面是一个比特币交易的原始数据示例(将原始二进制数据按字节以16进制ASCII码形式输出,便于分析):
0100000002eb2121e4e727bdb28525e79d39a90bd711b9e8413c054b29ffc4bb4775e69f82010000006b483045022100df82
cf6c95b4eb64e4e9cee3af88a94c65fa81650e824d515f089192b7e3c09c0220119c1fcfd9354755ea815cf714c181b56784
b8f98f59f33e977c8939cd6f75db0121022e9f31292873eee495ca9744fc410343ff373622cca60d3a4c926e58716114b9ff
ffffffc9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a47304402206b993231ad
ec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec37dfa2c646c78d9a0753d56cb4393e8d0b
22dc580ef1aa6cccef208d0141042ff65bd6b3ef04253225405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92
a51330eb0c76cf49e9e474fb9159ab41653a9c1725c031449d31026affffffff031027000000000000475121022e9f312928
73eee495ca9744fc410343ff373622cca60d3a4c926e58716114b9210250504b2d42455441506565722d506565722d6e6574
776f726b207075626c696352aee07b9a3b000000001976a914391ef5239da2a3904cda1fd995fb7c4377487ea988ac000000
00000000000d6a0b436f6465206973204c617700000000
对上述报文按协议规则可按字段分解说明如下:
01000000 // 版本号,UINT32
02 // Tx输入数量,变长INT。0×02=2个输入。
/*** 接下来是第一组Input Tx ***/
eb2121e4e727bdb28525e79d39a90bd711b9e8413c054b29ffc4bb4775e69f82 // Tx交易的Hash值,固定32字节
01000000 // 消费的Tx位于前向交易输出的第0个,UINT32,固定4字节
6b // 接下来对应签名数据的长度, 0x6b = 107字节 。
// 这107字节长度的签名,含有两个部分:私钥签名 + 公钥。
// 当这里的数值为00时,则表示为尚未经过签名的原始交易
48 // 对应私钥签名的数据长度,0×48 = 72字节
3045022100df82cf6c95b4eb64e4e9cee3af88a94c65fa81650e824d515f089192b7e3c09c022011
9c1fcfd9354755ea815cf714c181b56784b8f98f59f33e977c8939cd6f75db01 //私钥签名内容
21 // 对应公钥的数据长度,0×21 = 33字节
022e9f31292873eee495ca9744fc410343ff373622cca60d3a4c926e58716114b9 //对应公钥数据
ffffffff // 序列号,UINT32, 固定4字节。该字段是目前未被使用的交易替换功能,默认都设成0xFFFFFFFF
/*** 第二组Input Tx。与上同理,省略分解 ***/
c9f3b07ebfca68fd1a6339d0808fbb013c90c6095fc93901ea77410103489ab7010000008a473044
02206b993231adec55e6085e75f7dc5ca6c19e42e744cd60abaff957b1c352b3ef9a022022a22fec
37dfa2c646c78d9a0753d56cb4393e8d0b22dc580ef1aa6cccef208d0141042ff65bd6b3ef042532
25405ccc3ab2dd926ff2ee48aac210819698440f35d785ec3cec92a51330eb0c76cf49e9e474fb91
59ab41653a9c1725c031449d31026affffffff
03 // Tx输出交易数量,变长INT类型。0×03=3个输入。
/*** 第一组输出 ***/
1027000000000000 //输出的比特币数量,UINT64,8个字节。字节序需翻转得到0×0000000000002710 = 10000 satoshi = 0.0001 BTC
47 //输出描述脚本字节数, 0×47 = 71字节,由一些操作码与数值构成
51 //Ox51代表OP_1(将脚本代码1压入堆栈)
21 //压入堆栈的第一个公钥的数据长度,0×21 = 33字节
022e9f31292873eee495ca9744fc410343ff373622cca60d3a4c926e58716114b9
21 //压入堆栈的第二个公钥的数据长度,0×21 = 33字节
0250504b2d42455441506565722d506565722d6e6574776f726b207075626c6963
52 //Ox52 代表 OP_2 (将脚本代码2压入堆栈)。与前面的0×51合在一起表示1of2多重签名
ae //Oxae 代表 OP_CHECKMULTISIG (执行多重签名验证)
/*** 第二组输出 ***/
e07b9a3b00000000 //输出的比特币数量,UINT64,8个字节。字节序需翻转,
19 //输出描述脚本字节数, 0×19 = 25字节,由一些操作码与数值构成
76 //脚本起始操作 0×76 代表 OP_DUP(复制栈顶元素)
a9 //地址类型 0xa9 代表 OP_HASH160(栈顶项进行两次HASH,先用SHA-256,再用RIPEMD-160)
14 //地址长度 0×14 = 20字节
391ef5239da2a3904cda1fd995fb7c4377487ea9 // 对应的HASH160值,20字节
88 //0×88 代表 OP_EQUALVERIFY (运行脚本的二进制算术和条件,如结果为0,之后运行OP_VERIFY)
ac //Oxac 代表 OP_CHECKSIG (交易所用的签名必须是哈希值和公钥的有效签名,如果为真,则返回1)
/*** 第三组输出 ***/
0000000000000000 //输出的比特币数量,UINT64,8个字节。因为是为了加入备注信息,不是普通转账交易,所以输出金额为0
0d //输出描述脚本字节数, 0x0d = 13字节,由一些操作码与数值构成
6a //0x6a代表OP_RETURN(标记交易无效),表示该交易只是追加的备注信息,不是普通转账交易
0b //备注内容长度, 0x0b = 11字节
436f6465206973204c6177 //备注数据内容(将原始二进制数据按16进制ASCII码形式表示)
00000000 // 锁定时间,UINT32,固定4字节
通过上述解析,理解和掌握了对比特币交易记录的常见组织格式后,充分利用其中对应蓝色字体部分的数据内容块来嵌入自定义的一些二进制数据内容,就可以开发实现自己特定的业务逻辑了。
三、运行示例程序
下述的示例程序是将一段特定内容的字符串按一定格式嵌入到比特币交易的备注数据块中,这样就可以被存入比特币区块链上。
在运行下述示例程序前,请确认已安装比特币测试网络(bitcoin-testnet)的Docker运行环境。如尚未安装可以参考上一篇《比特币 区块链开发由浅入深指南2》里面的说明进行安装( http://www.8btc.com/ppkpub_blockchain_develope_lesson_2 )。
将下述示例代码复制保存到比特币测试网络(bitcoin-testnet)的测试环境下(保存文件名为 OpreturnTestnet.js),在命令行下输入以下命令即可运行并看到运行结果:
node OpreturnTestnet.js
注意:每运行一次测试代码后,都需要到Docker运行环境的命令行下输入”make generate BLOCKS=10″, 模拟产生新的区块记录,让测试代码产生的交易记录得到有效的确认。
示例程序 OpreturnTestnet.js 源码如下:
/********************* 示例代码起始 **********************/
声明:此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。本网站所提供的信息,只供参考之用。