比特币交易教程

创建交易是大多数比特币应用程序所做的事情。本节介绍如何使用比特币核心的RPC接口创建具有各种属性的交易。

你的应用程序可能会使用除比特币核心之外的东西来创建交易,但在任何系统中,你都需要提供相同的类型用于创建具有与那些属性相同属性的交易数据如下面所描述的。

为了使用本教程,你需要设置[比特币核心][核心可执行文件]并在测试中创建一个50 BTC的回归测试模式环境钱包。

简单消费

比特币核心提供了几个处理所有细节的RPC支出,包括创造变革产出和支付适当的费用。即使是高级用户也应该尽可能使用这些RPC来减少satoshis错误丢失的可能性。

1
2
3
4
> bitcoin-cli -regtest getnewaddress
mvbnrCX3bg1cDRUu8pkecrvP6vQkSLDSou

> NEW_ADDRESS=mvbnrCX3bg1cDRUu8pkecrvP6vQkSLDSou

获取一个新的比特币地址并将其保存在shell变量$ NEW_ADDRESS中。

1
2
> bitcoin-cli -regtest sendtoaddress $NEW_ADDRESS 10.00
263c018582731ff54dc72c7d67e858c002ae298835501d80200f05753de0edf0

使用sendtoaddress RPC将10位比特币发送到地址。返回的十六进制字符串是交易标识符(txid)。

sendtoaddress RPC自动选择一个未使用的交易输出(UTXO)来从中使用satoshis。在这种情况下,它从我们唯一可用的UTXO中撤回了satoshis,这是块#1的coinbase交易,它随着块#101的创建而成熟。

要花费特定的UTXO,你可以使用sendfrom RPC。

1
2
3
> bitcoin-cli -regtest listunspent
[
]

使用listunspent RPC显示属于此钱包的UTXO。该列表为空,因为它默认只显示已确认的UTXO,我们只使用了我们唯一确认的UTXO。

1
> bitcoin-cli -regtest listunspent 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 0,
"address" : "muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt",
"scriptPubKey" : "76a9149ba386253ea698158b6d34802bb9b550\
f5ce36dd88ac",
"amount" : 40.00000000,
"confirmations" : 0,
"spendable" : true,
"solvable" : true
},
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 1,
"address" : "mvbnrCX3bg1cDRUu8pkecrvP6vQkSLDSou",
"account" : "",
"scriptPubKey" : "76a914a57414e5ffae9ef5074bacbe10a320bb\
2614e1f388ac",
"amount" : 10.00000000,
"confirmations" : 0,
"spendable" : true,
"solvable" : true
}
]

使用参数“0”重新运行listunspent RPC以显示未经证实的交易表明我们有两个具有相同txid的UTXO。显示的第一个UTXO是一个更改输出,它使用密钥池中的新地址创建sendtoaddress。显示的第二个UTXO是我们提供的地址的支出。如果我们将这些satoshis用于其他人,则第二次交易将不会显示在我们的UTXO列表中。

1
2
3
> bitcoin-cli -regtest generate 1

> unset NEW_ADDRESS

创建一个新块以确认上面的交易(少于一个第二)并清除shell变量。

简单的原生交易

原生交易RPC允许用户创建自定义交易和延迟广播这些交易。但是,原生的错误比特币核心可能无法检测到交易,还有一些原生交易中交易用户已经永久丢失了大量的satoshis,所以请小心使用mainnet上的原生交易。

本小节介绍了最简单的原生交易之一。

1
> bitcoin-cli -regtest listunspent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 0,
"address" : "muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt",
"scriptPubKey" : "76a9149ba386253ea698158b6d34802bb9b550\
f5ce36dd88ac",
"amount" : 40.00000000,
"confirmations" : 1,
"spendable" : true,
"solvable" : true
},
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 1,
"address" : "mvbnrCX3bg1cDRUu8pkecrvP6vQkSLDSou",
"account" : "",
"scriptPubKey" : "76a914a57414e5ffae9ef5074bacbe10a320bb\
2614e1f388ac",
"amount" : 10.00000000,
"confirmations" : 1,
"spendable" : true,
"solvable" : true
},
{
"txid" : "3f4fa19803dec4d6a84fae3821da7ac7577080ef754512\
94e71f9b20e0ab1e7b",
"vout" : 0,
"address" : "mwJTL1dZG8BAP6X7Be3CNNcuVKi7Qqt7Gk",
"scriptPubKey" : "210260a275cccf0f4b106220725be516adba27\
52db1bec8c5b7174c89c4c07891f88ac",
"amount" : 50.00000000,
"confirmations" : 101,
"spendable" : true,
"solvable" : true
}
]
1
2
> UTXO_TXID=3f4fa19803dec4d6a84fae3821da7ac7577080ef75451294e71f[...]
> UTXO_VOUT=0

重新运行listunspent。我们现在有三个UTXO:我们之前创建的两个交易加上块#2的coinbase交易。我们将该coinbase UTXO的txid和输出索引号(vout)保存到shell变量中。

1
2
3
4
> bitcoin-cli -regtest getnewaddress
mz6KvC4aoUeo6wSxtiVQTo7FDwPnkp6URG

> NEW_ADDRESS=mz6KvC4aoUeo6wSxtiVQTo7FDwPnkp6URG

