Bitcoin使用的是UTXO模式,一筆交易得包含至少一個(gè)vin和一個(gè)vout,還沒有被使用的vout稱為UTXO(unspent transaction output),只要你能夠解鎖UTXO中的scriptPubKey,你就能消費(fèi)其中的余額。
script
scriptPubKey不是簡單的就是一個(gè)公鑰,而是一段script代碼,它決定了消費(fèi)這筆錢的條件。它由bitcoin規(guī)定的script words組成。[1]前面提到的“解鎖scriptPubKey”指的是“你能給scriptPubKey傳入數(shù)值,使它返回的結(jié)果為true”。
不同的script可以支持不同的應(yīng)用場景,比如常用的P2PKH、P2SH交易,前者用于支付給個(gè)人,后者使用多重簽名,需要多個(gè)人同時(shí)簽名才能消費(fèi)。他們的scriptPubKey分別是這樣的:
- P2PKH:
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG - P2SH:
OP_HASH160 <scriptHash> OP_EQUAL
一點(diǎn)代碼的改變,就可以創(chuàng)建完全不同的邏輯,決定需要滿足什么條件才能消費(fèi)這筆錢。這也是bitcoin script的強(qiáng)大之處。通過編寫script,甚至可以支持側(cè)鏈、閃電網(wǎng)絡(luò)。
P2PKH交易
P2PKH(pay to pulic key hash)是最常見的交易形式,用于支付給個(gè)人,它的script的形式是這樣的:
scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>
它用到的命令OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG在 https://en.bitcoin.it/wiki/Script 的對照表中都可以找到對應(yīng)的意義。比如OP_HASH160表示執(zhí)行兩次hash運(yùn)算,依次用SHA-256、RIPEMD-160算法。

地址
scriptPubKey的<pubKeyHash>可以看作是P2PKH交易的地址,也就是說接收轉(zhuǎn)賬的時(shí)候需要告訴對方的是自己的<pubKeyHash>(等于RIPEMD-160(SHA-256(public key))),而不是public key本身。
為了迎合人的習(xí)慣,減少地址的長度,可以對地址進(jìn)行Base58編碼。相比Base64編碼,Base58編碼不使用(0, O, l, I),因?yàn)槿巳菀赘慊煜?。然后還會(huì)在地址前面加上地址使用的規(guī)范的版本號,以實(shí)現(xiàn)向后兼容。編碼后的地址是給人看的,計(jì)算機(jī)處理的時(shí)候會(huì)先解碼。
生成地址的過程[2]:

舉例
比如有一筆交易,Alice轉(zhuǎn)出500個(gè)BTC給Bob。使用命令bitcoin-cli getrawtransaction txid 1查看交易的完整信息,它包含了交易的scriptSig, scriptPubKey。(末尾加上1,表示將返回可讀形式的結(jié)果,否則得到是hex格式。)
$ getrawtransaction 963e9c337a8d7277194313abd2df70646e5cc1c8d7e36ebf2acf5d8fd66e10dc 1
返回結(jié)果包括vin和vout
vin
vin需要指定它UTXO的txid,和用于解鎖這個(gè)UTXO的scriptSig。
"vin": [
{
"txid": "75ff1170e017ba20bb20a9bf2f3c7cd4390a72f1d5eb4af35cbd8851aa303fb6",
"vout": 1,
"scriptSig": {
"asm": "0014bd392459da24fbbd84beb55604f2e9a0d9524b1e",
"hex": "160014bd392459da24fbbd84beb55604f2e9a0d9524b1e"
},
"txinwitness": [
"3044022003b07eafbddef7e8d36b1591b39166fc01810e0490d7fbe88cb77e5cc38cf75c0220015db647488ecb83a1ab467880698ac7f32beb74c3ae3c1bab14fc0e9fade68601",
"022058a9a3a8e1415b7db0aca473c1df5d47634a0da44e4c25dc2a14739d189ee4"
],
"sequence": 4294967293
}
],
vout
vout需要指定轉(zhuǎn)賬金額,和這些金額的解鎖條件scriptPubKey。
"vout": [
{
"value": 500.00000000,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 e7c1345fc8f87c68170b3aa798a956c2fe6a9eff OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914e7c1345fc8f87c68170b3aa798a956c2fe6a9eff88ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": [
"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi"
]
}
},
...
],
Alice轉(zhuǎn)賬500BTC給Bob后,Bob創(chuàng)建了新的交易,轉(zhuǎn)了一些BTC給Carol。Bob的這筆轉(zhuǎn)賬交易的vin需要包含scriptSig,P2PKH交易的scriptSig的格式是<sig> <pubKey>,即公鑰加電子簽名。
礦工收到交易后,會(huì)對其進(jìn)行驗(yàn)證。驗(yàn)證內(nèi)容是:前一筆交易(Alice轉(zhuǎn)賬給Bob)的vout中scriptPubKey,和新交易(Bob轉(zhuǎn)賬給Carol)的vin中的scriptSig。
- 先將scriptSig添加到scriptPubKey前面,拼起來,得到
<Sig> <PubKey> OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG。 - 對這個(gè)拼接得到的script進(jìn)行運(yùn)算,如果結(jié)果為true,則通過驗(yàn)證。[3]
script的運(yùn)算過程
<Sig> <PubKey> OP_DUP OP_HASH160 <PubkeyHash> OP_EQUALVERIFY OP_CHECKSIG
script從左到右一步一步,基于棧模式執(zhí)行:

-
<Sig>: 在棧中存入<Sig> -
<PubKey>: 再存入<PubKey> -
OP_DUP:復(fù)制一份<PubKey>添加到棧(因?yàn)樽詈笠徊絆P_CHECKSIG要用到)` -
OP_HASH160:計(jì)算<PubKey>的hash,并替換之前復(fù)制得到的<PubKey> -
<PubkeyHash>:存入<PubkeyHash> -
OP_EQUALVERIFY:比較前面兩個(gè)hash是否相等,如果相等就繼續(xù)執(zhí)行,否則退出 -
OP_CHECKSIG:使用<PubKey>檢驗(yàn)<Sig>,只有由<PubKey>對應(yīng)的私鑰創(chuàng)建的<Sig>才能通過檢驗(yàn)。因?yàn)橹挥蠦ob有這個(gè)私鑰,所以只有他創(chuàng)建的<Sig>才能通過檢驗(yàn),從而也就只有它能消費(fèi)這個(gè)UTXO。
Q&A
<Sig>是對什么內(nèi)容的簽名?這個(gè)問題很重要,直接決定有些layer2的算法能否基于Bitcoin實(shí)現(xiàn)。簽名內(nèi)容有多種情況, bitcoin.org文檔 有詳細(xì)介紹。
P2PKH相比P2PK有什么優(yōu)勢嗎,為什么要多加個(gè)hash環(huán)節(jié)?縮短交易地址,破解難度更大。
P2SH(pay to script hash)的地址是script的hash,具體是怎么實(shí)現(xiàn)multisig的?mutlsig 可以脫離P2SH單獨(dú)實(shí)現(xiàn),原理和P2PK差不多,只是需要多驗(yàn)證幾個(gè)簽名?;赑2SH實(shí)現(xiàn)的multisig更加靈活。詳情見"Bitcoin Development Reference"的4.3.4 Multisig部分。
參考
-
"Mastering Bitcoin" 的 Bitcoin Address 章節(jié) ?
-
https://bitcoin.org/en/developer-guide#p2pkh-script-validation ? ?