Transaction是什么?
我们随意看一个最简单的Transaction,看看什么是Transaction。在block exploper中经过简单的查询一个经典的txid 7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18
,可以看到,他到底是什么。
0100000001524d288f25cada331c298e21995ad070e1d1a0793e818f2f7cfb5f6122ef3e71000000008c493046022100a59e516883459706ac2e6ed6a97ef9788942d3c96a0108f2699fa48d9a5725d1022100f9bb4434943e87901c0c96b5f3af4e7ba7b83e12c69b1edbfe6965f933fcd17d014104e5a0b4de6c09bd9d3f730ce56ff42657da3a7ec4798c0ace2459fb007236bc3249f70170509ed663da0300023a5de700998bfec49d4da4c66288a58374626c8dffffffff0180969800000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000000
上边是一个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
}
一个典型的bitcoin transaction由两部分组成,input和output,input是这笔transaction的输入,output是输出。首先看input,因为bitcoin是utxo model所以一个的输出是另一transaction的输出。仔细看上边的trasaction,input中有一个script,output中另外一个script,那么script是什么呢?
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那么这个钱就能花了。具体来看看是怎么回事。
下边是一个例子是典型的一个P2PKH Transaction的unlocking script + locking script,假设Alice在第n个transaction中,有一笔utxo,然后她在弟n+1笔transaction中将其花出。那么她的unlocking script是在第n+1笔 transaction的input中,locking script是在第n的output中。这一点一定要注意一下。
unlocking + locking在图中的PubkeyHash是Alice的公钥Hash,Sig是Alice的签名,Pubkey是Alice的公钥。所以如果手动运行一下这个script,那么得到的结果是什么呢?
执行结果如果手动运行一下得到的结果是True。所以那么Alice就可以用这笔钱了。所以现在比较明确了为什么output中的script是locking script,因为这个transaction将这个笔钱lock在一个publicKey中,只有你证明你是这个私钥的owner你才能动这笔钱,反过来这个key的owner因为有私钥,通过签名可以解锁这笔钱。所以通过这样的方式把钱lock了起来,Locking script由此得名。Brilliant!
是不是有另一问题?
上边介绍transaction的基本的结构,那么这里有一个问题是,那么签名到底签的是什么,如果可以随意签名,例如签名一个hello world,miner如何验证呢?第二如果签名数据可以是一个任意的数据那么如果hacker or other任意修改locking script呢?本来是给A的,hacker or others给了B?这样是否可能?或者如何应对这个问题?
第一个问题,首先签名签的是transaction 或者说是transaction的一部分 or hash,这样miner就可以知道如何验证签名了。
第二问题,这就引出了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只签一个 |
另外还有其他的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)
网友评论