获取要在原生交易中使用的新地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## Outputs - inputs = transaction fee, so always double-check your math!
> bitcoin-cli -regtest createrawtransaction '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT'
}
]
''' '''
{
"'$NEW_ADDRESS'": 49.9999
}'''
01000000017b1eabe0209b1fe794124575ef807057c77ada2138ae4fa8d6c4de\
0398a14f3f0000000000ffffffff01f0ca052a010000001976a914cbc20a7664\
f2f69e5355aa427045bc15e7c6c77288ac00000000

> RAW_TX=01000000017b1eabe0209b1fe794124575ef807057c77ada2138ae4[...]

使用createrawtransaction RPC的两个参数,我们创建一个新的原生格式交易。第一个参数(一个JSON数组)引用块#2中的coinbase交易的txid和我们想要花费的那个交易的输出的索引号(0)。第二个参数(一个JSON对象)使用地址(公钥哈希)和我们想要传输的比特币数创建输出。

我们将生成的原生格式交易保存到shell变量中。

警告:createrawtransaction不会自动创建更改输出,因此你很容易意外支付大笔交易费用。在这个例子中,我们的输入有50.0000比特币,我们的输出($NEW_ADDRESS)正在支付49.9999比特币,所以交易将包括0.0001比特币的费用。如果我们支付了$NEW_ADDRESS只有10比特币没有对此交易进行其他更改,交易费用将高达40比特币。请参阅下面的复杂原生交易子部分,了解如何创建具有多个输出的交易,以便你可以将更改发送回自己。

1
> bitcoin-cli -regtest decoderawtransaction $RAW_TX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"txid" : "c80b343d2ce2b5d829c2de9854c7c8d423c0e33bda264c4013\
8d834aab4c0638",
"hash" : "c80b343d2ce2b5d829c2de9854c7c8d423c0e33bda264c40138d834aab4c0638",
"size" : 85,
"vsize" : 85,
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "3f4fa19803dec4d6a84fae3821da7ac7577080ef75\
451294e71f9b20e0ab1e7b",
"vout" : 0,
"scriptSig" : {
"asm" : "",
"hex" : ""
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 49.99990000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 cbc20a7664f2f69e5355a\
a427045bc15e7c6c772 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914cbc20a7664f2f69e5355aa427045bc15e\
7c6c77288ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"mz6KvC4aoUeo6wSxtiVQTo7FDwPnkp6URG"
]
}
}
]
}

使用decoderawtransaction RPC来确切了解我们刚创建的交易的作用。

1
> bitcoin-cli -regtest signrawtransaction $RAW_TX
1
2
3
4
5
6
7
8
9
10
{
"hex" : "01000000017b1eabe0209b1fe794124575ef807057c77ada213\
8ae4fa8d6c4de0398a14f3f00000000494830450221008949f0\
cb400094ad2b5eb399d59d01c14d73d8fe6e96df1a7150deb38\
8ab8935022079656090d7f6bac4c9a94e0aad311a4268e082a7\
25f8aeae0573fb12ff866a5f01ffffffff01f0ca052a0100000\
01976a914cbc20a7664f2f69e5355aa427045bc15e7c6c77288\
ac00000000",
"complete" : true
}
1
> SIGNED_RAW_TX=01000000017b1eabe0209b1fe794124575ef807057c77ada[...]

使用signrawtransaction RPC对createrawtransaction创建的交易进行签名,并将返回的hex原始格式signed transaction保存到shell变量中。

即使交易现在已经完成,我们所连接的比特币核心节点也不知道有关交易的任何信息,也不了解网络的任何其他部分。我们已经创建了一个支出,但我们实际上没有花费任何东西,因为我们可以简单地取消设置$SIGNED_RAW_TX变量来消除交易。

1
2
> bitcoin-cli -regtest sendrawtransaction $SIGNED_RAW_TX
c7736a0a0046d5a8cc61c8c3c2821d4d7517f5de2bc66a966011aaa79965ffba

使用以下命令将签名的交易发送到连接的节点 sendrawtransaction RPC。在接受交易之后,节点通常会将其广播到其他对等体,但我们当前没有连接到其他对等体,因为我们以regtest模式启动。

1
2
3
> bitcoin-cli -regtest generate 1

> unset UTXO_TXID UTXO_VOUT NEW_ADDRESS RAW_TX SIGNED_RAW_TX

生成一个块以确认交易并清除我们的shell变量。

复杂的原生交易

在这个例子中,我们将创建一个具有两个输入和两个输出的交易。我们将分别对每个输入进行签名,如果两个输入属于同意一起创建交易的不同人(例如CoinJoin交易),则可能会发生这种情况。

1
> bitcoin-cli -regtest listunspent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 0,
"address" : "muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt",
"scriptPubKey" : "76a9149ba386253ea698158b6d34802bb9b550\
f5ce36dd88ac",
"amount" : 40.00000000,
"confirmations" : 2,
"spendable" : true,
"solvable" : true
},
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835501d\
80200f05753de0edf0",
"vout" : 1,
"address" : "mvbnrCX3bg1cDRUu8pkecrvP6vQkSLDSou",
"account" : "",
"scriptPubKey" : "76a914a57414e5ffae9ef5074bacbe10a320bb\
2614e1f388ac",
"amount" : 10.00000000,
"confirmations" : 2,
"spendable" : true,
"solvable" : true
},
{
"txid" : "78203a8f6b529693759e1917a1b9f05670d036fbb12911\
0ed26be6a36de827f3",
"vout" : 0,
"address" : "n2KprMQm4z2vmZnPMENfbp2P1LLdAEFRjS",
"scriptPubKey" : "210229688a74abd0d5ad3b06ddff36fa9cd8ed\
d181d97b9489a6adc40431fb56e1d8ac",
"amount" : 50.00000000,
"confirmations" : 101,
"spendable" : true,
"solvable" : true
},
{
"txid" : "c7736a0a0046d5a8cc61c8c3c2821d4d7517f5de2bc66a\
966011aaa79965ffba",
"vout" : 0,
"address" : "mz6KvC4aoUeo6wSxtiVQTo7FDwPnkp6URG",
"account" : "",
"scriptPubKey" : "76a914cbc20a7664f2f69e5355aa427045bc15\
e7c6c77288ac",
"amount" : 49.99990000,
"confirmations" : 1,
"spendable" : true,
"solvable" : true
}
]
1
2
3
4
5
6
7
> UTXO1_TXID=78203a8f6b529693759e1917a1b9f05670d036fbb129110ed26[...]
> UTXO1_VOUT=0
> UTXO1_ADDRESS=n2KprMQm4z2vmZnPMENfbp2P1LLdAEFRjS

> UTXO2_TXID=263c018582731ff54dc72c7d67e858c002ae298835501d80200[...]
> UTXO2_VOUT=0
> UTXO2_ADDRESS=muhtvdmsnbQEPFuEmxcChX58fGvXaaUoVt

对于我们的两个输入,我们通过将txid和输出索引号(vouts)放在shell变量中来选择两个UTXO。我们还保存与这些交易中使用的公钥(哈希或未哈希)相对应的地址。我们需要addresses,以便我们可以从钱包中获取相应的私钥。

1
2
3
4
5
6
7
8
9
> bitcoin-cli -regtest dumpprivkey $UTXO1_ADDRESS
cSp57iWuu5APuzrPGyGc4PGUeCg23PjenZPBPoUs24HtJawccHPm

> bitcoin-cli -regtest dumpprivkey $UTXO2_ADDRESS
cT26DX6Ctco7pxaUptJujRfbMS2PJvdqiSMaGaoSktHyon8kQUSg

> UTXO1_PRIVATE_KEY=cSp57iWuu5APuzrPGyGc4PGUeCg23PjenZPBPoUs24Ht[...]

> UTXO2_PRIVATE_KEY=cT26DX6Ctco7pxaUptJujRfbMS2PJvdqiSMaGaoSktHy[...]

使用dumpprivkey RPC获取与我们将花费的两个UTXO中使用的公钥相对应的私钥。我们需要私钥,因此我们可以单独签署每个输入。

警告: 用户不应手动管理主网上的私钥。与原生交易一样危险(参见上面的警告),使用私钥进行错误可能会更糟糕,就像HD钱包[跨代密钥泄露] [devguide harddened keys]的情况一样。

这些示例是为了帮助你学习,而不是为了你在mainnet上进行模拟。

1
2
3
4
5
6
7
> bitcoin-cli -regtest getnewaddress
n4puhBEeEWD2VvjdRC9kQuX2abKxSCMNqN
> bitcoin-cli -regtest getnewaddress
n4LWXU59yM5MzQev7Jx7VNeq1BqZ85ZbLj

> NEW_ADDRESS1=n4puhBEeEWD2VvjdRC9kQuX2abKxSCMNqN
> NEW_ADDRESS2=n4LWXU59yM5MzQev7Jx7VNeq1BqZ85ZbLj

对于我们的两个输出,获取两个新地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
## Outputs - inputs = transaction fee, so always double-check your math!
> bitcoin-cli -regtest createrawtransaction '''
[
{
"txid": "'$UTXO1_TXID'",
"vout": '$UTXO1_VOUT'
},
{
"txid": "'$UTXO2_TXID'",
"vout": '$UTXO2_VOUT'
}
]
''' '''
{
"'$NEW_ADDRESS1'": 79.9999,
"'$NEW_ADDRESS2'": 10
}'''
0100000002f327e86da3e66bd20e1129b1fb36d07056f0b9a117199e75939652\
6b8f3a20780000000000fffffffff0ede03d75050f20801d50358829ae02c058\
e8677d2cc74df51f738285013c260000000000ffffffff02f028d6dc01000000\
1976a914ffb035781c3c69e076d48b60c3d38592e7ce06a788ac00ca9a3b0000\
00001976a914fa5139067622fd7e1e722a05c17c2bb7d5fd6df088ac00000000

> RAW_TX=0100000002f327e86da3e66bd20e1129b1fb36d07056f0b9a117199[...]

使用createrawtransaction创建原生交易与以前一样,除了现在我们有两个输入和两个输出。

1
2
3
4
> bitcoin-cli -regtest signrawtransaction $RAW_TX '[]' '''
[
"'$UTXO1_PRIVATE_KEY'"
]'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"hex" : "0100000002f327e86da3e66bd20e1129b1fb36d07056f0b9a11\
7199e759396526b8f3a20780000000049483045022100fce442\
ec52aa2792efc27fd3ad0eaf7fa69f097fdcefab017ea56d179\
9b10b2102207a6ae3eb61e11ffaba0453f173d1792f1b7bb8e7\
422ea945101d68535c4b474801fffffffff0ede03d75050f208\
01d50358829ae02c058e8677d2cc74df51f738285013c260000\
000000ffffffff02f028d6dc010000001976a914ffb035781c3\
c69e076d48b60c3d38592e7ce06a788ac00ca9a3b0000000019\
76a914fa5139067622fd7e1e722a05c17c2bb7d5fd6df088ac0\
0000000",
"complete" : false
"errors": [
{
"txid": "c53f8f5ac0b6b10cdc77f543718eb3880fee6cf9b5e0cbf4edb2a59c0fae09a4",
"vout": 0,
"scriptSig": "",
"sequence": 4294967295,
"error": "Operation not valid with the current stack size"
}
]
}
1
> PARTLY_SIGNED_RAW_TX=0100000002f327e86da3e66bd20e1129b1fb36d07[...]

