如何编程构建 bitcoin 交易
第一部分介绍了交易构建的基础知识,如从目标地址创建输出和收集所需的输入价值。最复杂的部分是构建输入签名的消息。现在我们已经有了一个,我们将生成一个签名,最终生成交易输入的脚本。最后一步是将所有内容打包在一起。
作为 ex-tx-build.c 的结果,为我们的输入构建了可签名的消息。在 ex-tx-sign.c 中,我们将重复使用该消息作为起点。
与其签署消息本身,我们将签署其 hash256 摘要:
62 44 98 0f a0 75 2e 5b
46 43 ed b3 53 fd a5 23
8a 9a 3d 44 49 16 76 78
8e fd d2 5d d6 48 55 ba
使用我们的 ECDSA 私钥 :
16 26 07 83 e4 0b 16 73
16 73 62 2a c8 a5 b0 45
fc 3e a4 af 70 f7 27 f3
f9 e9 2b dd 3a 1d dc 42
这是我得到的一个 DER 签名(你的将会有所不同):
30 44 02 20 11 1a 48 2a
ba 6a fb a1 2a 6f 27 de
76 7d d4 d0 64 17 de f6
65 bd 10 0b c6 8c 42 84
5c 75 2a 8f 02 20 5e 86
f5 e0 54 b2 c6 ca c5 d6
63 66 4e 35 77 9f b0 34
38 7c 07 84 8b c7 72 44
42 ca cf 65 93 24
SIGHASH
标志(现在是 8 位)再次附加到签名上,与压缩的 ECDSA 公钥一起:
02
82 00 6e 93 98 a6 98 6e
da 61 fe 91 67 4c 3a 10
8c 39 94 75 bf 1e 73 8f
19 df c2 db 11 db 1d 28
最终,我们能够构建我们的 P2PKH 输入脚本:
47 30 44 02 20 11 1a 48
2a ba 6a fb a1 2a 6f 27
de 76 7d d4 d0 64 17 de
f6 65 bd 10 0b c6 8c 42
84 5c 75 2a 8f 02 20 5e
86 f5 e0 54 b2 c6 ca c5
d6 63 66 4e 35 77 9f b0
34 38 7c 07 84 8b c7 72
44 42 ca cf 65 93 24 01
21 02 82 00 6e 93 98 a6
98 6e da 61 fe 91 67 4c
3a 10 8c 39 94 75 bf 1e
73 8f 19 df c2 db 11 db
1d 28
是的,签名过程可能是我们工作中最令人讨厌和容易出错的部分。
我们已经准备好从我们的输入和输出中打包交易。让我们看看 ex-tx-pack.c。
就像我为输出所做的那样,我还在 tx.h 中编写了一个用于创建 P2PKH 输入的 C 宏。该宏接受 UTXO 输出点、签名、公钥和SIGHASH
标志:
void bbp_txin_create_p2pkh(bbp_txin_t *txin, const bbp_outpoint_t *outpoint,
const char *sig, const char *pub, bbp_sighash_t flag);
我们使用该宏来创建我们的输入,而输出代码则直接从 ex-tx-build.c 中取出:
bbp_outpoint_fill(&outpoint,
"f34e1c37e736727770fed85d1b129713ef7f300304498c31c833985f487fa2f3", 0);
bbp_txin_create_p2pkh(&ins[0], &outpoint,
"30440220111a482aba6afba12a6f27de767dd4d06...",
"0282006e9398a6986eda61fe91674c3a108c39947...",
BBP_SIGHASH_ALL);
bbp_txout_create_p2pkh(&outs[0], 25100000,
"18ba14b3682295cb05230e31fecb000892406608");
bbp_txout_create_p2pkh(&outs[1], 61900000,
"6bf19e55f94d986b4640c154d864699341919511");
最简单的步骤是组装交易结构并将其传递给bbp_tx_serialize
:
tx.version = bbp_eint32(BBP_LITTLE, 1);
tx.outputs_len = 2;
tx.outputs = outs;
tx.inputs_len = 1;
tx.inputs = ins;
tx.locktime = 0;
rawtx_len = bbp_tx_size(&tx, 0);
rawtx = malloc(rawtx_len);
bbp_tx_serialize(&tx, rawtx, 0);
将flag
参数设置为0
以打包已签名的交易而不是可签名消息。序列化的交易为:
/* 版本(32 位) */
01 00 00 00
/* 输入数量(varint) */
01
/* UTXO txid(hash256,小端) */
f3 a2 7f 48 5f 98 33 c8
31 8c 49 04 03 30 7f ef
13 97 12 1b 5d d8 fe 70
77 72 36 e7 37 1c 4e f3
/* UTXO 索引(32 位) */
00 00 00 00
/* 输入脚本(varint + 数据) */
6a 47 30 44 02 20 11 1a
48 2a ba 6a fb a1 2a 6f
27 de 76 7d d4 d0 64 17
de f6 65 bd 10 0b c6 8c
42 84 5c 75 2a 8f 02 20
5e 86 f5 e0 54 b2 c6 ca
c5 d6 63 66 4e 35 77 9f
b0 34 38 7c 07 84 8b c7
72 44 42 ca cf 65 93 24
01 21 02 82 00 6e 93 98
a6 98 6e da 61 fe 91 67
4c 3a 10 8c 39 94 75 bf
1e 73 8f 19 df c2 db 11
db 1d 28
/* UTXO 序列 */
ff ff ff ff
/* 输出数量(varint) */
02
/* 输出价值(64 位) */
e0 fe 7e 01 00 00 00 00
/* 输出脚本(varint + 数据) */
19 76 a9 14 18 ba 14 b3
68 22 95 cb 05 23 0e 31
fe cb 00 08 92 40 66 08
88 ac
/* 找零输出价值(64 位) */
e0 84 b0 03 00 00 00 00
/* 找零输出脚本(varint + 数据) */
19 76 a9 14 6b f1 9e 55
f9 4d 98 6b 46 40 c1 54
d8 64 69 93 41 91 95 11
88 ac
/* 锁定时间(32 位) */
00 00 00 00
总共 225 字节。通过对交易进行 hash256(大端)可获得 txid:
99 96 e2 f6 4b 6a f0 23
2d d9 c8 97 39 5c e5 1f
dd 35 e6 35 9e dd 28 55
c6 0f f8 23 d8 d6 57 d1
我们成功了,我们刚刚构建了我们的第一笔比特币交易!接下来呢?当然,我们希望将我们的交易提交到区块链。为此,存在网络服务可以帮助我们避免与 P2P 网络通信的负担。
例如,Blockr 推送服务 (在我们的场景中为测试网)非常方便,可以将原始交易发布到区块链。插入一个原始交易,就像从 ex-tx-pack.c 输出的rawtx
一样,然后你就完成了,甚至可以在确认之前再次检查。如果你之前没有双花你的 UTXO,并且你已经做了一切正确,几分钟后你应该能看到交易实时显示。也就是说,一旦它在一个区块中被挖出。
然而,如果你尝试发布我们的示例交易,你将收到以下错误:
别担心,那是因为我已经自己发布了它。看看它在区块浏览器上的样子 。
在 GitHub 上的完整源代码 。
你学会了如何签署交易输入并打包原始区块链交易。交易标识符(txid)是交易字节的 hash256 摘要。
在下一篇文章中,我们将看一看钱包软件。如果你喜欢这篇文章,请分享。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!