轉自:http://618.io/2013/10/27/bitcoin-transaction-02/
交易的構造、簽名與廣播
上篇介紹了交易結構、簽名等,為了更直觀的認識比特幣,借助bitcoind演示手動構造並廣播交易的完整過程。
普通交易
1. 找出未花費的幣(unspent output)
通過命令:listunspent [minconf=1] [maxconf=9999999] ["address",...]
列出某個地址未花費的幣(交易),minconf
/maxconf
表示該筆收入交易的確認數范圍,如果需要列出還未確認的交易,需將minconf
設置為0。
執行:
1
|
bitcoind listunspent 0 100 '["1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"]'
|
輸出:
1
2
3
4
5
6
7
8
9
10
11
|
[
{
"
txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"
vout" : 0,
"
address" : "1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3",
"
account" : "",
"
scriptPubKey" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"
amount" : 0.19900000,
"
confirmations" : 1
}
]
|
我們找到該地址的一個未花費交易,位於交易296ea7bf981b4499…9f0a41e589132156的第0個位置。
2. 創建待發送交易
創建待發送交易,由命令:createrawtransaction [{"txid":txid,"vout":n},...] {address:amount,...}
來完成。我們將 0.1BTC發送至 1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x ,並支付 0.0001 BTC做為礦工費。輸入交易的額度為 0.199 ,輸出為 0.1 + 0.0001 = 0.1001 ,那么還剩余: 0.199 - 0.1001 = 0.0989 ,將此作為找零發回給自己。
執行:
1
2
3
|
bitcoind createrawtransaction \
'[{"txid":"296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156","vout":0}]' \
'{"1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x":0.1, "1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3":0.0989}'
|
輸出:
1
|
010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000
|
通過命令:decoderawtransaction <hex string>
,可以將此段十六進制字符串解碼。
執行:
1
|
bitcoind decoderawtransaction '010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000'
|
輸出:
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
|
{
"
txid" : "54f773a3fdf7cb3292fc76b46c97e536348b3a0715886dbfd2f60e115fb3a8f0",
"
version" : 1,
"
locktime" : 0,
"
vin" : [
{
"
txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"
vout" : 0,
"
scriptSig" : {
"
asm" : "",
"
hex" : ""
},
"
sequence" : 4294967295
}
],
"
vout" : [
{
"
value" : 0.10000000,
"
n" : 0,
"
scriptPubKey" : {
"
asm" : "OP_DUP OP_HASH160 fdc7990956642433ea75cabdcc0a9447c5d2b4ee OP_EQUALVERIFY OP_CHECKSIG",
"
hex" : "76a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88ac",
"
reqSigs" : 1,
"
type" : "pubkeyhash",
"
addresses" : [
"1Q8s4qDRbCbFypG5AFNR9tFC57PStkPX1x"
]
}
},
{
"
value" : 0.09890000,
"
n" : 1,
"
scriptPubKey" : {
"
asm" : "OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG",
"
hex" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"
reqSigs" : 1,
"
type" : "pubkeyhash",
"
addresses" : [
"1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"
]
}
}
]
}
|
至此,一個“空白交易”就構造好了,尚未使用私鑰對交易進行簽名,字段scriptSig
是留空的,無簽名的交易是無效的。此時的Tx ID並不是最終的Tx ID,填入簽名后Tx ID會發生變化。
在手動創建交易時,務必注意輸入、輸出的值,非常容易犯錯的是忘記構造找零輸出
(如非必要勿手動構造交易)。曾經有人構造交易時忘記找零,發生了支付 200 BTC 的礦工費的人間慘劇,所幸的是收錄該筆交易的Block由著名挖礦團隊“烤貓(Friedcat)”挖得,該團隊非常厚道的退回了多余費用。
3. 簽名
交易簽名使用命令:
1
2
3
|
signrawtransaction <hex string> \
[{
"txid":txid,"vout":n,"scriptPubKey":hex,"redeemScript":hex},...] [<privatekey1>,...] \
[sighashtype=
"ALL"]
|
- 第一個參數是創建的待簽名交易的十六進制字符串;
- 第二個參數有點類似創建交易時的參數,不過需要多出一個公鑰字段
scriptPubKey
,其他節點驗證交易時是通過公鑰和簽名來完成的,所以要提供公鑰;如果是合成地址,則需要提供redeemScript
; - 第三個參數是即將花費的幣所在地址的私鑰,用來對交易進行簽名,如果該地址私鑰已經導入至bitcoind中,則無需顯式提供;
- 最后一個參數表示簽名類型,在上一篇里,介紹了三種交易簽名類型;
簽名之前需要找到scriptPubKey
,提取輸入交易信息即可獲取(也可以根據其公鑰自行計算),由命令:getrawtransaction <txid> [verbose=0]
完成。
執行:
1
|
bitcoind getrawtransaction 296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156 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
|
{
"
hex" : "01000000010511331f639e974283d3909496787a660583dc88f41598d177e225b5f352314a000000006c493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4ffffffff0160a62f01000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000",
"
txid" : "296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156",
"
version" : 1,
"
locktime" : 0,
"
vin" : [
{
"
txid" : "4a3152f3b525e277d19815f488dc8305667a78969490d38342979e631f331105",
"
vout" : 0,
"
scriptSig" : {
"
asm" : "3046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d401 03cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4",
"
hex" : "493046022100be8c796122ec598295e6dfd6664a20a7e20704a17f76d3d925c9ec421ca60bc1022100cf9f2d7b9f24285f7c119c91f24521e5483f6b141de6ee55658fa70116ee04d4012103cad07f6de0b181891b5291a5bc82b228fe6509699648b0b53556dc0057eeb5a4"
},
"
sequence" : 4294967295
}
],
"
vout" : [
{
"
value" : 0.19900000,
"
n" : 0,
"
scriptPubKey" : {
"
asm" : "OP_DUP OP_HASH160 d6c492056f3f99692b56967a42b8ad44ce76b67a OP_EQUALVERIFY OP_CHECKSIG",
"
hex" : "76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac",
"
reqSigs" : 1,
"
type" : "pubkeyhash",
"
addresses" : [
"1Lab618UuWjLmVA1Q64tHZXcLoc4397ZX3"
]
}
}
],
"
blockhash" : "000000000000000488f18f7659acd85b2bd06a5ed2c4439eea74a8b968d16656",
"
confirmations" : 19,
"
time" : 1383235737,
"
blocktime" : 1383235737
}
|
scriptPubKey
位於”vout”[0]->”scriptPubKey”->”hex”,即: 76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac 。
簽名使用ECDSA算法,對其,“空白交易”簽名之,執行:
1
2
3
|
bitcoind signrawtransaction \
"010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e290000000000ffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000" \
'[{"txid":"296ea7bf981b44999d689853d17fe0ceb852a8a34e68fcd19f0a41e589132156","vout":0,"scriptPubKey":"76a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac"}]'
|
輸出:
1
2
3
4
|
{
"
hex" : "010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000",
"
complete" : true
}
|
簽名后,簽名值會填入上文所述的空字段中,從而得到一個完整的交易。可通過上文介紹的命令decoderawtransaction <hex string>
解碼查看之。
最后一步,就是將其廣播出去,等待網絡傳播至所有節點,約10~60秒廣播至全球節點,取決與你的節點的網絡連接狀況。稍后一些時刻,就會進入Block中。廣播由命令sendrawtransaction <hex string>
來完成。如果沒有運行節點,可以通過公共節點的API進行廣播,例如:blockchain.info/pushtx。
執行:
1
2
|
bitcoind sendrawtransaction \
"010000000156211389e5410a9fd1fc684ea3a852b8cee07fd15398689d99441b98bfa76e29000000008c493046022100f9da4f53a6a4a8317f6e7e9cd9a7b76e0f5e95dcdf70f1b1e2b3548eaa3a6975022100858d48aed79da8873e09b0e41691f7f3e518ce9a88ea3d03f7b32eb818f6068801410477c075474b6798c6e2254d3d06c1ae3b91318ca5cc62d18398697208549f798e28efb6c55971a1de68cca81215dd53686c31ad8155cdc03563bf3f73ce87b4aaffffffff0280969800000000001976a914fdc7990956642433ea75cabdcc0a9447c5d2b4ee88acd0e89600000000001976a914d6c492056f3f99692b56967a42b8ad44ce76b67a88ac00000000"
|
輸出:
1
|
b5f8da1ea9e02ec3cc0765f9600f49945e94ed4b0c88ed0648896bf3e213205d
|
返回的是Transaction Hash值,即該交易的ID。至此,交易構造、簽名、發送的完整過程完成了。
合成地址交易
合成地址以3開頭,可以實現多方管理資產,極大提高安全性,也可以輕松實現基於比特幣原生的三方交易擔保支付。一個M-of-N
的模式:
1
|
m {pubkey}
...{pubkey} n OP_CHECKMULTISIG
|
M和N需滿足:
1<=N<=3
1<=M<=N
可以是1 of 1
,1 of 2
,2 of 3
等組合,通常選擇N=3
:
1 of 3
,最大程度私鑰冗余。防丟私鑰損失,3把私鑰中任意一把即可簽名發幣,即使丟失2把都可以保障不受損失;2 of 3
,提高私鑰冗余度的同時解決單點信任問題。3把私鑰任意2把私鑰可簽名發幣,三方不完全信任的情形,即中介交易中,非常適用;3 of 3
,最大程度解決資金信任問題,無私鑰冗余。必須3把私鑰全部簽名才能發幣,適用多方共同管理重要資產,但任何一方遺失私鑰均造成嚴重損失;
合成地址的交易構造、簽名、發送過程與普通交易類似,這里只介紹如何創建一個合成地址。大神Gavin Andresen已經演示過,下面內容摘自其gist.
首先,需要三對公鑰、私鑰。公鑰創建地址、私鑰用於簽名。
1
2
3
4
5
6
|
# No.1
0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86 / 5JaTXbAUmfPYZFRwrYaALK48fN6sFJp4rHqq2QSXs8ucfpE4yQU
# No.2
04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874 / 5Jb7fCeh1Wtm4yBBg3q3XbT6B525i17kVhy3vMC9AqfR6FH2qGk
# No.3
048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213 / 5JFjmGo5Fww9p8gvx48qBYDJNAzR9pmH5S389axMtDyPT8ddqmw
|
使用命令:createmultisig <nrequired> <'["key","key"]'>
來合成,其中key
為公鑰,創建地址時僅需公鑰。創建類型是2 of 3
.
輸入:
1
2
|
bitcoind createmultisig
2 \
'["0491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f86","04865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec6874","048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d46213"]'
|
輸出:
1
2
3
4
|
{
"
address" : "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC",
"
redeemScript" : "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"
}
|
得到的合成地址是:3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC
,該地址沒有公鑰,僅有redeemScript
,作用與公鑰相同。后續的構造、簽名、發送過程與上文普通地址交易類似,略去。