使用signrawtransaction签署原生交易可以获得更多,我们现在有三个参数:

  • 1.未签名的原始交易。
  • 2.一个空数组。在此操作中,我们不对此参数执行任何操作,但必须提供一些有效的JSON才能访问后面的位置参数。
  • 3.我们想要用来签署其中一个输入的私钥。

结果是原生交易只有一个输入签名; 交易未完全签名的事实由complete JSON字段的值指示。我们将不完整的,部分签名的原生交易hex保存到shell变量中。

1
2
3
4
> bitcoin-cli -regtest signrawtransaction $PARTLY_SIGNED_RAW_TX '[]' '''
[
"'$UTXO2_PRIVATE_KEY'"
]'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"hex" : "0100000002f327e86da3e66bd20e1129b1fb36d07056f0b9a11\
7199e759396526b8f3a20780000000049483045022100fce442\
ec52aa2792efc27fd3ad0eaf7fa69f097fdcefab017ea56d179\
9b10b2102207a6ae3eb61e11ffaba0453f173d1792f1b7bb8e7\
422ea945101d68535c4b474801fffffffff0ede03d75050f208\
01d50358829ae02c058e8677d2cc74df51f738285013c260000\
00006b483045022100b77f935ff366a6f3c2fdeb83589c79026\
5d43b3d2cf5e5f0047da56c36de75f40220707ceda75d8dcf2c\
caebc506f7293c3dcb910554560763d7659fb202f8ec324b012\
102240d7d3c7aad57b68aa0178f4c56f997d1bfab2ded3c2f94\
27686017c603a6d6ffffffff02f028d6dc010000001976a914f\
fb035781c3c69e076d48b60c3d38592e7ce06a788ac00ca9a3b\
000000001976a914fa5139067622fd7e1e722a05c17c2bb7d5f\
d6df088ac00000000",
"complete" : true
}

要签署第二个输入,我们重复使用第二个私钥对第一个输入进行签名的过程。 现在两个输入都已签名,complete结果为true

1
> unset PARTLY_SIGNED_RAW_TX RAW_TX NEW_ADDRESS1 [...]

清理使用的shell变量。与以前的小节不同,我们不会使用sendrawtransaction将此交易发送到连接节点。这将允许我们在下面的离线签名小节中说明如何花费尚未在块链或内存池中的交易。

离线签名

我们现在将花费在上面的复杂原生交易子部分中创建的交易,而不首先将其发送到本地节点。这与钱包程序用于离线签名的基本过程相同,这通常意味着在不访问当前UTXO集的情况下签署交易。

离线签名是安全的。但是,在这个例子中,我们还将花费一个不属于块链的输出,因为包含它的交易从未被广播过。这可能不安全:

警告:从未经证实的交易中获得产出的交易容易受到交易延展性的影响。在主网上花费未经证实的交易之前,请务必阅读有关交易可延展性并采用良好做法的信息。

1
2
3
4
5
6
7
8
9
10
11
12
> OLD_SIGNED_RAW_TX=0100000002f327e86da3e66bd20e1129b1fb36d07056\
f0b9a117199e759396526b8f3a20780000000049483045022100fce442\
ec52aa2792efc27fd3ad0eaf7fa69f097fdcefab017ea56d1799b10b21\
02207a6ae3eb61e11ffaba0453f173d1792f1b7bb8e7422ea945101d68\
535c4b474801fffffffff0ede03d75050f20801d50358829ae02c058e8\
677d2cc74df51f738285013c26000000006b483045022100b77f935ff3\
66a6f3c2fdeb83589c790265d43b3d2cf5e5f0047da56c36de75f40220\
707ceda75d8dcf2ccaebc506f7293c3dcb910554560763d7659fb202f8\
ec324b012102240d7d3c7aad57b68aa0178f4c56f997d1bfab2ded3c2f\
9427686017c603a6d6ffffffff02f028d6dc010000001976a914ffb035\
781c3c69e076d48b60c3d38592e7ce06a788ac00ca9a3b000000001976\
a914fa5139067622fd7e1e722a05c17c2bb7d5fd6df088ac00000000

将先前签名(但未发送)的交易放入shell变量。

1
> bitcoin-cli -regtest decoderawtransaction $OLD_SIGNED_RAW_TX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
{
"txid" : "682cad881df69cb9df8f0c996ce96ecad758357ded2da03bad\
40cf18ffbb8e09",
"hash" : "682cad881df69cb9df8f0c996ce96ecad758357ded2da03bad40cf18ffbb8e09",
"size" : 340,
"vsize" : 340,
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "78203a8f6b529693759e1917a1b9f05670d036fbb1\
29110ed26be6a36de827f3",
"vout" : 0,
"scriptSig" : {
"asm" : "3045022100fce442ec52aa2792efc27fd3ad0ea\
f7fa69f097fdcefab017ea56d1799b10b210220\
7a6ae3eb61e11ffaba0453f173d1792f1b7bb8e\
7422ea945101d68535c4b474801",
"hex" : "483045022100FCE442ec52aa2792efc27fd3ad0\
eaf7fa69f097fdcefab017ea56d1799b10b2102\
207a6ae3eb61e11ffaba0453f173d1792f1b7bb\
8e7422ea945101d68535c4b474801"
},
"sequence" : 4294967295
},
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835\
501d80200f05753de0edf0",
"vout" : 0,
"scriptSig" : {
"asm" : "3045022100b77f935ff366a6f3c2fdeb83589c7\
90265d43b3d2cf5e5f0047da56c36de75f40220\
707ceda75d8dcf2ccaebc506f7293c3dcb91055\
4560763d7659fb202f8ec324b01
02240d7d3c7aad57b68aa0178f4c56f997d1bfa\
b2ded3c2f9427686017c603a6d6",
"hex" : "483045022100b77f935ff366a6f3c2fdeb83589\
c790265d43b3d2cf5e5f0047da56c36de75f402\
20707ceda75d8dcf2ccaebc506f7293c3dcb910\
554560763d7659fb202f8ec324b012102240d7d\
3c7aad57b68aa0178f4c56f997d1bfab2ded3c2\
f9427686017c603a6d6"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 79.99990000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 ffb035781c3c69e076d48\
b60c3d38592e7ce06a7 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914ffb035781c3c69e076d48b60c3d38592e\
7ce06a788ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"n4puhBEeEWD2VvjdRC9kQuX2abKxSCMNqN"
]
}
},
{
"value" : 10.00000000,
"n" : 1,
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 fa5139067622fd7e1e722\
a05c17c2bb7d5fd6df0 OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a914fa5139067622fd7e1e722a05c17c2bb7d\
5fd6df088ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"n4LWXU59yM5MzQev7Jx7VNeq1BqZ85ZbLj"
]
}
}
]
}
1
2
3
4
> UTXO_TXID=682cad881df69cb9df8f0c996ce96ecad758357ded2da03bad40[...]
> UTXO_VOUT=1
> UTXO_VALUE=10.00000000
> UTXO_OUTPUT_SCRIPT=76a914fa5139067622fd7e1e722a05c17c2bb7d5fd6[...]

解码已签名的原始交易,以便我们可以获得它的txid。另外,选择要花费的特定一个UTXO并将UTXO的输出索引号(vout)和hex pubkey脚本(scriptPubKey)保存到shell变量中。

1
2
3
4
> bitcoin-cli -regtest getnewaddress
mfdCHEFL2tW9eEUpizk7XLZJcnFM4hrp78

> NEW_ADDRESS=mfdCHEFL2tW9eEUpizk7XLZJcnFM4hrp78

获取一个新地址来花费satoshis。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## Outputs - inputs = transaction fee, so always double-check your math!
> bitcoin-cli -regtest createrawtransaction '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT'
}
]
''' '''
{
"'$NEW_ADDRESS'": 9.9999
}'''
0100000001098ebbff18cf40ad3ba02ded7d3558d7ca6ee96c990c8fdfb99cf6\
1d88ad2c680100000000ffffffff01f0a29a3b000000001976a914012e2ba6a0\
51c033b03d712ca2ea00a35eac1e7988ac00000000

