开源 Bitcoin P2P电子货币系统背后的技术(四)
Ok,接着描述交易验证的具体的过程。
涉及的脚本命令(操作符)
首先讲讲涉及到的脚本命令(操作符),描述栈顶项的变化方式如下:(n1 n2 — ) 用 “–”分隔操作符执行前后的栈中元素的变化,左边表示操作前,右边表示操作后。最右边的项表示栈顶元素。这里例子中,n2 表示栈顶,n1表示次栈顶,操作后n1,n2均被弹出。
OP_EQUAL(n1 n2 — true/false): 判断栈顶两项是否相等的脚本命令,如果相等则压入true,否则false
OP_VERIFY(true/false — NOTHING/false): 如果栈顶项为true,标记改交易单为有效,否则失败,压入false并返回
OP_CODESEPARATOR( — ): 将当前执行PC(ProgramCount)保存到变量: pbegincodehash
OP_CHECKSIG(– true/false): 校验收入来源(in)的签名是否有效。栈顶是公开密钥(PubKey),次栈顶是签名(Sig)。如果验证通过会压入True,否则压入False。
OP_EQUALVERIFY(n1 n2 — NOTHING/false): 等于 OP_EQUAL OP_VERIFY 两个操作符的联合。
交易验证
以最常见的接收地址交易转账为例描述交易验证的过程(SendMoneyToBitcoinAddress)
首先示例交易如下:
{
"hash":"23c9e8a3140938a65c73fa0712b1d8614c8eeef486a4ab14107fcce54fbf768c",
"ver":1,
"vin_sz":1,
"vout_sz":2,
"lock_time":0,
"size":258,
"in":[
{
"prev_out":{
"hash":"0740c9ecdef03e46e4c0f74833db5af3cb24c7d48810ef73411662252046f9c6",
"n":0
},
"scriptSig":"304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564c34ef9bc1ddc022100d37c214e978808099fc96006b41244d31b203eb4b1c69be9d57b0e2f843ad93501
0481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6864d9e9c18c9c16e5887c3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b74be07d6"
}
],
"out":[
{
"value":"12.99000000",
"scriptPubKey":"OP_DUP OP_HASH160 ef055cb1e8e192cbd8b6a89ccf1eb73552d9764b OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value":"17.00000000",
"scriptPubKey":"OP_DUP OP_HASH160 923f3cdf648c1755455a17097d22f0be6add7c7c OP_EQUALVERIFY OP_CHECKSIG"
}
]
}
ScriptSig(): 中有两个值,一是当前拥有者的该交易单的签名,二是拥有者的公开密钥(用来验证签名)。
Sig: 304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564
c34ef9bc1ddc022100d37c214e978808099fc96006b41244d31b203eb4b1c69
be9d57b0e2f843ad93501
PubKey:0481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6
864d9e9c18c9c16e5887c3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b7
4be07d6
脚本执行验证过程:
执行 scriptSig:
压入和:
执行 scriptPubKey:
OP_DUP 复制栈顶:
OP_HASH160 对栈顶项做HASH160(RIPEMD-160(SHA256())散列:
: 压入
OP_EQUALVERIFY: 对栈顶两个元素做是否相等校验,如果不相等那么标记交易单无效并返回,如果等于继续:
OP_CHECKSIG: 校验收入来源的In的签名是否有效,根据pubKey去校验签名,签名的内容为交易单的Ins, Outs和脚本(从最近一次的 OP_CODESEPARATOR 到脚本结束的位置)的散列。 PubKey.Verify(SignatureHash(scriptCode, nIn, Outs), Sig)
//nIn 当前的交易单的in的索引号
//scriptCode: 保存的当前执行的代码从OP_CODESEPARATOR(如果没有就从脚本开始) 到脚本结束。
uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
if (nIn >= txTo.vin.size())
{
printf("ERROR: SignatureHash() : nIn=%d out of rangen", nIn);
return 1;
}
CTransaction txTmp(txTo);
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible incompatibilities.
scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
// Blank out other inputs' signatures
for (int i = 0; i < txTmp.vin.size(); i++)
txTmp.vin[i].scriptSig = CScript();
txTmp.vin[nIn].scriptSig = scriptCode;
// Blank out some of the outputs
if ((nHashType & 0x1f) == SIGHASH_NONE)
{
// Wildcard payee
txTmp.vout.clear();
// Let the others update at will
for (int i = 0; i < txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
}
else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
{
// Only lockin the txout payee at same index as txin
unsigned int nOut = nIn;
if (nOut >= txTmp.vout.size())
{
printf("ERROR: SignatureHash() : nOut=%d out of rangen", nOut);
return 1;
}
txTmp.vout.resize(nOut+1);
for (int i = 0; i < nOut; i++)
txTmp.vout[i].SetNull();
// Let the others update at will
for (int i = 0; i < txTmp.vin.size(); i++)
if (i != nIn)
txTmp.vin[i].nSequence = 0;
}
// Blank out other inputs completely, not recommended for open transactions
if (nHashType & SIGHASH_ANYONECANPAY)
{
txTmp.vin[0] = txTmp.vin[nIn];
txTmp.vin.resize(1);
}
// Serialize and hash
CDataStream ss(SER_GETHASH);
ss.reserve(10000);
ss << txTmp << nHashType;
return Hash(ss.begin(), ss.end());
}
转自http://www.showmuch.com/a/20110608/094205.html
作者:李雪愚
Scan QR code with WeChat