X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fscript.cpp;h=e12a9368a06f33957b421094a936506628c904e2;hb=64ada2e23a37f3150898d59509d77dbb6551c475;hp=c0da405b215322cb886082b854282649b86efc6c;hpb=20d8d5f32162f37a10b53bbbe82c913174503ad3;p=novacoin.git diff --git a/src/script.cpp b/src/script.cpp index c0da405..e12a936 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -2,11 +2,6 @@ // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include -#include - -using namespace std; -using namespace boost; #include "script.h" #include "keystore.h" @@ -16,6 +11,8 @@ using namespace boost; #include "sync.h" #include "util.h" +using namespace std; + bool CheckSig(vector vchSig, const vector &vchPubKey, const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, int flags); static const valtype vchFalse(0); @@ -78,13 +75,19 @@ void MakeSameSize(valtype& vch1, valtype& vch2) } - // // Script is a stack machine (like Forth) that evaluates a predicate // returning a bool indicating valid or not. There are no loops. // #define stacktop(i) (stack.at(stack.size()+(i))) + +//static inline valtype stacktop(vector& st, int nDepth) +//{ +// return st.at(st.size()+nDepth); +//} + #define altstacktop(i) (altstack.at(altstack.size()+(i))) + static inline void popstack(vector& stack) { if (stack.empty()) @@ -338,12 +341,89 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return IsDERSignature(vchSig, true, (flags & SCRIPT_VERIFY_LOW_S) != 0); } -bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType) +bool CheckLockTime(const int64_t& nLockTime, const CTransaction &txTo, unsigned int nIn) +{ + // There are two kinds of nLockTime: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nLockTime < LOCKTIME_THRESHOLD. + // + // We want to compare apples to apples, so fail the script + // unless the type of nLockTime being tested is the same as + // the nLockTime in the transaction. + if (!( + (txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || + (txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) + )) + return false; + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nLockTime > (int64_t)txTo.nLockTime) + return false; + + // Finally the nLockTime feature can be disabled and thus + // CHECKLOCKTIMEVERIFY bypassed if every txin has been + // finalized by setting nSequence to maxint. The + // transaction would be allowed into the blockchain, making + // the opcode ineffective. + // + // Testing if this vin is not final is sufficient to + // prevent this condition. Alternatively we could test all + // inputs, but testing just this input minimizes the data + // required to prove correct CHECKLOCKTIMEVERIFY execution. + if (SEQUENCE_FINAL == txTo.vin[nIn].nSequence) + return false; + + return true; +} + +bool CheckSequence(const int64_t& nSequence, const CTransaction &txTo, unsigned int nIn) +{ + // Relative lock times are supported by comparing the passed + // in operand to the sequence number of the input. + const int64_t txToSequence = (int64_t)txTo.vin[nIn].nSequence; + + // Sequence numbers with their most significant bit set are not + // consensus constrained. Testing that the transaction's sequence + // number do not have this bit set prevents using this property + // to get around a CHECKSEQUENCEVERIFY check. + if (txToSequence & SEQUENCE_LOCKTIME_DISABLE_FLAG) + return false; + + // Mask off any bits that do not have consensus-enforced meaning + // before doing the integer comparisons + const uint32_t nLockTimeMask = SEQUENCE_LOCKTIME_TYPE_FLAG | SEQUENCE_LOCKTIME_MASK; + const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; + const int64_t nSequenceMasked = nSequence & nLockTimeMask; + + // There are two kinds of nSequence: lock-by-blockheight + // and lock-by-blocktime, distinguished by whether + // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // + // We want to compare apples to apples, so fail the script + // unless the type of nSequenceMasked being tested is the same as + // the nSequenceMasked in the transaction. + if (!( + (txToSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < SEQUENCE_LOCKTIME_TYPE_FLAG) || + (txToSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= SEQUENCE_LOCKTIME_TYPE_FLAG) + )) { + return false; + } + + // Now that we know we're comparing apples-to-apples, the + // comparison is a simple numeric one. + if (nSequenceMasked > txToSequenceMasked) + return false; + + return true; +} + +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, uint32_t nIn, unsigned int flags, int nHashType) { CAutoBN_CTX pctx; - CScript::const_iterator pc = script.begin(); - CScript::const_iterator pend = script.end(); - CScript::const_iterator pbegincodehash = script.begin(); + auto pc = script.begin(); + auto pend = script.end(); + auto pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; vector vfExec; @@ -385,7 +465,7 @@ bool EvalScript(vector >& stack, const CScript& script, co opcode == OP_RSHIFT) return false; // Disabled opcodes. - if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + if (fExec && opcode <= OP_PUSHDATA4) stack.push_back(vchPushValue); else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) switch (opcode) @@ -412,7 +492,7 @@ bool EvalScript(vector >& stack, const CScript& script, co case OP_16: { // ( -- value) - CBigNum bn((int)opcode - (int)(OP_1 - 1)); + CBigNum bn(opcode - (OP_1 - 1)); stack.push_back(bn.getvch()); } break; @@ -435,7 +515,7 @@ bool EvalScript(vector >& stack, const CScript& script, co { if (stack.size() < 1) return false; - valtype& vch = stacktop(-1); + auto& vch = stacktop(-1); fValue = CastToBool(vch); if (opcode == OP_NOTIF) fValue = !fValue; @@ -486,71 +566,58 @@ bool EvalScript(vector >& stack, const CScript& script, co // CHECKLOCKTIMEVERIFY // // (nLockTime -- nLockTime) - if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) - break; // treat as a NOP is not enabled + if (!(flags & SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY)) { + // treat as a NOP2 if not enabled + break; + } + if (stack.size() < 1) return false; - const CBigNum nLockTime = CastToBigNum(stacktop(-1)); - if (nLockTime < 0) - return false; // Negative argument is senseless. - if (!( // We can have either lock-by-blockheight or lock-by-blocktime. - (txTo.nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || - (txTo.nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) - )) - return false; + auto nLockTime = CastToBigNum(stacktop(-1)); - // Now we can perform a simple numerical comparison - if (nLockTime > (int64_t)txTo.nLockTime) + // In the rare event that the argument may be < 0 due to + // some arithmetic being done first, you can always use + // 0 MAX CHECKLOCKTIMEVERIFY. + if (nLockTime < 0) return false; - // Finally the nLockTime feature can be disabled and thus - // CHECKLOCKTIMEVERIFY bypassed if every txin has been - // finalized by setting nSequence to maxint. The - // transaction would be allowed into the blockchain, making - // the opcode ineffective. - // - // Testing if this vin is not final is sufficient to - // prevent this condition. Alternatively we could test all - // inputs, but testing just this input minimizes the data - // required to prove correct CHECKLOCKTIMEVERIFY execution. - if (txTo.vin[nIn].IsFinal()) + // Actually compare the specified lock time with the transaction. + if (!CheckLockTime(nLockTime.getuint64(), txTo, nIn)) return false; + break; } case OP_CHECKSEQUENCEVERIFY: { - if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) - // treat as a NOP if not enabled + if (!(flags & SCRIPT_VERIFY_CHECKSEQUENCEVERIFY)) { + // treat as a NOP3 not enabled break; + } + if (stack.size() < 1) return false; - const CBigNum nInvSequence = CastToBigNum(stacktop(-1)); + + // nSequence, like nLockTime, is a 32-bit unsigned integer + // field. See the comment in CHECKLOCKTIMEVERIFY regarding + // 5-byte numeric operands. + auto nSequence = CastToBigNum(stacktop(-1)); // In the rare event that the argument may be < 0 due to // some arithmetic being done first, you can always use // 0 MAX CHECKSEQUENCEVERIFY. - if (nInvSequence < 0) - return false; // negative nSequence is senseless - - // Relative lock times are supported by comparing the passed - // in lock time to the sequence number of the input. All other - // logic is the same, all that differs is what we are comparing - // the lock time to. - int64_t txToLockTime = (int64_t)~txTo.vin[nIn].nSequence; - if (txToLockTime >= SEQUENCE_THRESHOLD) + if (nSequence < 0) return false; - if (!( - (txToLockTime < SEQUENCE_THRESHOLD && nInvSequence < SEQUENCE_THRESHOLD) || - (txToLockTime >= SEQUENCE_THRESHOLD && nInvSequence >= SEQUENCE_THRESHOLD) - )) - return false; + // To provide for future soft-fork extensibility, if the + // operand has the disabled lock-time flag set, + // CHECKSEQUENCEVERIFY behaves as a NOP. + if ((nSequence.getint32() & SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + break; - // Now that we know we're comparing apples-to-apples, the - // comparison is a simple numeric one. - if (nInvSequence > txToLockTime) + // Compare the specified sequence number with the input. + if (!CheckSequence(nSequence.getuint64(), txTo, nIn)) return false; break; @@ -592,8 +659,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x1 x2 x1 x2) if (stack.size() < 2) return false; - valtype vch1 = stacktop(-2); - valtype vch2 = stacktop(-1); + auto vch1 = stacktop(-2); + auto vch2 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); } @@ -604,9 +671,9 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) if (stack.size() < 3) return false; - valtype vch1 = stacktop(-3); - valtype vch2 = stacktop(-2); - valtype vch3 = stacktop(-1); + auto vch1 = stacktop(-3); + auto vch2 = stacktop(-2); + auto vch3 = stacktop(-1); stack.push_back(vch1); stack.push_back(vch2); stack.push_back(vch3); @@ -618,8 +685,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) if (stack.size() < 4) return false; - valtype vch1 = stacktop(-4); - valtype vch2 = stacktop(-3); + auto vch1 = stacktop(-4); + auto vch2 = stacktop(-3); stack.push_back(vch1); stack.push_back(vch2); } @@ -630,8 +697,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) if (stack.size() < 6) return false; - valtype vch1 = stacktop(-6); - valtype vch2 = stacktop(-5); + auto vch1 = stacktop(-6); + auto vch2 = stacktop(-5); stack.erase(stack.end()-6, stack.end()-4); stack.push_back(vch1); stack.push_back(vch2); @@ -643,8 +710,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 x3 x4 -- x3 x4 x1 x2) if (stack.size() < 4) return false; - swap(stacktop(-4), stacktop(-2)); - swap(stacktop(-3), stacktop(-1)); + swap(*(stack.end()-4),*(stack.end()-2)); + swap(*(stack.end()-3),*(stack.end()-1)); } break; @@ -653,7 +720,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x - 0 | x x) if (stack.size() < 1) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); if (CastToBool(vch)) stack.push_back(vch); } @@ -681,7 +748,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x -- x x) if (stack.size() < 1) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); stack.push_back(vch); } break; @@ -700,7 +767,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x1 x2 x1) if (stack.size() < 2) return false; - valtype vch = stacktop(-2); + auto vch = stacktop(-2); stack.push_back(vch); } break; @@ -712,11 +779,11 @@ bool EvalScript(vector >& stack, const CScript& script, co // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) if (stack.size() < 2) return false; - int n = CastToBigNum(stacktop(-1)).getint32(); + int n = CastToBigNum(stack.back()).getint32(); popstack(stack); if (n < 0 || n >= (int)stack.size()) return false; - valtype vch = stacktop(-n-1); + auto vch = stacktop(-n-1); if (opcode == OP_ROLL) stack.erase(stack.end()-n-1); stack.push_back(vch); @@ -730,8 +797,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // x2 x3 x1 after second swap if (stack.size() < 3) return false; - swap(stacktop(-3), stacktop(-2)); - swap(stacktop(-2), stacktop(-1)); + swap(*(stack.end()-3), *(stack.end()-2)); + swap(*(stack.end()-2), *(stack.end()-1)); } break; @@ -740,7 +807,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x2 x1) if (stack.size() < 2) return false; - swap(stacktop(-2), stacktop(-1)); + swap(*(stack.end()-2),*(stack.end()-1)); } break; @@ -749,7 +816,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- x2 x1 x2) if (stack.size() < 2) return false; - valtype vch = stacktop(-1); + auto vch = stacktop(-1); stack.insert(stack.end()-2, vch); } break; @@ -760,7 +827,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (in -- in size) if (stack.size() < 1) return false; - CBigNum bn((uint16_t) stacktop(-1).size()); + CBigNum bn((uint16_t) (stack.back()).size()); stack.push_back(bn.getvch()); } break; @@ -776,8 +843,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 - bool) if (stack.size() < 2) return false; - valtype& vch1 = stacktop(-2); - valtype& vch2 = stacktop(-1); + auto& vch1 = stacktop(-2); + auto& vch2 = stacktop(-1); bool fEqual = (vch1 == vch2); // OP_NOTEQUAL is disabled because it would be too easy to say // something like n != 1 and have some wiseguy pass in 1 with extra @@ -811,7 +878,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (in -- out) if (stack.size() < 1) return false; - CBigNum bn = CastToBigNum(stacktop(-1)); + auto bn = CastToBigNum(stacktop(-1)); switch (opcode) { case OP_1ADD: bn += bnOne; break; @@ -844,8 +911,8 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x1 x2 -- out) if (stack.size() < 2) return false; - CBigNum bn1 = CastToBigNum(stacktop(-2)); - CBigNum bn2 = CastToBigNum(stacktop(-1)); + auto bn1 = CastToBigNum(stacktop(-2)); + auto bn2 = CastToBigNum(stacktop(-1)); CBigNum bn; switch (opcode) { @@ -889,9 +956,9 @@ bool EvalScript(vector >& stack, const CScript& script, co // (x min max -- out) if (stack.size() < 3) return false; - CBigNum bn1 = CastToBigNum(stacktop(-3)); - CBigNum bn2 = CastToBigNum(stacktop(-2)); - CBigNum bn3 = CastToBigNum(stacktop(-1)); + auto bn1 = CastToBigNum(stacktop(-3)); + auto bn2 = CastToBigNum(stacktop(-2)); + auto bn3 = CastToBigNum(stacktop(-1)); bool fValue = (bn2 <= bn1 && bn1 < bn3); popstack(stack); popstack(stack); @@ -913,7 +980,7 @@ bool EvalScript(vector >& stack, const CScript& script, co // (in -- hash) if (stack.size() < 1) return false; - valtype& vch = stacktop(-1); + auto& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) RIPEMD160(&vch[0], vch.size(), &vchHash[0]); @@ -923,12 +990,12 @@ bool EvalScript(vector >& stack, const CScript& script, co SHA256(&vch[0], vch.size(), &vchHash[0]); else if (opcode == OP_HASH160) { - uint160 hash160 = Hash160(vch); + auto hash160 = Hash160(vch); memcpy(&vchHash[0], &hash160, sizeof(hash160)); } else if (opcode == OP_HASH256) { - uint256 hash = Hash(vch.begin(), vch.end()); + auto hash = Hash(vch.begin(), vch.end()); memcpy(&vchHash[0], &hash, sizeof(hash)); } popstack(stack); @@ -950,8 +1017,8 @@ bool EvalScript(vector >& stack, const CScript& script, co if (stack.size() < 2) return false; - valtype& vchSig = stacktop(-2); - valtype& vchPubKey = stacktop(-1); + auto& vchSig = stacktop(-2); + auto& vchPubKey = stacktop(-1); ////// debug print //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); @@ -1013,15 +1080,15 @@ bool EvalScript(vector >& stack, const CScript& script, co // Drop the signatures, since there's no way for a signature to sign itself for (int k = 0; k < nSigsCount; k++) { - valtype& vchSig = stacktop(-isig-k); + auto& vchSig = stacktop(-isig-k); scriptCode.FindAndDelete(CScript(vchSig)); } bool fSuccess = true; while (fSuccess && nSigsCount > 0) { - valtype& vchSig = stacktop(-isig); - valtype& vchPubKey = stacktop(-ikey); + auto& vchSig = stacktop(-isig); + auto& vchPubKey = stacktop(-ikey); // Check signature bool fOk = IsCanonicalSignature(vchSig, flags) && IsCanonicalPubKey(vchPubKey, flags) && @@ -1096,11 +1163,11 @@ bool EvalScript(vector >& stack, const CScript& script, co -uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) +uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, uint32_t nIn, int nHashType) { if (nIn >= txTo.vin.size()) { - printf("ERROR: SignatureHash() : nIn=%d out of range\n", nIn); + printf("ERROR: SignatureHash() : nIn=%" PRIu32 " out of range\n", nIn); return 1; } CTransaction txTmp(txTo); @@ -1128,10 +1195,10 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int else if ((nHashType & 0x1f) == SIGHASH_SINGLE) { // Only lock-in the txout payee at same index as txin - unsigned int nOut = nIn; + uint32_t nOut = nIn; if (nOut >= txTmp.vout.size()) { - printf("ERROR: SignatureHash() : nOut=%d out of range\n", nOut); + printf("ERROR: SignatureHash() : nOut=%" PRIu32 " out of range\n", nOut); return 1; } txTmp.vout.resize(nOut+1); @@ -1167,44 +1234,43 @@ class CSignatureCache { private: // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, CPubKey > sigdata_type; - std::set< sigdata_type> setValid; + typedef tuple, CPubKey > sigdata_type; + set< sigdata_type> setValid; boost::shared_mutex cs_sigcache; public: bool - Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + Get(const uint256 &hash, const vector& vchSig, const CPubKey& pubKey) { boost::shared_lock lock(cs_sigcache); sigdata_type k(hash, vchSig, pubKey); - std::set::iterator mi = setValid.find(k); + auto mi = setValid.find(k); if (mi != setValid.end()) return true; return false; } - void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + void Set(const uint256 &hash, const vector& vchSig, const CPubKey& pubKey) { // DoS prevention: limit cache size to less than 10MB // (~200 bytes per cache entry times 50,000 entries) // Since there are a maximum of 20,000 signature operations per block // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + size_t nMaxCacheSize = GetArgUInt("-maxsigcachesize", 50000u); if (nMaxCacheSize <= 0) return; boost::shared_lock lock(cs_sigcache); - while (static_cast(setValid.size()) > nMaxCacheSize) + while (setValid.size() > nMaxCacheSize) { // Evict a random entry. Random because that helps // foil would-be DoS attackers who might try to pre-generate // and re-use a set of valid signatures just-slightly-greater // than our cache size. - uint256 randomHash = GetRandHash(); - std::vector unused; - std::set::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + auto randomHash = GetRandHash(); + vector unused; + auto it = setValid.lower_bound(sigdata_type(randomHash, unused, unused)); if (it == setValid.end()) it = setValid.begin(); setValid.erase(*it); @@ -1220,10 +1286,7 @@ bool CheckSig(vector vchSig, const vector &vchPubK { static CSignatureCache signatureCache; - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; - CPubKey pubkey = key.GetPubKey(); + CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) return false; @@ -1236,12 +1299,12 @@ bool CheckSig(vector vchSig, const vector &vchPubK return false; vchSig.pop_back(); - uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + auto sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - if (!key.Verify(sighash, vchSig)) + if (!pubkey.Verify(sighash, vchSig)) return false; if (!(flags & SCRIPT_VERIFY_NOCACHE)) @@ -1251,12 +1314,6 @@ bool CheckSig(vector vchSig, const vector &vchPubK } - - - - - - // // Return public keys or hashes from scriptPubKey, for 'standard' transaction types. // @@ -1267,22 +1324,19 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector SMALLDATA_SWITCH_TIME) - { - // Malleable pubkey tx hack, sender provides generated pubkey combined with R parameter. The R parameter is dropped before checking a signature. - mTemplates.insert(make_pair(TX_PUBKEY_DROP, CScript() << OP_PUBKEY << OP_PUBKEY << OP_DROP << OP_CHECKSIG)); - } + // Malleable pubkey tx hack, sender provides generated pubkey combined with R parameter. The R parameter is dropped before checking a signature. + mTemplates.insert({ TX_PUBKEY_DROP, CScript() << OP_PUBKEY << OP_PUBKEY << OP_DROP << OP_CHECKSIG }); // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); + mTemplates.insert({ TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG }); // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); + mTemplates.insert({ TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG }); // Empty, provably prunable, data-carrying output - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); + mTemplates.insert({ TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA }); } vSolutionsRet.clear(); @@ -1308,19 +1362,19 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector vch1, vch2; // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - while (true) + auto pc1 = script1.begin(); + auto pc2 = script2.begin(); + for ( ; ; ) { if (pc1 == script1.end() && pc2 == script2.end()) { @@ -1329,8 +1383,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector n || vSolutionsRet.size()-2 != n) return false; } @@ -1383,7 +1437,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector (GetTime() > SMALLDATA_SWITCH_TIME ? 1024 : 80)) + if (vch1.size() > 1024) break; } else if (opcode1 != opcode2 || vch1 != vch2) @@ -1447,10 +1501,10 @@ bool SignN(const vector& multisigdata, const CKeyStore& keystore, const { int nSigned = 0; int nRequired = multisigdata.front()[0]; - for (unsigned int i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) + for (uint32_t i = 1; i < multisigdata.size()-1 && nSigned < nRequired; i++) { - const valtype& pubkey = multisigdata[i]; - CKeyID keyID = CPubKey(pubkey).GetID(); + const auto& pubkey = multisigdata[i]; + auto keyID = CPubKey(pubkey).GetID(); if (Sign1(keyID, keystore, hash, nHashType, scriptSigRet)) ++nSigned; } @@ -1483,8 +1537,8 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, const uint25 return Sign1(keyID, keystore, hash, nHashType, scriptSigRet); case TX_PUBKEY_DROP: { - CPubKey key = CPubKey(vSolutions[0]); - CPubKey R = CPubKey(vSolutions[1]); + auto key = CPubKey(vSolutions[0]); + auto R = CPubKey(vSolutions[1]); return SignR(key, R, keystore, hash, nHashType, scriptSigRet); } case TX_PUBKEYHASH: @@ -1508,7 +1562,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, const uint25 return false; } -int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) +int ScriptSigArgsExpected(txnouttype t, const vector >& vSolutions) { switch (t) { @@ -1554,10 +1608,10 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) { - unsigned int nResult = 0; - BOOST_FOREACH(const valtype& pubkey, pubkeys) + uint32_t nResult = 0; + for(const auto& pubkey : pubkeys) { - CKeyID keyID = CPubKey(pubkey).GetID(); + auto keyID = CPubKey(pubkey).GetID(); if (keystore.HaveKey(keyID)) ++nResult; } @@ -1576,10 +1630,10 @@ public: bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } }; -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +isminetype IsMine(const CKeyStore &keystore, const CBitcoinAddress& dest) { CScript script; - script.SetDestination(dest); + script.SetAddress(dest); return IsMine(keystore, script); } @@ -1606,8 +1660,8 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) break; case TX_PUBKEY_DROP: { - CPubKey key = CPubKey(vSolutions[0]); - CPubKey R = CPubKey(vSolutions[1]); + auto key = CPubKey(vSolutions[0]); + auto R = CPubKey(vSolutions[1]); if (keystore.CheckOwnership(key, R)) return MINE_SPENDABLE; } @@ -1619,10 +1673,10 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) break; case TX_SCRIPTHASH: { - CScriptID scriptID = CScriptID(uint160(vSolutions[0])); + auto scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); + auto ret = IsMine(keystore, subscript); if (ret == MINE_SPENDABLE) return ret; } @@ -1673,21 +1727,57 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) return false; } +bool ExtractAddress(const CKeyStore &keystore, const CScript& scriptPubKey, CBitcoinAddress& addressRet) +{ + vector vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + if (whichType == TX_PUBKEY) + { + addressRet = CBitcoinAddress(CPubKey(vSolutions[0]).GetID()); + return true; + } + if (whichType == TX_PUBKEY_DROP) + { + // Pay-to-Pubkey-R + CMalleableKeyView view; + if (!keystore.CheckOwnership(CPubKey(vSolutions[0]), CPubKey(vSolutions[1]), view)) + return false; + + addressRet = CBitcoinAddress(view.GetMalleablePubKey()); + return true; + } + else if (whichType == TX_PUBKEYHASH) + { + addressRet = CBitcoinAddress(CKeyID(uint160(vSolutions[0]))); + return true; + } + else if (whichType == TX_SCRIPTHASH) + { + addressRet = CBitcoinAddress(CScriptID(uint160(vSolutions[0]))); + return true; + } + // Multisig txns have more than one address... + return false; +} + class CAffectedKeysVisitor : public boost::static_visitor { private: const CKeyStore &keystore; CAffectedKeysVisitor& operator=(CAffectedKeysVisitor const&); - std::vector &vKeys; + vector &vKeys; public: - CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} + CAffectedKeysVisitor(const CKeyStore &keystoreIn, vector &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} void Process(const CScript &script) { txnouttype type; - std::vector vDest; + vector vDest; int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { - BOOST_FOREACH(const CTxDestination &dest, vDest) + for(const CTxDestination &dest : vDest) boost::apply_visitor(*this, dest); } } @@ -1707,7 +1797,7 @@ public: }; -void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector &vKeys) { +void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, vector &vKeys) { CAffectedKeysVisitor(keystore, vKeys).Process(scriptPubKey); } @@ -1747,8 +1837,7 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, - unsigned int flags, int nHashType) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, uint32_t nIn, unsigned int flags, int nHashType) { vector > stack, stackCopy; if (!EvalScript(stack, scriptSig, txTo, nIn, flags, nHashType)) @@ -1774,7 +1863,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // an empty stack and the EvalScript above would return false. assert(!stackCopy.empty()); - const valtype& pubKeySerialized = stackCopy.back(); + const auto& pubKeySerialized = stackCopy.back(); CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stackCopy); @@ -1788,14 +1877,14 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C return true; } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransaction& txTo, uint32_t nIn, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; + auto& txin = txTo.vin[nIn]; // Leave out the signature from the hash, since a signature can't sign itself. // The checksig op will also drop the signatures from its hash. - uint256 hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); + auto hash = SignatureHash(fromPubKey, txTo, nIn, nHashType); txnouttype whichType; if (!Solver(keystore, fromPubKey, hash, nHashType, txin.scriptSig, whichType)) @@ -1806,14 +1895,13 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa // Solver returns the subscript that need to be evaluated; // the final scriptSig is the signatures from that // and then the serialized subscript: - CScript subscript = txin.scriptSig; + auto subscript = txin.scriptSig; // Recompute txn hash using subscript in place of scriptPubKey: - uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + auto hash2 = SignatureHash(subscript, txTo, nIn, nHashType); txnouttype subType; - bool fSolved = - Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; + bool fSolved = Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType) && subType != TX_SCRIPTHASH; // Append serialized subscript whether or not it is completely signed: txin.scriptSig << static_cast(subscript); if (!fSolved) return false; @@ -1823,13 +1911,13 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CTransa return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STRICT_FLAGS, 0); } -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, uint32_t nIn, int nHashType) { assert(nIn < txTo.vin.size()); - CTxIn& txin = txTo.vin[nIn]; + auto& txin = txTo.vin[nIn]; assert(txin.prevout.n < txFrom.vout.size()); assert(txin.prevout.hash == txFrom.GetHash()); - const CTxOut& txout = txFrom.vout[txin.prevout.n]; + const auto& txout = txFrom.vout[txin.prevout.n]; return SignSignature(keystore, txout.scriptPubKey, txTo, nIn, nHashType); } @@ -1837,7 +1925,7 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans static CScript PushAll(const vector& values) { CScript result; - BOOST_FOREACH(const valtype& v, values) + for(const auto& v : values) result << v; return result; } @@ -1848,12 +1936,12 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& { // Combine all the signatures we've got: set allsigs; - BOOST_FOREACH(const valtype& v, sigs1) + for(const auto& v : sigs1) { if (!v.empty()) allsigs.insert(v); } - BOOST_FOREACH(const valtype& v, sigs2) + for(const auto& v : sigs2) { if (!v.empty()) allsigs.insert(v); @@ -1861,14 +1949,15 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& // Build a map of pubkey -> signature by matching sigs to pubkeys: assert(vSolutions.size() > 1); - unsigned int nSigsRequired = vSolutions.front()[0]; - unsigned int nPubKeys = (unsigned int)(vSolutions.size()-2); + auto nSigsRequired = (uint32_t)vSolutions.front()[0]; + auto nPubKeys = (uint32_t)(vSolutions.size()-2); + map sigs; - BOOST_FOREACH(const valtype& sig, allsigs) + for(const auto& sig : allsigs) { for (unsigned int i = 0; i < nPubKeys; i++) { - const valtype& pubkey = vSolutions[i+1]; + const auto& pubkey = vSolutions[i+1]; if (sigs.count(pubkey)) continue; // Already got a sig for this pubkey @@ -1880,9 +1969,9 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& } } // Now build a merged CScript: - unsigned int nSigsHave = 0; + uint32_t nSigsHave = 0; CScript result; result << OP_0; // pop-one-too-many workaround - for (unsigned int i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) + for (uint32_t i = 0; i < nPubKeys && nSigsHave < nSigsRequired; i++) { if (sigs.count(vSolutions[i+1])) { @@ -1891,7 +1980,7 @@ static CScript CombineMultisig(const CScript& scriptPubKey, const CTransaction& } } // Fill any missing with OP_0: - for (unsigned int i = nSigsHave; i < nSigsRequired; i++) + for (auto i = nSigsHave; i < nSigsRequired; i++) result << OP_0; return result; @@ -1924,7 +2013,7 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction else { // Recur to combine: - valtype spk = sigs1.back(); + auto spk = sigs1.back(); CScript pubKey2(spk.begin(), spk.end()); txnouttype txType2; @@ -1932,7 +2021,7 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction Solver(pubKey2, txType2, vSolutions2); sigs1.pop_back(); sigs2.pop_back(); - CScript result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); + auto result = CombineSignatures(pubKey2, txTo, nIn, txType2, vSolutions2, sigs1, sigs2); result << spk; return result; } @@ -1943,7 +2032,7 @@ static CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction return CScript(); } -CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, +CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, uint32_t nIn, const CScript& scriptSig1, const CScript& scriptSig2) { txnouttype txType; @@ -1958,11 +2047,248 @@ CScript CombineSignatures(const CScript& scriptPubKey, const CTransaction& txTo, return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } +// +//CScript +// + +CScript& CScript::push_int64(int64_t n) +{ + if (n == -1 || (n >= 1 && n <= 16)) + { + push_back((uint8_t)n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; +} + +CScript& CScript::push_uint64(uint64_t n) +{ + if (n >= 1 && n <= 16) + { + push_back((uint8_t)n + (OP_1 - 1)); + } + else + { + CBigNum bn(n); + *this << bn.getvch(); + } + return *this; +} + +CScript& CScript::operator+=(const CScript& b) +{ + insert(end(), b.begin(), b.end()); + return *this; +} + +CScript& CScript::operator<<(opcodetype opcode) +{ + insert(end(), opcode); + return *this; +} + +CScript& CScript::operator<<(const uint160& b) +{ + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; +} + +CScript& CScript::operator<<(const uint256& b) +{ + insert(end(), sizeof(b)); + insert(end(), (uint8_t*)&b, (uint8_t*)&b + sizeof(b)); + return *this; +} + +CScript& CScript::operator<<(const CPubKey& key) +{ + vector vchKey(key.begin(), key.end()); + return (*this) << vchKey; +} + +CScript& CScript::operator<<(const CBigNum& b) +{ + *this << b.getvch(); + return *this; +} + +CScript& CScript::operator<<(const vector& b) +{ + if (b.size() < OP_PUSHDATA1) + { + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xff) + { + insert(end(), OP_PUSHDATA1); + insert(end(), (uint8_t)b.size()); + } + else if (b.size() <= 0xffff) + { + insert(end(), OP_PUSHDATA2); + uint16_t nSize = (uint16_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&nSize + sizeof(nSize)); + } + else + { + insert(end(), OP_PUSHDATA4); + uint32_t nSize = (uint32_t) b.size(); + insert(end(), (uint8_t*)&nSize, (uint8_t*)&nSize + sizeof(nSize)); + } + insert(end(), b.begin(), b.end()); + return *this; +} + +CScript& CScript::operator<<(const CScript& b) +{ + // I'm not sure if this should push the script or concatenate scripts. + // If there's ever a use for pushing a script onto a script, delete this member fn + assert(!"Warning: Pushing a CScript onto a CScript with << is probably not intended, use + to concatenate!"); + return *this; +} + +bool CScript::GetOp(iterator& pc, opcodetype& opcodeRet, vector& vchRet) +{ + // Wrapper so it can be called with either iterator or const_iterator + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, &vchRet); + pc = begin() + (pc2 - begin()); + return fRet; +} + +bool CScript::GetOp(iterator& pc, opcodetype& opcodeRet) +{ + const_iterator pc2 = pc; + bool fRet = GetOp2(pc2, opcodeRet, NULL); + pc = begin() + (pc2 - begin()); + return fRet; +} + +bool CScript::GetOp(const_iterator& pc, opcodetype& opcodeRet, vector& vchRet) const +{ + return GetOp2(pc, opcodeRet, &vchRet); +} + +bool CScript::GetOp(const_iterator& pc, opcodetype& opcodeRet) const +{ + return GetOp2(pc, opcodeRet, NULL); +} + +bool CScript::GetOp2(const_iterator& pc, opcodetype& opcodeRet, vector* pvchRet) const +{ + opcodeRet = OP_INVALIDOPCODE; + if (pvchRet) + pvchRet->clear(); + if (pc >= end()) + return false; + + // Read instruction + if (end() - pc < 1) + return false; + uint32_t opcode = *pc++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + uint32_t nSize = OP_0; + 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; + memcpy(&nSize, &pc[0], 2); + pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end() - pc < 4) + return false; + memcpy(&nSize, &pc[0], 4); + pc += 4; + } + if (end() - pc < 0 || (uint32_t)(end() - pc) < nSize) + return false; + if (pvchRet) + pvchRet->assign(pc, pc + nSize); + pc += nSize; + } + + opcodeRet = (opcodetype)opcode; + return true; +} + +int CScript::DecodeOP_N(opcodetype opcode) +{ + if (opcode == OP_0) + return 0; + assert(opcode >= OP_1 && opcode <= OP_16); + return (opcode - (OP_1 - 1)); +} + +opcodetype CScript::EncodeOP_N(int n) +{ + assert(n >= 0 && n <= 16); + if (n == 0) + return OP_0; + return (opcodetype)(OP_1+n-1); +} + +int CScript::FindAndDelete(const CScript& b) +{ + int nFound = 0; + if (b.empty()) + return nFound; + CScript result; + iterator pc = begin(), pc2 = begin(); + opcodetype opcode; + do + { + result.insert(result.end(), pc2, pc); + while (static_cast(end() - pc) >= b.size() && equal(b.begin(), b.end(), pc)) + { + pc = pc + b.size(); + ++nFound; + } + pc2 = pc; + } + while (GetOp(pc, opcode)); + + if (nFound > 0) { + result.insert(result.end(), pc2, end()); + *this = result; + } + return nFound; +} + +int CScript::Find(opcodetype op) const +{ + int nFound = 0; + opcodetype opcode; + for (const_iterator pc = begin(); pc != end() && GetOp(pc, opcode);) + if (opcode == op) + ++nFound; + return nFound; +} + unsigned int CScript::GetSigOpCount(bool fAccurate) const { - unsigned int n = 0; - const_iterator pc = begin(); - opcodetype lastOpcode = OP_INVALIDOPCODE; + uint32_t n = 0; + auto pc = begin(); + auto lastOpcode = OP_INVALIDOPCODE; while (pc < end()) { opcodetype opcode; @@ -1990,7 +2316,7 @@ unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const // This is a pay-to-script-hash scriptPubKey; // get the last item that the scriptSig // pushes onto the stack: - const_iterator pc = scriptSig.begin(); + auto pc = scriptSig.begin(); vector data; while (pc < scriptSig.end()) { @@ -2015,13 +2341,32 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::IsPushOnly(const_iterator pc) const +{ + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + if (opcode > OP_16) + return false; + } + return true; +} + +// Called by CTransaction::IsStandard and P2SH VerifyScript (which makes it consensus-critical). +bool CScript::IsPushOnly() const +{ + return this->IsPushOnly(begin()); +} + bool CScript::HasCanonicalPushes() const { - const_iterator pc = begin(); + auto pc = begin(); while (pc < end()) { opcodetype opcode; - std::vector data; + vector data; if (!GetOp(pc, opcode, data)) return false; if (opcode > OP_16) @@ -2072,19 +2417,69 @@ void CScript::SetDestination(const CTxDestination& dest) boost::apply_visitor(CScriptVisitor(this), dest); } -void CScript::SetDestination(const CPubKey& R, CPubKey& pubKeyVariant) +void CScript::SetAddress(const CBitcoinAddress& dest) { this->clear(); - *this << pubKeyVariant << R << OP_DROP << OP_CHECKSIG; + if (dest.IsScript()) + *this << OP_HASH160 << dest.GetData() << OP_EQUAL; + else if (dest.IsPubKey()) + *this << OP_DUP << OP_HASH160 << dest.GetData() << OP_EQUALVERIFY << OP_CHECKSIG; + else if (dest.IsPair()) { + // Pubkey pair address, going to generate + // new one-time public key. + CMalleablePubKey mpk; + if (!mpk.setvch(dest.GetData())) + return; + CPubKey R, pubKeyVariant; + mpk.GetVariant(R, pubKeyVariant); + *this << pubKeyVariant << R << OP_DROP << OP_CHECKSIG; + } } - -void CScript::SetMultisig(int nRequired, const std::vector& keys) +void CScript::SetMultisig(int nRequired, const vector& keys) { this->clear(); *this << EncodeOP_N(nRequired); - BOOST_FOREACH(const CKey& key, keys) - *this << key.GetPubKey(); + for(const auto& key : keys) + *this << key; *this << EncodeOP_N((int)(keys.size())) << OP_CHECKMULTISIG; } + +void CScript::PrintHex() const +{ + printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str()); +} + +string CScript::ToString(bool fShort) const +{ + string str; + opcodetype opcode; + vector vch; + const_iterator pc = begin(); + while (pc < end()) + { + if (!str.empty()) + str += " "; + if (!GetOp(pc, opcode, vch)) + { + str += "[error]"; + return str; + } + if (opcode <= OP_PUSHDATA4) + str += fShort? ValueString(vch).substr(0, 10) : ValueString(vch); + else + str += GetOpName(opcode); + } + return str; +} + +void CScript::print() const +{ + printf("%s\n", ToString().c_str()); +} + +CScriptID CScript::GetID() const +{ + return CScriptID(Hash160(*this)); +}