> RAW_TX=0100000001098ebbff18cf40ad3ba02ded7d3558d7ca6ee96c990c8[...]

创建原生交易的方式与我们在前面的小节中完成的方式相同。

1
> bitcoin-cli -regtest signrawtransaction $RAW_TX
1
2
3
4
5
6
7
{
"hex" : "0100000001098ebbff18cf40ad3ba02ded7d3558d7ca6ee\
96c990c8fdfb99cf61d88ad2c680100000000ffffffff01\
f0a29a3b000000001976a914012e2ba6a051c033b03d712\
ca2ea00a35eac1e7988ac00000000",
"complete" : false
}

尝试在没有任何特殊参数的情况下签署原生交易,这是我们在Simple Raw Transaction子部分中成功签署原生交易的方式。如果你已阅读指南的[交易部分][交易],你可能知道调用失败的原因并保留原始数据交易十六进制不变。

如上所示,获得签名的数据包括来自先前交易的txid和vout。该信息包含在createrawtransaction原生交易中。但是,签名的数据还包括上一个交易中的pubkey脚本,即使它没有出现在unsigned或signed交易中。

在上面的其他原生交易小节中,前一个输出是钱包已知的UTXO集的一部分,因此钱包能够使用txid和输出索引号来查找以前的pubkey脚本并自动插入它。

