咱們隨意看一個最簡單的Transaction,看看什麼是Transaction。在block exploper中通過簡單的查詢一個經典的txid 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
,能夠看到,他究竟是什麼。git
0100000001524d288f25cada331c298e21995ad070e1d1a0793e818f2f7cfb5f6122ef3e71000000008c493046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d014104e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8dffffffff0180969800000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000000
複製代碼
上邊是一個Transaction的hex,可是究竟是什麼意思呢?如何將其decode,能夠看的比較明白。github
{
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
}
複製代碼
一個典型的bitcoin transaction由兩部分組成,input和output,input是這筆transaction的輸入,output是輸出。首先看input,由於bitcoin是utxo model因此一個的輸出是另外一transaction的輸出。仔細看上邊的trasaction,input中有一個script,output中另一個script,那麼script是什麼呢?bash
input中的script是unlocking script, output中的script是locking script。爲何說input中的script是unlocking script呢?由於他是解鎖上一筆transaction的output。也就是說礦工在verify transaction的時候,會找到utxo(上一筆交易的output中的locking script)若是unlocking script + locking script 返回的結果是true那麼這個錢就能花了。具體來看看是怎麼回事。app
下邊是一個例子是典型的一個P2PKH Transaction的unlocking script + locking script,假設Alice在第n個transaction中,有一筆utxo,而後她在第n+1筆transaction中將其花出。那麼她的unlocking script是在第n+1筆 transaction的input中,locking script是在第n的output中。這一點必定要注意一下。ide
在圖中的PubkeyHash是Alice的公鑰Hash,Sig是Alice的簽名,Pubkey是Alice的公鑰。因此若是手動運行一下這個script,那麼獲得的結果是什麼呢?ui
若是手動運行一下獲得的結果是True。因此那麼Alice就能夠用這筆錢了。因此如今比較明確了爲何output中的script是locking script,由於這個transaction將這個筆錢lock在一個publicKey中,只有你證實你是這個私鑰的owner你才能動這筆錢,反過來這個key的owner由於有私鑰,經過簽名能夠解鎖這筆錢。因此經過這樣的方式把錢lock了起來,Locking script由此得名。Brilliant!this
上邊介紹transaction的基本的結構,那麼這裏有一個問題是,那麼簽名到底籤的是什麼,若是能夠隨意簽名,例如簽名一個hello world,miner如何驗證呢?第二若是簽名數據能夠是一個任意的數據那麼若是hacker or other任意修改locking script呢?原本是給A的,hacker or others給了B?這樣是否可能?或者如何應對這個問題?spa
第一個問題,首先簽名籤的是transaction 或者說是transaction的一部分 or hash,這樣miner就能夠知道如何驗證簽名了。.net
第二問題,這就引出了sign的type。bitcoin中的sign hash Type有這幾種。3d
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只籤一個 |
另外還有其餘的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只籤一個 |
通常說最近基本的transaction sign hash type 就是ALL,看一下具體的代碼中的實現。
// 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)
);
}
複製代碼
上述的的代碼實現中,能夠看出默認的hashType是ALL,因此上邊提的問題,就能夠經過這樣的方式解決,可是這麼看hash Type中各類形式能夠用於實現不一樣的功能。
最後看看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)