比特幣原始碼情景分析之script指令碼驗證(2)
阿新 • • 發佈:2019-01-04
通過上一篇的分析,我們應該已經對script有了一定的理解,這章節我們以原始碼分析的方式來了解下指令碼驗證執行流程 bitcoin節點在處理一條交易時就需要驗證交易的txin,由於一條交易可能包含多個txin,因而需要執行多個指令碼驗證,自然需要並行化,因而系統允許定義多個指令碼執行執行緒以加速驗證過程。有了這個思考,我們從指令碼執行執行緒出發剝繭抽絲掀開指令碼執行的面紗。 先看指令碼執行執行緒的初始化,這個在init.cpp的AppInitMain裡指令碼驗證執行執行緒 LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { //根據引數建立對應數量的指令碼執行執行緒 for (int i=0; i<nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); }執行緒的具體實現函式在 validataion.cppstatic CCheckQueue<CScriptCheck> scriptcheckqueue(128);void ThreadScriptCheck() { RenameThread("bitcoin-scriptch"); scriptcheckqueue.Thread();}class CCheckQueue{ //! Worker thread void Thread() //CCheckQueue.Thread() { Loop(); } bool Loop(bool fMaster = false) { boost::condition_variable& cond = fMaster ? condMaster : condWorker; std::vector<T> vChecks; vChecks.reserve(nBatchSize); unsigned int nNow = 0; bool fOk = true; do { { …………….. // Decide how many work units to process now. // * Do not try to do everything at once, but aim for increasingly smaller batches so // all workers finish approximately simultaneously. // * Try to account for idle jobs which will instantly start helping. // * Don't do batches smaller than 1 (duh), or larger than nBatchSize. nNow = std::max(1U, std::min(nBatchSize, (unsigned int)queue.size() / (nTotal + nIdle + 1))); vChecks.resize(nNow); for (unsigned int i = 0; i < nNow; i++) { // We want the lock on the mutex to be as short as possible, so swap jobs from the global // queue to the local batch vector instead of copying. // 該執行緒選取一定量的指令碼待執行物件 vChecks[i].swap(queue.back()); queue.pop_back(); } // Check whether we need to do work at all fOk = fAllOk; } // execute work for (T& check : vChecks) if (fOk) //check()函式就是CScriptCheck::operator()() fOk = check(); vChecks.clear(); } while (true); }新增指令碼驗證物件 //發現新塊並處理交易時會驗證指令碼bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck){ AssertLockHeld(cs_main);CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : nullptr); std::vector<CScriptCheck> vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetHash().ToString(), FormatStateMessage(state)); //control負責將某一個具體的驗證執行物件新增到佇列中 control.Add(vChecks);}class CCheckQueueControl{ void Add(std::vector<T>& vChecks) { if (pqueue != nullptr) pqueue->Add(vChecks); }}指令碼驗證執行函式上面可知指令碼驗證執行緒最終會執行CScriptCheck::operatorbool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error);}P2SH指令碼驗證原始碼分析為了更完整的分析該過程,我以最複雜的指令碼模板P2SH為例分析,從上一章接的介紹我們知道,P2SH的指令碼如下:<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG>OP_HASH160 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731 OP_EQUAL這裡的scriptSig是 <Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG>, scriptPubKey是OP_HASH160 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731 OP_EQUALbool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror){ std::vector<std::vector<unsigned char> > stack, stackCopy;//執行解鎖指令碼,執行完後stack中有了<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG> //這三個資料物件 if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror)) // serror is set return false; //解鎖指令碼執行後 if (flags & SCRIPT_VERIFY_P2SH) //由於下面EvalScript會破壞stack,而再後面仍然需要stack當前的資料,因而需要做一次拷貝 stackCopy = stack; //執行鎖定指令碼 OP_HASH160 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731 OP_EQUAL //就是將<2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG>資料做hash160,然後和鎖定指令碼中的hash比較 if (!EvalScript(stack, scriptPubKey, flags, checker, SigVersion::BASE, serror)) // serror is set return false; //這一步結束後,hash160被驗證通過了,此時棧只剩下<Sig1> <Sig2> // Additional validation for spend-to-script-hash transactions: //上一章節我們提到過,P2SH需要執行兩次指令碼驗證,即還有子鎖定指令碼,下面就是在做這個事 if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) { // scriptSig must be literals-only or validation fails if (!scriptSig.IsPushOnly()) return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY); // Restore stack.// hash160驗證後,堆疊只剩下subscript.scriptSig了,我們換需要subscript.scriptPubKey// 恢復前面儲存下來的棧可以做到這一點,恢復後的棧結構. subscript.scriptSig + subscript.scriptPubKey_(_代表被序列化了,文字化了) //即<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG> swap(stack, stackCopy); // stack cannot be empty here, because if it was the // P2SH HASH <> EQUAL scriptPubKey would be evaluated with // an empty stack and the EvalScript above would return false. assert(!stack.empty()); //從棧頂拿出subscript.scriptPubKey的指令碼文字,並構建CScript物件const valtype& pubKeySerialized = stack.back(); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); //到這裡後,已經恢復出完整多簽名子指令碼了<Sig1> <Sig2> 2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG //將subscript.scriptPubKey_文字彈出 //這一步相當於執行evalScript(subscript.scriptSig) popstack(stack); //然後evalScript(subscript.scriptPubKey) //驗證給的pubkey是否能夠被解鎖 if (!EvalScript(stack, pubKey2, flags, checker, SigVersion::BASE, serror)) // serror is set return false; } return set_success(serror);}bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror){ CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; std::vector<bool> vfExec; std::vector<valtype> altstack; try { while (pc < pend) { bool fExec = !count(vfExec.begin(), vfExec.end(), false); //指令碼執行就是不停GetOp然後case處理的過程 if (!script.GetOp(pc, opcode, vchPushValue)) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); // Note how OP_RESERVED does not count towards the opcode limit. if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); if (opcode == OP_CAT || opcode == OP_SUBSTR || ...... opcode == OP_RSHIFT) return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { return set_error(serror, SCRIPT_ERR_MINIMALDATA); } stack.push_back(vchPushValue); } else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) switch (opcode) { // // Push value // case OP_1NEGATE: case OP_1: case OP_2: ..... case OP_15: case OP_16: { // ( -- value) CScriptNum bn((int)opcode - (int)(OP_1 - 1)); stack.push_back(bn.getvch()); // The result of these opcodes should always be the minimal way to push the data // they push, so no need for a CheckMinimalPush here. } break; ….. } catch (...) { return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR); }}從上可知,目前實現的執行多次指令碼驗證不是一個通用方案,而是通過檢測指令碼型別(P2SH)而做的特殊處理,只支援P2SH這個型別的指令碼型別P2SH鎖定指令碼scriptSig生成下面來看看p2sh的scriptSig怎麼生成的bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& fromPubKey, SignatureData& sigdata){ std::vector<valtype> result; txnouttype whichType; //這裡會檢測出fromPubKey是P2SH指令碼,然後就會返回對應的Redeemscript給result當做scriptSig bool solved = SignStep(creator, fromPubKey, result, whichType, SigVersion::BASE); bool P2SH = false; CScript subscript; sigdata.scriptWitness.stack.clear(); //第一次SignStep執行後whichType == TX_SCRIPTHASH //得到的result為Redeemscript的序列化文字 if (solved && whichType == TX_SCRIPTHASH) { // Solver returns the subscript that needs to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: //這裡將Redeemscript文字反序列化為多簽名鎖定指令碼 //2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG subscript = CScript(result[0].begin(), result[0].end());//第二次SignStep會將作為pubKey的subscript解釋為multsig指令碼,然後會返回<Sig1><Sig2>資料到result當做scriptSig solved = solved && SignStep(creator, subscript, result, whichType, SigVersion::BASE) && whichType != TX_SCRIPTHASH; P2SH = true; } //上面result裡不是已經有subscript了啊,為啥要再次push_back呢 //因為SignStep函式每次都會清空result資料,所以需要再次push_back subscript資料 if (P2SH) { result.push_back(std::vector<unsigned char>(subscript.begin(), subscript.end())); } //將真正的scriptSig返回 sigdata.scriptSig = PushAll(result);}static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion){ CScript scriptRet; CScript scriptRet; uint160 h160; ret.clear(); std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; CKeyID keyID; switch (whichTypeRet) { case TX_SCRIPTHASH: //這裡會返回Redeemscript作為scriptSig解鎖指令碼 if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; }}bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const{ LOCK(cs_KeyStore); ScriptMap::const_iterator mi = mapScripts.find(hash); if (mi != mapScripts.end()) { redeemScriptOut = (*mi).second; return true; } return false;}其實,還有一個疑問點,就是怎麼根據P2SH的鎖定指令碼資料找到解鎖指令碼Redeemscript指令碼的呢?P2SH的鎖定指令碼資料有用的資料只有hash值,也即是如何通過hash找到Redeemscript指令碼的呢?仔細看的話,應該能看出一些端倪,就是mapScripts維護了一個hash和具體Redeemscript型別的scriptSig指令碼的map關係.那你可能會說,整個鏈上這麼多P2SH交易,hash就會很多,那這個mapScripts得多大啊。由於P2SH這個hash是<2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG>生成的,所以只有屬於本地節點多賬號簽名生成的P2SH才需要新增到mapScripts.且肯定是先有多簽名賬號才有P2SH交易,因此只需要在建立多賬號簽名的點建立對應的Redeemscript並儲存在mapScripts即可,事實上確實如此,流程如下。UniValue addmultisigaddress(const JSONRPCRequest& request) // Construct using pay-to-script-hash: CScript inner = CreateMultisigRedeemscript(required, pubkeys); pwallet->AddCScript(inner);}// Creates a multisig redeemscript from a given list of public keys and number required.CScript CreateMultisigRedeemscript(const int required, const std::vector<CPubKey>& pubkeys){ CScript result = GetScriptForMultisig(required, pubkeys); return result;}CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys){ CScript script; script << CScript::EncodeOP_N(nRequired); for (const CPubKey& key : keys) script << ToByteVector(key); script << CScript::EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; return script;}bool CBasicKeyStore::AddCScript(const CScript& redeemScript){ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) return error("CBasicKeyStore::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE); LOCK(cs_KeyStore); mapScripts[CScriptID(redeemScript)] = redeemScript; return true;}Solver函式
上面在分析P2SH的scriptSig就提到過Resolver,那Solver函式究竟做啥用的,它是用來解釋scriptPubKey的,比如解釋出scriptPubKey是什麼型別的指令碼,比如分析出判斷一個scriptPubKey是否是P2SH,檢驗資料的合法性並取出bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet){ // Templates static std::multimap<txnouttype, CScript> mTemplates; if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); // Sender provides N pubkeys, receivers provides M signatures mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); } vSolutionsRet.clear(); //解釋scriptPubkey的過程就是將scriptPubkey和模板對比,同時取出裡面的資料比如 // Scan templates const CScript& script1 = scriptPubKey; for (const std::pair<txnouttype, CScript>& tplate : mTemplates) { const CScript& script2 = tplate.second; vSolutionsRet.clear(); opcodetype opcode1, opcode2; std::vector<unsigned char> vch1, vch2; // Compare CScript::const_iterator pc1 = script1.begin(); CScript::const_iterator pc2 = script2.begin(); while (true) { if (pc1 == script1.end() && pc2 == script2.end()) { // Found a match typeRet = tplate.first; if (typeRet == TX_MULTISIG) { // Additional checks for TX_MULTISIG: unsigned char m = vSolutionsRet.front()[0]; unsigned char n = vSolutionsRet.back()[0]; if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) return false; } return true; } if (!script1.GetOp(pc1, opcode1, vch1)) break; if (!script2.GetOp(pc2, opcode2, vch2)) break; // Template matching opcodes: if (opcode2 == OP_PUBKEYS) { while (vch1.size() >= 33 && vch1.size() <= 65) { vSolutionsRet.push_back(vch1); if (!script1.GetOp(pc1, opcode1, vch1)) break; } if (!script2.GetOp(pc2, opcode2, vch2)) break; // Normal situation is to fall through // to other if/else statements } if (opcode2 == OP_PUBKEY) { if (vch1.size() < 33 || vch1.size() > 65) break; vSolutionsRet.push_back(vch1); } else if (opcode2 == OP_PUBKEYHASH) { if (vch1.size() != sizeof(uint160)) break; vSolutionsRet.push_back(vch1); } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly break; } } } vSolutionsRet.clear(); typeRet = TX_NONSTANDARD; return false;}附錄:指令碼指令解釋過程分析 上面在分析道evalScript執行函式時提到指令碼執行就是不停GetOp然後case處理的過程,下面就來分析下GetOp bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { return GetOp2(pc, opcodeRet, nullptr); } bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const { opcodeRet = OP_INVALIDOPCODE; if (pvchRet) pvchRet->clear(); if (pc >= end()) return false; // Read instruction if (end() - pc < 1) return false; //第一位元組為指令 unsigned int opcode = *pc++; // Immediate operand if (opcode <= OP_PUSHDATA4) { unsigned int nSize = 0; //[0x4] sig, 這類指令, opcode就是size if (opcode < OP_PUSHDATA1) { nSize = opcode; } else if (opcode == OP_PUSHDATA1) { if (end() - pc < 1) return false; nSize = *pc++;
} else if (opcode == OP_PUSHDATA2) { if (end() - pc < 2) return false; nSize = ReadLE16(&pc[0]); pc += 2; } else if (opcode == OP_PUSHDATA4) { if (end() - pc < 4) return false; //進一步讀取資料size數值, OP_PUSHDATA4 [xx][xx][xx][xx] <largedata> nSize = ReadLE32(&pc[0]); pc += 4; } if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) return false; if (pvchRet) //讀取真正的資料內容 pvchRet->assign(pc, pc + nSize); pc += nSize; } opcodeRet = static_cast<opcodetype>(opcode); return true; }其他指令碼的scriptSig生成static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion){ CScript scriptRet; uint160 h160; ret.clear(); std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: case TX_NULL_DATA: case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: //P2PK模板keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH://P2PKH keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; creator.Provider().GetPubKey(keyID, vch); ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: //P2SH if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; case TX_MULTISIG: //MS ret.push_back(valtype()); // workaround CHECKMULTISIG bug return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); default: return false; }/********************************* 本文來自CSDN博主"愛踢門"******************************************/
上面在分析P2SH的scriptSig就提到過Resolver,那Solver函式究竟做啥用的,它是用來解釋scriptPubKey的,比如解釋出scriptPubKey是什麼型別的指令碼,比如分析出判斷一個scriptPubKey是否是P2SH,檢驗資料的合法性並取出bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet){ // Templates static std::multimap<txnouttype, CScript> mTemplates; if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); // Sender provides N pubkeys, receivers provides M signatures mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); } vSolutionsRet.clear(); //解釋scriptPubkey的過程就是將scriptPubkey和模板對比,同時取出裡面的資料比如 // Scan templates const CScript& script1 = scriptPubKey; for (const std::pair<txnouttype, CScript>& tplate : mTemplates) { const CScript& script2 = tplate.second; vSolutionsRet.clear(); opcodetype opcode1, opcode2; std::vector<unsigned char> vch1, vch2; // Compare CScript::const_iterator pc1 = script1.begin(); CScript::const_iterator pc2 = script2.begin(); while (true) { if (pc1 == script1.end() && pc2 == script2.end()) { // Found a match typeRet = tplate.first; if (typeRet == TX_MULTISIG) { // Additional checks for TX_MULTISIG: unsigned char m = vSolutionsRet.front()[0]; unsigned char n = vSolutionsRet.back()[0]; if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) return false; } return true; } if (!script1.GetOp(pc1, opcode1, vch1)) break; if (!script2.GetOp(pc2, opcode2, vch2)) break; // Template matching opcodes: if (opcode2 == OP_PUBKEYS) { while (vch1.size() >= 33 && vch1.size() <= 65) { vSolutionsRet.push_back(vch1); if (!script1.GetOp(pc1, opcode1, vch1)) break; } if (!script2.GetOp(pc2, opcode2, vch2)) break; // Normal situation is to fall through // to other if/else statements } if (opcode2 == OP_PUBKEY) { if (vch1.size() < 33 || vch1.size() > 65) break; vSolutionsRet.push_back(vch1); } else if (opcode2 == OP_PUBKEYHASH) { if (vch1.size() != sizeof(uint160)) break; vSolutionsRet.push_back(vch1); } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly break; } } } vSolutionsRet.clear(); typeRet = TX_NONSTANDARD; return false;}附錄:指令碼指令解釋過程分析 上面在分析道evalScript執行函式時提到指令碼執行就是不停GetOp然後case處理的過程,下面就來分析下GetOp bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const { return GetOp2(pc, opcodeRet, nullptr); } bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const { opcodeRet = OP_INVALIDOPCODE; if (pvchRet) pvchRet->clear(); if (pc >= end()) return false; // Read instruction if (end() - pc < 1) return false; //第一位元組為指令 unsigned int opcode = *pc++; // Immediate operand if (opcode <= OP_PUSHDATA4) { unsigned int nSize = 0; //[0x4] sig, 這類指令, opcode就是size if (opcode < OP_PUSHDATA1) { nSize = opcode; } else if (opcode == OP_PUSHDATA1) { if (end() - pc < 1) return false; nSize = *pc++;
} else if (opcode == OP_PUSHDATA2) { if (end() - pc < 2) return false; nSize = ReadLE16(&pc[0]); pc += 2; } else if (opcode == OP_PUSHDATA4) { if (end() - pc < 4) return false; //進一步讀取資料size數值, OP_PUSHDATA4 [xx][xx][xx][xx] <largedata> nSize = ReadLE32(&pc[0]); pc += 4; } if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize) return false; if (pvchRet) //讀取真正的資料內容 pvchRet->assign(pc, pc + nSize); pc += nSize; } opcodeRet = static_cast<opcodetype>(opcode); return true; }其他指令碼的scriptSig生成static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptPubKey, std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion){ CScript scriptRet; uint160 h160; ret.clear(); std::vector<valtype> vSolutions; if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; CKeyID keyID; switch (whichTypeRet) { case TX_NONSTANDARD: case TX_NULL_DATA: case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: //P2PK模板keyID = CPubKey(vSolutions[0]).GetID(); return Sign1(keyID, creator, scriptPubKey, ret, sigversion); case TX_PUBKEYHASH://P2PKH keyID = CKeyID(uint160(vSolutions[0])); if (!Sign1(keyID, creator, scriptPubKey, ret, sigversion)) return false; else { CPubKey vch; creator.Provider().GetPubKey(keyID, vch); ret.push_back(ToByteVector(vch)); } return true; case TX_SCRIPTHASH: //P2SH if (creator.Provider().GetCScript(uint160(vSolutions[0]), scriptRet)) { ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end())); return true; } return false; case TX_MULTISIG: //MS ret.push_back(valtype()); // workaround CHECKMULTISIG bug return (SignN(vSolutions, creator, scriptPubKey, ret, sigversion)); default: return false; }/********************************* 本文來自CSDN博主"愛踢門"******************************************/