在这种情况下,你将花费钱包未知的输出,因此它无法自动插入以前的pubkey脚本。

1
2
3
4
5
6
7
8
9
> bitcoin-cli -regtest signrawtransaction $RAW_TX '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT',
"scriptPubKey": "'$UTXO_OUTPUT_SCRIPT'",
"value": '$UTXO_VALUE'
}
]'''
1
2
3
4
5
6
7
8
9
10
11
{
"hex" : "0100000001098ebbff18cf40ad3ba02ded7d3558d7ca6ee96c9\
90c8fdfb99cf61d88ad2c68010000006b483045022100c3f92f\
b74bfa687d76ebe75a654510bb291b8aab6f89ded4fe26777c2\
eb233ad02207f779ce2a181cc4055cb0362aba7fd7a6f72d5db\
b9bd863f4faaf47d8d6c4b500121028e4e62d25760709806131\
b014e2572f7590e70be01f0ef16bfbd51ea5f389d4dffffffff\
01f0a29a3b000000001976a914012e2ba6a051c033b03d712ca\
2ea00a35eac1e7988ac00000000",
"complete" : true
}
1
> SIGNED_RAW_TX=0100000001098ebbff18cf40ad3ba02ded7d3558d7ca6ee9[...]

通过提供先前的pubkey脚本和其他所需的输入数据成功签署交易。

此特定操作通常是脱机签名钱包的功能。

在线钱包创建原生交易并获取所有输入的先前pubkey脚本。用户将此信息提供给脱机钱包。在向用户显示交易细节之后,离线钱包如上所述签署交易。用户将签名的交易带回到在线钱包,该钱包广播它。

1
> bitcoin-cli -regtest sendrawtransaction $SIGNED_RAW_TX
1
{"error": {"code":-22,"message":"TX rejected"}}

在我们广播第一笔交易之前尝试广播第二笔交易。节点拒绝此尝试,因为第二个交易花费的输出不是节点知道的UTXO。

1
2
3
4
> bitcoin-cli -regtest sendrawtransaction $OLD_SIGNED_RAW_TX
682cad881df69cb9df8f0c996ce96ecad758357ded2da03bad40cf18ffbb8e09
> bitcoin-cli -regtest sendrawtransaction $SIGNED_RAW_TX
67d53afa1a8167ca093d30be7fb9dcb8a64a5fdecacec9d93396330c47052c57

广播第一个成功的交易,然后广播第二个交易,现在也成功,因为节点现在看到了UTXO。

1
> bitcoin-cli -regtest getrawmempool
1
2
3
4
[
"67d53afa1a8167ca093d30be7fb9dcb8a64a5fdecacec9d93396330c47052c57",
"682cad881df69cb9df8f0c996ce96ecad758357ded2da03bad40cf18ffbb8e09"
]

我们再次没有生成额外的块,因此上述交易尚未成为regtest块链的一部分。但是,它们是本地节点的内存池的一部分。

1
> unset OLD_SIGNED_RAW_TX SIGNED_RAW_TX RAW_TX [...]

删除旧的shell变量。

P2SH Multisig

在本小节中,我们将创建一个P2SH multisig地址,花费satoshis,然后将那些satoshis从其中转移到另一个地址。

创建multisig地址很容易。Multisig输出有两个参数,最小所需签名数( m)和用于验证这些签名的公钥的number。这被称为m-of-n,在这种情况下,我们将使用2-of-3。

1
2
3
4
5
6
7
8
9
10
> bitcoin-cli -regtest getnewaddress
mhAXF4Eq7iRyvbYk1mpDVBiGdLP3YbY6Dm
> bitcoin-cli -regtest getnewaddress
moaCrnRfP5zzyhW8k65f6Rf2z5QpvJzSKe
> bitcoin-cli -regtest getnewaddress
mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP

> NEW_ADDRESS1=mhAXF4Eq7iRyvbYk1mpDVBiGdLP3YbY6Dm
> NEW_ADDRESS2=moaCrnRfP5zzyhW8k65f6Rf2z5QpvJzSKe
> NEW_ADDRESS3=mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP

生成三个新的P2PKH地址。P2PKH地址不能与下面创建的multisig兑换脚本一起使用。(无论如何都不需要哈希每个公钥,当对兑换脚本进行哈希处理时,所有公钥都受哈希保护。)然而,比特币核心使用地址作为一种方式来引用它知道的基础完整(未哈希)公钥。所以我们得到上面的三个新地址,以便使用他们的公钥。

1
> bitcoin-cli -regtest validateaddress $NEW_ADDRESS3
1
2
3
4
5
6
7
8
9
10
11
12
{
"isvalid" : true,
"address" : "mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP",
"scriptPubKey" : "76a9143172b5654f6683c8fb146959d347ce303cae4ca788ac",
"ismine" : true,
"iswatchonly" : false,
"isscript" : false,
"pubkey" : "029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a\
95c37e148f78c77255",
"iscompressed" : true,
"account" : ""
}
1
> NEW_ADDRESS3_PUBLIC_KEY=029e03a901b85534ff1e92c43c74431f7ce720[...]

回想一下指南,地址中使用的散列公钥会模糊完整的公钥,因此你无法在创建典型的multisig输出或P2SH multisig兑换脚本时将地址提供给其他人或设备。 你必须给他们一个完整的公钥。

使用validateaddress RPC显示其中一个地址的完整(未哈希)公钥。这是实际包含在multisig redeem脚本中的信息。这也是你在创建multisig输出或P2SH multisig兑换脚本时向其他人或设备提供的信息。

我们保存返回shell变量的地址。

1
2
3
4
5
6
> bitcoin-cli -regtest createmultisig 2 '''
[
"'$NEW_ADDRESS1'",
"'$NEW_ADDRESS2'",
"'$NEW_ADDRESS3_PUBLIC_KEY'"
]'''
1
2
3
4
5
6
7
{
"address" : "2N7NaqSKYQUeM8VNgBy8D9xQQbiA8yiJayk",
"redeemScript" : "522103310188e911026cf18c3ce274e0ebb5f95b00\
7f230d8cb7d09879d96dbeab1aff210243930746e6ed6552e03359db521b\
088134652905bd2d1541fa9124303a41e95621029e03a901b85534ff1e92\
c43c74431f7ce72046060fcf7a95c37e148f78c7725553ae"
}
1
2
> P2SH_ADDRESS=2N7NaqSKYQUeM8VNgBy8D9xQQbiA8yiJayk
> P2SH_REDEEM_SCRIPT=522103310188e911026cf18c3ce274e0ebb5f95b007[...]

使用带有两个参数的createmultisig RPC,所需签名的数量(n)以及地址或公钥列表。由于P2PKH地址不能在由此RPC创建的multisig兑换脚本中使用,因此可以提供的唯一地址是属于钱包中的公钥的地址。在这种情况下,我们提供两个地址和一个公钥—所有这些都将在兑换脚本中转换为公钥。

P2SH地址与兑换脚本一起返回,当我们将satoshis发送到P2SH地址时,必须提供该兑换脚本。

警告: 你不能丢失兑换脚本,特别是如果你没有用于创建P2SH多重地址的公钥的记录。你需要兑换脚本才能将任何比特币用于P2SH地址。如果丢失了兑换脚本,则可以通过运行上面的相同命令重新创建它,并使用相同的顺序列出公钥。但是,如果你丢失了兑换脚本甚至其中一个公钥,你将永远无法使用发送到该P2SH地址的satos。

使用createmultisig时,地址和兑换脚本都不会存储在钱包中。要将它们存储在钱包中,请使用addmultisigaddress RPC。如果你向钱包添加地址,则还应进行新备份。

1
2
3
4
> bitcoin-cli -regtest sendtoaddress $P2SH_ADDRESS 10.00
7278d7d030f042ebe633732b512bcb31fff14a697675a1fe1884db139876e175

> UTXO_TXID=7278d7d030f042ebe633732b512bcb31fff14a697675a1fe1884[...]

使用比特币核心支付P2SH multisig地址就像支付更常见的P2PKH地址一样简单。这里我们使用在Simple Spending子部分中使用的相同命令(但是不同的变量)。和以前一样,此命令会自动选择UTXO,如有必要,会创建一个新的P2PKH地址更改输出,并在必要时支付交易费用。

我们将txid保存为shell变量,作为我们计划下一步花费的UTXO的txid。

1
> bitcoin-cli -regtest getrawtransaction $UTXO_TXID 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
"hex" : "0100000001f0ede03d75050f20801d50358829ae02c058e8677\
d2cc74df51f738285013c26010000006a47304402203c375959\
2bf608ab79c01596c4a417f3110dd6eb776270337e575cdafc6\
99af20220317ef140d596cc255a4067df8125db7f349ad94521\
2e9264a87fa8d777151937012102a92913b70f9fb15a7ea5c42\
df44637f0de26e2dad97d6d54957690b94cf2cd05ffffffff01\
00ca9a3b0000000017a9149af61346ce0aa2dffcf697352b4b7\
04c84dcbaff8700000000",
"txid" : "7278d7d030f042ebe633732b512bcb31fff14a697675a1fe18\
84db139876e175",
"hash" : "7278d7d030f042ebe633732b512bcb31fff14a697675a1fe1884db139876e175",
"size" : 189,
"vsize" : 189,
"version" : 1,
"locktime" : 0,
"vin" : [
{
"txid" : "263c018582731ff54dc72c7d67e858c002ae298835\
501d80200f05753de0edf0",
"vout" : 1,
"scriptSig" : {
"asm" : "304402203c3759592bf608ab79c01596c4a417f\
3110dd6eb776270337e575cdafc699af2022031\
7ef140d596cc255a4067df8125db7f349ad9452\
12e9264a87fa8d77715193701
02a92913b70f9fb15a7ea5c42df44637f0de26e\
2dad97d6d54957690b94cf2cd05",
"hex" : "47304402203c3759592bf608ab79c01596c4a41\
7f3110dd6eb776270337e575cdafc699af20220\
317ef140d596cc255a4067df8125db7f349ad94\
5212e9264a87fa8d777151937012102a92913b7\
0f9fb15a7ea5c42df44637f0de26e2dad97d6d5\
4957690b94cf2cd05"
},
"sequence" : 4294967295
}
],
"vout" : [
{
"value" : 10.00000000,
"n" : 0,
"scriptPubKey" : {
"asm" : "OP_HASH160 9af61346ce0aa2dffcf697352b4b\
704c84dcbaff OP_EQUAL",
"hex" : "a9149af61346ce0aa2dffcf697352b4b704c84d\
cbaff87",
"reqSigs" : 1,
"type" : "scripthash",
"addresses" : [
"2N7NaqSKYQUeM8VNgBy8D9xQQbiA8yiJayk"
]
}
}
]
}
1
2
> UTXO_VOUT=0
> UTXO_OUTPUT_SCRIPT=a9149af61346ce0aa2dffcf697352b4b704c84dcbaff87

我们使用getrawtransaction RPC和可选的第二个参数(true)来获取我们刚刚用sendtoaddress创建的解码交易。我们选择其中一个输出作为我们的UTXO并获得其输出索引号(vout)和pubkey脚本(scriptPubKey)。

1
2
3
4
> bitcoin-cli -regtest getnewaddress
mxCNLtKxzgjg8yyNHeuFSXvxCvagkWdfGU

> NEW_ADDRESS4=mxCNLtKxzgjg8yyNHeuFSXvxCvagkWdfGU

我们生成一个新的P2PKH地址,用于我们即将创建的输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## Outputs - inputs = transaction fee, so always double-check your math!
> bitcoin-cli -regtest createrawtransaction '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT'
}
]
''' '''
{
"'$NEW_ADDRESS4'": 9.998
}'''

