签署比特币 Segwit 交易
Signing a Bitcoin Segwit Transaction
为了了解有关比特币交易的更多信息,我正在研究二进制文件,并没有真正使用任何工具,如 BitcoinTx 或 Bitcoin-cli。无论如何,我在尝试签署隔离见证交易时遇到了一些障碍。现在这是一个简单的交易,一个 UTXO 和一个接收者,这是一个 P2WPKH 交易。
问题是,对于 segwit,我不知道在签署交易之前要在 SigScript 字段中输入什么。文档说它应该是空的,但这似乎不起作用。
02000000010a02214430acee2ed509798187210171bb387075ce2c82ef5a73774d6159387500000000 00 ffffffff
018ca1b404000000001976a914906ec1c4804632c0b067e5a2732c41cdf620c4e688ac0000000001000000
以上是原始交易,1个输入,1个输出。在 segwit 字段中,我有一个 0x00 字节,如图所示。所以,我 sha256(sha256) 上面的二进制文件,然后创建我的签名隔离见证交易如下:
020000000001010a02214430acee2ed509798187210171bb387075ce2c82ef5a73774d615938750000000017160014d99f1ea19e98d9ed10e5d442be37a44b76ffffc44b76b63
018ca1b404000000001976a914906ec1c4804632c0b067e5a2732c41cdf620c4e688ac
02
4830460221009f5743a2fc62bb9cee292ef79eb3ec3a20a3ed1ec84bdf9e8520592ca1d84f35022100922f887be7283eb0fd73da53efa554e62a589f6e268c0d300c2f727791
41047d011958b661181242addd300b6d5c51f80d62674555831dd855b34358b57e05fb46477ba167a57b23bf8a492305c0085d8c34aa04d483b7f15a2d5610=1e00[0ff60]
所以在上面,我得到了我的输入交易,在 SigScript 中我输入了 0,然后是 public 键的 20 字节 Hash160。
接下来,输出交易,它只有一个 public 键散列 OP_DUP, OP_HASH_160 OP_EQUALVERIFY, OP_CHECKSIG.
最后是签名,02 分为两部分,1 是签名本身(反编码),最后是 public 密钥。
出于某种原因,它说签名不正确,我不知道为什么。在我签名之前,交易中的 SigScript 实际上应该为 NULL 吗?在我签名之前,它是否应该是钱包的 public 密钥的 HASH160 有资金(就像它用于 P2PKH 交易)?
任何指点都将不胜感激,因为我认为我已经接近了,我只需要知道交易必须是什么样子,然后再签名。
您需要将 ScriptPubKey 放在 SigScript 字段上以进行签名。对椭圆曲线签名的字节顺序和低-高 R 和 S 规则要非常小心。
解释来自:https://bitcoin.stackexchange.com/posts/32695/edit
分步说明:
我们开始创建一个我们散列并签名的新交易。
- 添加四字节版本字段:
01000000
- 指定输入数量的单字节 varint:
01
- 我们要从中兑换输出的交易的 32 字节哈希值(倒序):
be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
- 四字节字段,表示我们要从具有上述哈希(从零开始计算)的交易中兑换的输出索引:
00000000
- 现在是scriptSig。为了签署交易,这里临时填充了我们要兑换的输出的 scriptPubKey。首先,我们写一个单字节的 varint,表示 scriptSig 的长度(0x19 = 25 字节):
19
- 然后我们编写实际的 scriptSig(这是我们要兑换的输出的 scriptPubKey):
76 a9 14 dd6cce9f255a8cc17bda8ba0373df8e861cb866e 88 ac
(查看 https://blockchain.info/tx/96534da2f213367a6d589f18d7d6d1689748cd911f8c33a9aee754a80de166be?show_adv=true 上的底线)
- 然后我们写一个四字节的字段来表示序列。当前始终设置为 0xffffffff:
ffffffff
- 接下来是一个单字节的 varint,其中包含我们新交易中的输出数量。在本例中我们将其设置为 1:
01
- 然后我们写入一个 8 字节字段(64 位整数,小端),其中包含我们要从指定输出中兑换的金额。我会将其设置为输出中可用的总金额减去 0.0001 BTC (128307 - 10000) 的费用:
23ce010000000000
- 然后我们开始写交易的输出。我们从一个单字节的 varint 开始,表示输出脚本的长度(0x19 或 25 字节):
19
- 然后实际输出脚本:
76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 ac
(这是将资金转回地址1FromKBPAS8MWsk1Yv1Yiu8rJbjfVioBHc)
- 然后我们写入四字节的"lock time"字段:
00000000
- 最后,我们写了一个四字节的 "hash code type"(在我们的例子中是 1):
01000000
好的,结果是
01000000
01
be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
00000000
19 76 a9 14 dd6cce9f255a8cc17bda8ba0373df8e861cb866e 88 ac
ffffffff
01
23ce010000000000
19 76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 ac
00000000
01000000
现在我们对整个结构进行双重 SHA256 哈希,得到哈希 1cde0239b55717cca8003104abc2ec2673d4f6fabea0b74351940e382e88486f
现在我们应该创建 ECDSA 签名...
1MBngSqZbMydscpzSoehjP8kznMaHAzh9y
是 "mrbubbymrbubbymrbubby!" 的脑钱包,它恰好编码了一个从 'MB' 开始的地址(使链接 2 变得非常容易;请参阅下面的@WizardOfAussie 评论以了解短语来源)。
WIF 中的私钥:5HvofFG7K1e2aeWESm5pbCzRHtCSiZNbfLYXBvxyA57DhKHV4U3
在十六进制中,私钥是0ecd20654c2e2be708495853e8da35c664247040c00bd10b9b13e5e86e6a808d
。每个加密库中都有一个 sign (key, digest) 方法。它将 return 一个字节数组。这个数组不超过 72 个字节,以十六进制代码 30 开头。假设签名是 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207e9d103dd9018e12cb7180e
对于这个签名,我们附加了一个字节的哈希码类型:01
。 1MBngSqZbMydscpzSoehjP8kznMaHAzh9y 的 public 密钥是:042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
- 我们通过连接构造最终的 scriptSig:<包含 DER 编码签名的长度加上一字节哈希代码类型的单字节脚本 OPCODE>|< 实际的 DER 编码签名加上一字节哈希码类型>|< 包含 public 密钥长度的单字节脚本 OPCODE>|<实际 public 密钥>
scriptSig 将是
49 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207e9d103dd9018e12cb7180e 01
41 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
第一行是'push signature concatenated with 01',第二行是'push pubkey'。 scriptSig 的长度为 140 字节(十六进制为 0x8c)
然后我们用第 16 步的数据长度替换第 5 步的单字节 varint 长度字段。长度为 140 字节,或 0x8C 字节:8c
并且我们将实际的scriptSig替换为第16步中构建的数据结构。
我们通过 删除 我们在步骤 13 中添加的四字节哈希码类型来完成,我们最终得到以下字节流,是最终交易:
01000000
01
be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
00000000
8c
49 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207de9cd18103
41 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
ffffffff
01
23ce010000000000
19 76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 交流电
00000000
为了了解有关比特币交易的更多信息,我正在研究二进制文件,并没有真正使用任何工具,如 BitcoinTx 或 Bitcoin-cli。无论如何,我在尝试签署隔离见证交易时遇到了一些障碍。现在这是一个简单的交易,一个 UTXO 和一个接收者,这是一个 P2WPKH 交易。
问题是,对于 segwit,我不知道在签署交易之前要在 SigScript 字段中输入什么。文档说它应该是空的,但这似乎不起作用。
02000000010a02214430acee2ed509798187210171bb387075ce2c82ef5a73774d6159387500000000 00 ffffffff
018ca1b404000000001976a914906ec1c4804632c0b067e5a2732c41cdf620c4e688ac0000000001000000
以上是原始交易,1个输入,1个输出。在 segwit 字段中,我有一个 0x00 字节,如图所示。所以,我 sha256(sha256) 上面的二进制文件,然后创建我的签名隔离见证交易如下:
020000000001010a02214430acee2ed509798187210171bb387075ce2c82ef5a73774d615938750000000017160014d99f1ea19e98d9ed10e5d442be37a44b76ffffc44b76b63 018ca1b404000000001976a914906ec1c4804632c0b067e5a2732c41cdf620c4e688ac 02 4830460221009f5743a2fc62bb9cee292ef79eb3ec3a20a3ed1ec84bdf9e8520592ca1d84f35022100922f887be7283eb0fd73da53efa554e62a589f6e268c0d300c2f727791 41047d011958b661181242addd300b6d5c51f80d62674555831dd855b34358b57e05fb46477ba167a57b23bf8a492305c0085d8c34aa04d483b7f15a2d5610=1e00[0ff60]
所以在上面,我得到了我的输入交易,在 SigScript 中我输入了 0,然后是 public 键的 20 字节 Hash160。
接下来,输出交易,它只有一个 public 键散列 OP_DUP, OP_HASH_160 OP_EQUALVERIFY, OP_CHECKSIG.
最后是签名,02 分为两部分,1 是签名本身(反编码),最后是 public 密钥。
出于某种原因,它说签名不正确,我不知道为什么。在我签名之前,交易中的 SigScript 实际上应该为 NULL 吗?在我签名之前,它是否应该是钱包的 public 密钥的 HASH160 有资金(就像它用于 P2PKH 交易)?
任何指点都将不胜感激,因为我认为我已经接近了,我只需要知道交易必须是什么样子,然后再签名。
您需要将 ScriptPubKey 放在 SigScript 字段上以进行签名。对椭圆曲线签名的字节顺序和低-高 R 和 S 规则要非常小心。
解释来自:https://bitcoin.stackexchange.com/posts/32695/edit
分步说明:
我们开始创建一个我们散列并签名的新交易。
- 添加四字节版本字段:
01000000
- 指定输入数量的单字节 varint:
01
- 我们要从中兑换输出的交易的 32 字节哈希值(倒序):
be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
- 四字节字段,表示我们要从具有上述哈希(从零开始计算)的交易中兑换的输出索引:
00000000
- 现在是scriptSig。为了签署交易,这里临时填充了我们要兑换的输出的 scriptPubKey。首先,我们写一个单字节的 varint,表示 scriptSig 的长度(0x19 = 25 字节):
19
- 然后我们编写实际的 scriptSig(这是我们要兑换的输出的 scriptPubKey):
76 a9 14 dd6cce9f255a8cc17bda8ba0373df8e861cb866e 88 ac
(查看 https://blockchain.info/tx/96534da2f213367a6d589f18d7d6d1689748cd911f8c33a9aee754a80de166be?show_adv=true 上的底线) - 然后我们写一个四字节的字段来表示序列。当前始终设置为 0xffffffff:
ffffffff
- 接下来是一个单字节的 varint,其中包含我们新交易中的输出数量。在本例中我们将其设置为 1:
01
- 然后我们写入一个 8 字节字段(64 位整数,小端),其中包含我们要从指定输出中兑换的金额。我会将其设置为输出中可用的总金额减去 0.0001 BTC (128307 - 10000) 的费用:
23ce010000000000
- 然后我们开始写交易的输出。我们从一个单字节的 varint 开始,表示输出脚本的长度(0x19 或 25 字节):
19
- 然后实际输出脚本:
76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 ac
(这是将资金转回地址1FromKBPAS8MWsk1Yv1Yiu8rJbjfVioBHc) - 然后我们写入四字节的"lock time"字段:
00000000
- 最后,我们写了一个四字节的 "hash code type"(在我们的例子中是 1):
01000000
好的,结果是
01000000
01
be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396
00000000
19 76 a9 14 dd6cce9f255a8cc17bda8ba0373df8e861cb866e 88 ac
ffffffff
01
23ce010000000000
19 76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 ac
00000000
01000000
现在我们对整个结构进行双重 SHA256 哈希,得到哈希
1cde0239b55717cca8003104abc2ec2673d4f6fabea0b74351940e382e88486f
现在我们应该创建 ECDSA 签名...
1MBngSqZbMydscpzSoehjP8kznMaHAzh9y
是 "mrbubbymrbubbymrbubby!" 的脑钱包,它恰好编码了一个从 'MB' 开始的地址(使链接 2 变得非常容易;请参阅下面的@WizardOfAussie 评论以了解短语来源)。 WIF 中的私钥:5HvofFG7K1e2aeWESm5pbCzRHtCSiZNbfLYXBvxyA57DhKHV4U3
在十六进制中,私钥是0ecd20654c2e2be708495853e8da35c664247040c00bd10b9b13e5e86e6a808d
。每个加密库中都有一个 sign (key, digest) 方法。它将 return 一个字节数组。这个数组不超过 72 个字节,以十六进制代码 30 开头。假设签名是 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207e9d103dd9018e12cb7180e
对于这个签名,我们附加了一个字节的哈希码类型:01
。 1MBngSqZbMydscpzSoehjP8kznMaHAzh9y 的 public 密钥是:042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
- 我们通过连接构造最终的 scriptSig:<包含 DER 编码签名的长度加上一字节哈希代码类型的单字节脚本 OPCODE>|< 实际的 DER 编码签名加上一字节哈希码类型>|< 包含 public 密钥长度的单字节脚本 OPCODE>|<实际 public 密钥>
scriptSig 将是
49 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207e9d103dd9018e12cb7180e 01
41 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9
第一行是'push signature concatenated with 01',第二行是'push pubkey'。 scriptSig 的长度为 140 字节(十六进制为 0x8c)
然后我们用第 16 步的数据长度替换第 5 步的单字节 varint 长度字段。长度为 140 字节,或 0x8C 字节:
8c
并且我们将实际的scriptSig替换为第16步中构建的数据结构。
我们通过 删除 我们在步骤 13 中添加的四字节哈希码类型来完成,我们最终得到以下字节流,是最终交易:
01000000 01 be66e10da854e7aea9338c1f91cd489768d1d6d7189f586d7a3613f2a24d5396 00000000 8c 49 3046022100cf4d7571dd47a4d47f5cb767d54d6702530a3555726b27b6ac56117f5e7808fe0221008cbb42233bb04d7f28a715cf7c938e238afde90207de9cd18103 41 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9 ffffffff 01 23ce010000000000 19 76 a9 14 a2fd2e039a86dbcf0e1a664729e09e8007f89510 88 交流电 00000000