Ok,接着描述交易验证的具体的过程。
涉及的脚本命令(操作符)
首先讲讲涉及到的脚本命令(操作符),描述栈顶项的变化方式如下:(n1 n2 — ) 用 “–”分隔操作符执行前后的栈中元素的变化,左边表示操作前,右边表示操作后。最右边的项表示栈顶元素。这里例子中,n2 表示栈顶,n1表示次栈顶,操作后n1,n2均被弹出。OP_EQUAL(n1 n2 — true/false): 判断栈顶两项是否相等的脚本命令,如果相等则压入true,否则falseOP_VERIFY(true/false — NOTHING/false): 如果栈顶项为true,标记改交易单为有效,否则失败,压入false并返回OP_CODESEPARATOR( — ): 将当前执行PC(ProgramCount)保存到变量: pbegincodehashOP_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":"304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564c34ef9bc1ddc022100d37c214e978808099fc96006b41244d31b203eb4b1c69be9d57b0e2f843ad935010481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6864d9e9c18c9c16e5887c3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b74be07d6"}],"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: 304502204028931b310bfec094bbd04275f69beba6bf5f44ab3dbbec9564c34ef9bc1ddc022100d37c214e978808099fc96006b41244d31b203eb4b1c69be9d57b0e2f843ad93501
PubKey:0481f6d0a51bfecb5112f223807ba668c67ac975fb096d07ef65b852a6864d9e9c18c9c16e5887c3f03be519fd42cae52f6d1a05d10a2fa7c783b19e9b74be07d6
脚本执行验证过程:执行 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' signaturesfor (int i = 0; i < txTmp.vin.size(); i++)txTmp.vin[i].scriptSig = CScript();txTmp.vin[nIn].scriptSig = scriptCode;
// Blank out some of the outputsif ((nHashType & 0x1f) == SIGHASH_NONE){// Wildcard payeetxTmp.vout.clear();
// Let the others update at willfor (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 txinunsigned 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 willfor (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 transactionsif (nHashType & SIGHASH_ANYONECANPAY){txTmp.vin[0] = txTmp.vin[nIn];txTmp.vin.resize(1);}
// Serialize and hashCDataStream ss(SER_GETHASH);ss.reserve(10000);ss << txTmp << nHashType;return Hash(ss.begin(), ss.end());}
转自http://www.showmuch.com/a/20110608/094205.html
作者:李雪愚