010000000175e1769813db8418fea17576694af1ff31cb2b512b7333e6eb42f0\
30d0d778720000000000ffffffff01c0bc973b000000001976a914b6f64f5bf3\
e38f25ead28817df7929c06fe847ee88ac00000000

> RAW_TX=010000000175e1769813db8418fea17576694af1ff31cb2b512b733[...]

我们以与Simple Raw Transaction子部分相同的方式生成原生交易。

1
2
3
4
5
6
7
> bitcoin-cli -regtest dumpprivkey $NEW_ADDRESS1
cVinshabsALz5Wg4tGDiBuqEGq4i6WCKWXRQdM8RFxLbALvNSHw7
> bitcoin-cli -regtest dumpprivkey $NEW_ADDRESS3
cNmbnwwGzEghMMe1vBwH34DFHShEj5bcXD1QpFRPHgG9Mj1xc5hq

> NEW_ADDRESS1_PRIVATE_KEY=cVinshabsALz5Wg4tGDiBuqEGq4i6WCKWXRQd[...]
> NEW_ADDRESS3_PRIVATE_KEY=cNmbnwwGzEghMMe1vBwH34DFHShEj5bcXD1Qp[...]

我们获取了用于创建交易的两个公钥的私钥,就像我们在Complex Raw Transaction子部分中获取私钥一样。回想一下,我们创建了一个2-of-3 multisig pubkey脚本,因此需要来自两个私钥的签名。

