Transaction是什么墓造?
我們隨意看一個(gè)最簡(jiǎn)單的Transaction昆箕,看看什么是Transaction晰韵。在block exploper中經(jīng)過(guò)簡(jiǎn)單的查詢一個(gè)經(jīng)典的txid 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
惹挟,可以看到蒲凶,他到底是什么气筋。
0100000001524d288f25cada331c298e21995ad070e1d1a0793e818f2f7cfb5f6122ef3e71000000008c493046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d014104e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8dffffffff0180969800000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000000
上邊是一個(gè)Transaction的hex邀泉,但是到底是什么意思呢豪墅?如何將其decode,可以看的比較明白运怖。
{
hash 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
inputs
{
input
{
address_hash 54a28c6ba2bebdb694fe487a87e3e8ed4eab1502
previous_output
{
hash 713eef22615ffb7c2f8f813e79a0d1e170d05a99218e291c33daca258f284d52
index 0
}
script "[3046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d01] [04e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8d]"
sequence 4294967295
}
}
lock_time 0
outputs
{
output
{
address_hash 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8
script "dup hash160 [7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8] equalverify checksig"
value 10000000
}
}
version 1
}
一個(gè)典型的bitcoin transaction由兩部分組成灵巧,input和output搀矫,input是這筆transaction的輸入,output是輸出刻肄。首先看input瓤球,因?yàn)閎itcoin是utxo model所以一個(gè)的輸出是另一transaction的輸出。仔細(xì)看上邊的trasaction肄方,input中有一個(gè)script冰垄,output中另外一個(gè)script,那么script是什么呢?
input中的script是unlocking script虹茶, output中的script是locking script逝薪。為什么說(shuō)input中的script是unlocking script呢?因?yàn)樗墙怄i上一筆transaction的output蝴罪。也就是說(shuō)礦工在verify transaction的時(shí)候董济,會(huì)找到utxo(上一筆交易的output中的locking script)如果unlocking script + locking script 返回的結(jié)果是true那么這個(gè)錢就能花了。具體來(lái)看看是怎么回事要门。
下邊是一個(gè)例子是典型的一個(gè)P2PKH Transaction的unlocking script + locking script虏肾,假設(shè)Alice在第n個(gè)transaction中,有一筆utxo欢搜,然后她在弟n+1筆transaction中將其花出封豪。那么她的unlocking script是在第n+1筆 transaction的input中,locking script是在第n的output中炒瘟。這一點(diǎn)一定要注意一下吹埠。
在圖中的PubkeyHash是Alice的公鑰Hash,Sig是Alice的簽名疮装,Pubkey是Alice的公鑰缘琅。所以如果手動(dòng)運(yùn)行一下這個(gè)script,那么得到的結(jié)果是什么呢廓推?
如果手動(dòng)運(yùn)行一下得到的結(jié)果是True刷袍。所以那么Alice就可以用這筆錢了。所以現(xiàn)在比較明確了為什么output中的script是locking script樊展,因?yàn)檫@個(gè)transaction將這個(gè)筆錢lock在一個(gè)publicKey中呻纹,只有你證明你是這個(gè)私鑰的owner你才能動(dòng)這筆錢,反過(guò)來(lái)這個(gè)key的owner因?yàn)橛兴借€专缠,通過(guò)簽名可以解鎖這筆錢居暖。所以通過(guò)這樣的方式把錢lock了起來(lái),Locking script由此得名藤肢。Brilliant!
是不是有另一問(wèn)題糯景?
上邊介紹transaction的基本的結(jié)構(gòu)嘁圈,那么這里有一個(gè)問(wèn)題是,那么簽名到底簽的是什么蟀淮,如果可以隨意簽名最住,例如簽名一個(gè)hello world,miner如何驗(yàn)證呢怠惶?第二如果簽名數(shù)據(jù)可以是一個(gè)任意的數(shù)據(jù)那么如果hacker or other任意修改locking script呢涨缚?本來(lái)是給A的,hacker or others給了B策治?這樣是否可能脓魏?或者如何應(yīng)對(duì)這個(gè)問(wèn)題兰吟?
第一個(gè)問(wèn)題,首先簽名簽的是transaction 或者說(shuō)是transaction的一部分 or hash茂翔,這樣miner就可以知道如何驗(yàn)證簽名了混蔼。
第二問(wèn)題,這就引出了sign的type珊燎。bitcoin中的sign hash Type有這幾種惭嚣。
SIGHASH flag | Value | Description | 類比 |
---|---|---|---|
ALL | 0x01 | Signature applies to all inputs and outputs | 基本類型,from 和 to都簽名 |
NONE | 0x02 | Signature applies to all inputs, none of the outputs | 空白支票悔政,收款人隨便填 |
SINGLE | 0x03 | Signature applies to all inputs, none of the outputs | inputs全簽晚吞,output只簽一個(gè) |
另外還有其他的modifier
SIGHASH flag | Value | Description | 類比 |
---|---|---|---|
ALL_ANYONECANPAY | 0x81 | Signature applies to one input and all outputs | 募資transaction,outputs鎖定 |
NONE_ANYONECANPAY | 0x82 | Signature applies to one input, none of the outputs | 空白支票谋国,output隨便 |
SINGLE_ANYONECANPAY | 0x83 | Signature applies to one input and the output with the same index number | inputs全簽槽地,output只簽一個(gè) |
一般說(shuō)最近基本的transaction sign hash type 就是ALL,看一下具體的代碼中的實(shí)現(xiàn)。
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== network)
throw new TypeError('Inconsistent network');
if (!inputs[vin]) throw new Error('No input at index: ' + vin);
hashType = hashType || Transaction.SIGHASH_ALL;
if (needsOutputs(hashType)) throw new Error('Transaction needs outputs');
const input = inputs[vin];
// if redeemScript was previously provided, enforce consistency
if (
input.redeemScript !== undefined &&
redeemScript &&
!input.redeemScript.equals(redeemScript)
) {
throw new Error('Inconsistent redeemScript');
}
const txTmp = this.clone();
// SIGHASH_NONE: ignore all outputs? (wildcard payee)
if ((hashType & 0x1f) === Transaction.SIGHASH_NONE) {
txTmp.outs = [];
// ignore sequence numbers (except at inIndex)
txTmp.ins.forEach((input, i) => {
if (i === inIndex) return;
input.sequence = 0;
});
// SIGHASH_SINGLE: ignore all outputs, except at the same index?
} else if ((hashType & 0x1f) === Transaction.SIGHASH_SINGLE) {
// https://github.com/bitcoin/bitcoin/blob/master/src/test/sighash_tests.cpp#L60
if (inIndex >= this.outs.length) return ONE;
// truncate outputs after
txTmp.outs.length = inIndex + 1;
// "blank" outputs before
for (let i = 0; i < inIndex; i++) {
txTmp.outs[i] = BLANK_OUTPUT;
}
// ignore sequence numbers (except at inIndex)
txTmp.ins.forEach((input, y) => {
if (y === inIndex) return;
input.sequence = 0;
});
}
// SIGHASH_ANYONECANPAY: ignore inputs entirely?
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
txTmp.ins = [txTmp.ins[inIndex]];
txTmp.ins[0].script = ourScript;
// SIGHASH_ALL: only ignore input scripts, leave all the outputs
} else {
// "blank" others input scripts
txTmp.ins.forEach(input => {
input.script = EMPTY_SCRIPT;
});
txTmp.ins[inIndex].script = ourScript;
}
// serialize and hash
const buffer: Buffer = Buffer.allocUnsafe(txTmp.__byteLength(false) + 4);
buffer.writeInt32LE(hashType, buffer.length - 4);
txTmp.__toBuffer(buffer, 0, false);
private __byteLength(_ALLOW_WITNESS: boolean): number {
const hasWitnesses = _ALLOW_WITNESS && this.hasWitnesses();
return (
(hasWitnesses ? 10 : 8) +
varuint.encodingLength(this.ins.length) +
varuint.encodingLength(this.outs.length) +
this.ins.reduce((sum, input) => {
return sum + 40 + varSliceSize(input.script);
}, 0) +
this.outs.reduce((sum, output) => {
return sum + 8 + varSliceSize(output.script);
}, 0) +
(hasWitnesses
? this.ins.reduce((sum, input) => {
return sum + vectorSize(input.witness);
}, 0)
: 0)
);
}
上述的的代碼實(shí)現(xiàn)中烹卒,可以看出默認(rèn)的hashType是ALL闷盔,所以上邊提的問(wèn)題,就可以通過(guò)這樣的方式解決旅急,但是這么看hash Type中各種形式可以用于實(shí)現(xiàn)不同的功能逢勾。
最后看看unlocking scirpt的中簽名是什么。
3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e381301
簽名是DER格式的藐吮。
0x30—indicating the start of a DER sequence
0x45—the length of the sequence (69 bytes)
0x02—an integer value follows
0x21—the length of the integer (33 bytes)
R—00884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb
0x02—another integer follows
0x20—the length of the integer (32 bytes)
S—4b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813
A suffix (0x01) indicating the type of hash used (SIGHASH_ALL)