X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fscript.cpp;h=b6f120289a6430532c21595f701b33a48053e4c1;hb=882164196e5b1971313493f95a6d027f05e2ec92;hp=f576e14a8b5865e4f5a2b0a50ba474c79b820609;hpb=89772f932ac9477ff4745dc62a01cf57dbc0f70d;p=novacoin.git diff --git a/src/script.cpp b/src/script.cpp index f576e14..b6f1202 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2011 The Bitcoin developers +// Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" @@ -203,10 +203,8 @@ const char* GetOpName(opcodetype opcode) case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; - // meta - case OP_EVAL : return "OP_EVAL"; - // expanson + case OP_NOP1 : return "OP_NOP1"; case OP_NOP2 : return "OP_NOP2"; case OP_NOP3 : return "OP_NOP3"; case OP_NOP4 : return "OP_NOP4"; @@ -220,7 +218,6 @@ const char* GetOpName(opcodetype opcode) // template matching params - case OP_SCRIPTHASH : return "OP_SCRIPTHASH"; case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; @@ -230,26 +227,20 @@ const char* GetOpName(opcodetype opcode) } } -// -// Returns true if script is valid. -// -bool EvalScriptInner(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, - CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, - bool fStrictOpEval, int nRecurseDepth) +bool EvalScript(vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); opcodetype opcode; valtype vchPushValue; vector vfExec; vector altstack; if (script.size() > 10000) return false; + int nOpCount = 0; - // Limit OP_EVAL recursion - if (nRecurseDepth > 2) - return false; try { @@ -321,7 +312,7 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip // Control // case OP_NOP: - case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: break; @@ -917,13 +908,12 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pendcodehash); + CScript scriptCode(pbegincodehash, pend); // Drop the signature, since there's no way for a signature to sign itself scriptCode.FindAndDelete(CScript(vchSig)); bool fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType); - nSigOpCount++; popstack(stack); popstack(stack); @@ -967,7 +957,7 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip return false; // Subset of script starting at the most recent codeseparator - CScript scriptCode(pbegincodehash, pendcodehash); + CScript scriptCode(pbegincodehash, pend); // Drop the signatures, since there's no way for a signature to sign itself for (int k = 0; k < nSigsCount; k++) @@ -990,7 +980,6 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip } ikey++; nKeysCount--; - nSigOpCount++; // If there are more signatures left than keys left, // then too many signatures have failed @@ -1012,32 +1001,6 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip } break; - case OP_EVAL: - { - if (!fStrictOpEval) - break; // Act as a NO_OP - - - // Evaluate the top item on the stack as a Script - // [serialized script ] -- [result(s) of executing script] - if (stack.size() < 1) - return false; - valtype& vchScript = stacktop(-1); - CScript subscript(vchScript.begin(), vchScript.end()); - popstack(stack); - - // Codeseparators not allowed; they don't make sense 'inside' an OP_EVAL, because - // their purpose is to change which parts of the scriptPubKey script is copied - // and signed by OP_CHECKSIG, but OP_EVAl'ed code is in the scriptSig, not the scriptPubKey. - if (subscript.Find(OP_CODESEPARATOR)) - return false; - - if (!EvalScriptInner(stack, subscript, txTo, nIn, nHashType, - pbegincodehash, pendcodehash, nOpCount, nSigOpCount, fStrictOpEval, nRecurseDepth+1)) - return false; - } - break; - default: return false; } @@ -1059,18 +1022,6 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip return true; } -bool EvalScript(vector >& stack, const CScript& script, - const CTransaction& txTo, unsigned int nIn, int nHashType, - bool fStrictOpEval, int& nSigOpCountRet) -{ - CScript::const_iterator pbegincodehash = script.begin(); - CScript::const_iterator pendcodehash = script.end(); - - int nOpCount = 0; - return EvalScriptInner(stack, script, txTo, nIn, nHashType, pbegincodehash, pendcodehash, - nOpCount, nSigOpCountRet, fStrictOpEval, 0); -} - @@ -1186,10 +1137,16 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); + vSolutionsRet.push_back(hashBytes); + return true; } // Scan templates @@ -1253,12 +1210,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector& multisigdata, const CKeyStore& keystore, uint2 // // Sign scriptPubKey with private keys stored in keystore, given transaction hash and hash type. -// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed). -// Returns true if scriptPubKey could be completely satisified. +// Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), +// unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script. +// Returns false if scriptPubKey could not be completely satisified. // -bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, CScript& scriptSigRet) +bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash, int nHashType, + CScript& scriptSigRet, txnouttype& whichTypeRet) { scriptSigRet.clear(); - txnouttype whichType; vector vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) + if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) return false; CBitcoinAddress address; - CScript subscript; - switch (whichType) + switch (whichTypeRet) { case TX_NONSTANDARD: return false; @@ -1350,23 +1301,36 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash keystore.GetPubKey(address, vch); scriptSigRet << vch; } - break; + return true; case TX_SCRIPTHASH: - if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) - return false; - if (!Solver(keystore, subscript, hash, nHashType, scriptSigRet)) - return false; - if (hash != 0) - // static_cast to get vector.operator<< instead of CScript.operator<< - scriptSigRet << static_cast(subscript); // signatures AND serialized script - break; + return keystore.GetCScript(uint160(vSolutions[0]), scriptSigRet); + case TX_MULTISIG: scriptSigRet << OP_0; // workaround CHECKMULTISIG bug return (SignN(vSolutions, keystore, hash, nHashType, scriptSigRet)); } - return true; + return false; } +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions) +{ + switch (t) + { + case TX_NONSTANDARD: + return -1; + case TX_PUBKEY: + return 1; + case TX_PUBKEYHASH: + return 2; + case TX_MULTISIG: + if (vSolutions.size() < 1 || vSolutions[0].size() < 1) + return -1; + return vSolutions[0][0] + 1; + case TX_SCRIPTHASH: + return 1; // doesn't include args needed by the script + } + return -1; +} bool IsStandard(const CScript& scriptPubKey) { @@ -1436,7 +1400,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - return HaveKeys(vSolutions, keystore); + return HaveKeys(keys, keystore) == keys.size(); } } return false; @@ -1503,29 +1467,44 @@ bool ExtractAddresses(const CScript& scriptPubKey, txnouttype& typeRet, vector > stack; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) + vector > stack, stackCopy; + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType)) return false; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) + if (fValidatePayToScriptHash) + stackCopy = stack; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType)) return false; if (stack.empty()) return false; - bool fResult = CastToBool(stack.back()); - // This code should be removed when a compatibility-breaking block chain split has passed. - // Special check for OP_EVAL backwards-compatibility: if scriptPubKey or scriptSig contains - // OP_EVAL, then result must be identical if OP_EVAL is treated as a no-op: - if (fResult && fStrictOpEval && (scriptPubKey.Find(OP_EVAL) || scriptSig.Find(OP_EVAL))) - return VerifyScript(scriptSig, scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, false); + if (CastToBool(stack.back()) == false) + return false; + + // Additional validation for spend-to-script-hash transactions: + if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash()) + { + if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only + return false; // or validation fails - return fResult; + const valtype& pubKeySerialized = stackCopy.back(); + CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); + popstack(stackCopy); + + if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType)) + return false; + if (stackCopy.empty()) + return false; + return CastToBool(stackCopy.back()); + } + + return true; } -bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType, CScript scriptPrereq) +bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType) { assert(nIn < txTo.vin.size()); CTxIn& txin = txTo.vin[nIn]; @@ -1534,24 +1513,38 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans // 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(scriptPrereq + txout.scriptPubKey, txTo, nIn, nHashType); + uint256 hash = SignatureHash(txout.scriptPubKey, txTo, nIn, nHashType); - if (!Solver(keystore, txout.scriptPubKey, hash, nHashType, txin.scriptSig)) + txnouttype whichType; + if (!Solver(keystore, txout.scriptPubKey, hash, nHashType, txin.scriptSig, whichType)) return false; - txin.scriptSig = scriptPrereq + txin.scriptSig; + if (whichType == TX_SCRIPTHASH) + { + // 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; + + // Recompute txn hash using subscript in place of scriptPubKey: + uint256 hash2 = SignatureHash(subscript, txTo, nIn, nHashType); + txnouttype subType; + if (!Solver(keystore, subscript, hash2, nHashType, txin.scriptSig, subType)) + return false; + if (subType == TX_SCRIPTHASH) + return false; + txin.scriptSig << static_cast(subscript); // Append serialized subscript + } // Test solution - int nUnused = 0; - if (scriptPrereq.empty()) - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0, true)) - return false; + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, true, 0)) + return false; return true; } -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType, bool fStrictOpEval) +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1562,17 +1555,74 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig if (txin.prevout.hash != txFrom.GetHash()) return false; - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, fStrictOpEval)) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType)) return false; return true; } +int CScript::GetSigOpCount(bool fAccurate) const +{ + int n = 0; + const_iterator pc = begin(); + opcodetype lastOpcode = OP_INVALIDOPCODE; + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + break; + if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) + n++; + else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) + { + if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) + n += DecodeOP_N(lastOpcode); + else + n += 20; + } + lastOpcode = opcode; + } + return n; +} + +int CScript::GetSigOpCount(const CScript& scriptSig) const +{ + if (!IsPayToScriptHash()) + return GetSigOpCount(true); + + // This is a pay-to-script-hash scriptPubKey; + // get the last item that the scriptSig + // pushes onto the stack: + const_iterator pc = scriptSig.begin(); + vector data; + while (pc < scriptSig.end()) + { + opcodetype opcode; + if (!scriptSig.GetOp(pc, opcode, data)) + return 0; + if (opcode > OP_16) + return 0; + } + + /// ... and return it's opcount: + CScript subscript(data.begin(), data.end()); + return subscript.GetSigOpCount(true); +} + +bool CScript::IsPayToScriptHash() const +{ + // Extra-fast test for pay-to-script-hash CScripts: + return (this->size() == 23 && + this->at(0) == OP_HASH160 && + this->at(1) == 0x14 && + this->at(22) == OP_EQUAL); +} + void CScript::SetBitcoinAddress(const CBitcoinAddress& address) { this->clear(); if (address.IsScript()) - *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_EVAL; + *this << OP_HASH160 << address.GetHash160() << OP_EQUAL; else *this << OP_DUP << OP_HASH160 << address.GetHash160() << OP_EQUALVERIFY << OP_CHECKSIG; } @@ -1587,10 +1637,10 @@ void CScript::SetMultisig(int nRequired, const std::vector& keys) *this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG; } -void CScript::SetEval(const CScript& subscript) +void CScript::SetPayToScriptHash(const CScript& subscript) { assert(!subscript.empty()); uint160 subscriptHash = Hash160(subscript); this->clear(); - *this << OP_DUP << OP_HASH160 << subscriptHash << OP_EQUALVERIFY << OP_EVAL; + *this << OP_HASH160 << subscriptHash << OP_EQUAL; }