提醒: 用户不应手动管理主网上的私钥。请参阅[复杂原始事务部分]中的警告。

1
2
3
4
5
6
7
8
9
10
11
12
13
> bitcoin-cli -regtest signrawtransaction $RAW_TX '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT',
"scriptPubKey": "'$UTXO_OUTPUT_SCRIPT'",
"redeemScript": "'$P2SH_REDEEM_SCRIPT'"
}
]
''' '''
[
"'$NEW_ADDRESS1_PRIVATE_KEY'"
]'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"hex" : "010000000175e1769813db8418fea17576694af1ff31cb2b512\
b7333e6eb42f030d0d7787200000000b5004830450221008d5e\
c57d362ff6ef6602e4e756ef1bdeee12bd5c5c72697ef1455b3\
79c90531002202ef3ea04dfbeda043395e5bc701e4878c15baa\
b9c6ba5808eb3d04c91f641a0c014c69522103310188e911026\
cf18c3ce274e0ebb5f95b007f230d8cb7d09879d96dbeab1aff\
210243930746e6ed6552e03359db521b088134652905bd2d154\
1fa9124303a41e95621029e03a901b85534ff1e92c43c74431f\
7ce72046060fcf7a95c37e148f78c7725553aeffffffff01c0b\
c973b000000001976a914b6f64f5bf3e38f25ead28817df7929\
c06fe847ee88ac00000000",
"complete" : false
}
1
> PARTLY_SIGNED_RAW_TX=010000000175e1769813db8418fea17576694af1f[...]

我们做了第一个签名。输入参数(JSON对象)获取额外的redeem脚本参数,以便它可以在两个签名之后将兑换脚本附加到签名脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
> bitcoin-cli -regtest signrawtransaction $PARTLY_SIGNED_RAW_TX '''
[
{
"txid": "'$UTXO_TXID'",
"vout": '$UTXO_VOUT',
"scriptPubKey": "'$UTXO_OUTPUT_SCRIPT'",
"redeemScript": "'$P2SH_REDEEM_SCRIPT'"
}
]
''' '''
[
"'$NEW_ADDRESS3_PRIVATE_KEY'"
]'''
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"hex" : "010000000175e1769813db8418fea17576694af1ff31cb2b512\
b7333e6eb42f030d0d7787200000000fdfd0000483045022100\
8d5ec57d362ff6ef6602e4e756ef1bdeee12bd5c5c72697ef14\
55b379c90531002202ef3ea04dfbeda043395e5bc701e4878c1\
5baab9c6ba5808eb3d04c91f641a0c0147304402200bd8c62b9\
38e02094021e481b149fd5e366a212cb823187149799a68cfa7\
652002203b52120c5cf25ceab5f0a6b5cdb8eca0fd2f386316c\
9721177b75ddca82a4ae8014c69522103310188e911026cf18c\
3ce274e0ebb5f95b007f230d8cb7d09879d96dbeab1aff21024\
3930746e6ed6552e03359db521b088134652905bd2d1541fa91\
24303a41e95621029e03a901b85534ff1e92c43c74431f7ce72\
046060fcf7a95c37e148f78c7725553aeffffffff01c0bc973b\
000000001976a914b6f64f5bf3e38f25ead28817df7929c06fe\
847ee88ac00000000",
"complete" : true
}
1
> SIGNED_RAW_TX=010000000175e1769813db8418fea17576694af1ff31cb2b[...]

这里使用的signrawtransaction调用几乎与上面使用的相同。唯一的区别是使用的私钥。既然已经提供了两个必需的签名,则交易被标记为完成。

1
2
> bitcoin-cli -regtest sendrawtransaction $SIGNED_RAW_TX
430a4cee3a55efb04cbb8718713cab18dea7f2521039aa660ffb5aae14ff3f50

我们将使用P2SH multisig输出的交易发送到接受它的本地节点。

======================================================================

分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程:

  • EOS教程,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
  • java以太坊开发教程,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。
  • python以太坊,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。
  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。
  • 以太坊入门教程,主要介绍智能合约与dapp应用开发,适合入门。
  • 以太坊开发进阶教程,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • C#以太坊,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。
  • java比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。
  • php比特币开发教程,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。
  • tendermint区块链开发详解,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。

汇智网原创翻译,转载请标明出处。这里是原文比特币交易教程