X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=blobdiff_plain;f=src%2Fscript.cpp;h=9f453022cbcaa19d6ff091a02237545b4655c7ef;hp=34872808b3f156c9aa363eca1778117a7928a005;hb=e10622d1297e638109bbf58c35ad008f7acbae7c;hpb=6ec76ca09efca03236a67db65cbfe44d7c7a878e diff --git a/src/script.cpp b/src/script.cpp index 3487280..9f45302 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,12 +1,18 @@ // 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" +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include using namespace std; using namespace boost; +#include "script.h" +#include "keystore.h" +#include "bignum.h" +#include "key.h" +#include "main.h" + bool CheckSig(vector vchSig, vector vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); @@ -32,7 +38,7 @@ CBigNum CastToBigNum(const valtype& vch) bool CastToBool(const valtype& vch) { - for (int i = 0; i < vch.size(); i++) + for (unsigned int i = 0; i < vch.size(); i++) { if (vch[i] != 0) { @@ -203,10 +209,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 +224,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 +233,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 +318,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; @@ -539,7 +536,7 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip return false; int n = CastToBigNum(stacktop(-1)).getint(); popstack(stack); - if (n < 0 || n >= stack.size()) + if (n < 0 || n >= (int)stack.size()) return false; valtype vch = stacktop(-n-1); if (opcode == OP_ROLL) @@ -607,9 +604,9 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip int nEnd = nBegin + CastToBigNum(stacktop(-1)).getint(); if (nBegin < 0 || nEnd < nBegin) return false; - if (nBegin > vch.size()) + if (nBegin > (int)vch.size()) nBegin = vch.size(); - if (nEnd > vch.size()) + if (nEnd > (int)vch.size()) nEnd = vch.size(); vch.erase(vch.begin() + nEnd, vch.end()); vch.erase(vch.begin(), vch.begin() + nBegin); @@ -628,7 +625,7 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip int nSize = CastToBigNum(stacktop(-1)).getint(); if (nSize < 0) return false; - if (nSize > vch.size()) + if (nSize > (int)vch.size()) nSize = vch.size(); if (opcode == OP_LEFT) vch.erase(vch.begin() + nSize, vch.end()); @@ -658,7 +655,7 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip if (stack.size() < 1) return false; valtype& vch = stacktop(-1); - for (int i = 0; i < vch.size(); i++) + for (unsigned int i = 0; i < vch.size(); i++) vch[i] = ~vch[i]; } break; @@ -675,17 +672,17 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip MakeSameSize(vch1, vch2); if (opcode == OP_AND) { - for (int i = 0; i < vch1.size(); i++) + for (unsigned int i = 0; i < vch1.size(); i++) vch1[i] &= vch2[i]; } else if (opcode == OP_OR) { - for (int i = 0; i < vch1.size(); i++) + for (unsigned int i = 0; i < vch1.size(); i++) vch1[i] |= vch2[i]; } else if (opcode == OP_XOR) { - for (int i = 0; i < vch1.size(); i++) + for (unsigned int i = 0; i < vch1.size(); i++) vch1[i] ^= vch2[i]; } popstack(stack); @@ -917,13 +914,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 +963,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 +986,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 +1007,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 +1028,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); -} - @@ -1093,7 +1050,7 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR)); // Blank out other inputs' signatures - for (int i = 0; i < txTmp.vin.size(); i++) + for (unsigned int i = 0; i < txTmp.vin.size(); i++) txTmp.vin[i].scriptSig = CScript(); txTmp.vin[nIn].scriptSig = scriptCode; @@ -1104,7 +1061,7 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int txTmp.vout.clear(); // Let the others update at will - for (int i = 0; i < txTmp.vin.size(); i++) + for (unsigned int i = 0; i < txTmp.vin.size(); i++) if (i != nIn) txTmp.vin[i].nSequence = 0; } @@ -1118,11 +1075,11 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int return 1; } txTmp.vout.resize(nOut+1); - for (int i = 0; i < nOut; i++) + for (unsigned 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++) + for (unsigned int i = 0; i < txTmp.vin.size(); i++) if (i != nIn) txTmp.vin[i].nSequence = 0; } @@ -1135,7 +1092,7 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int } // Serialize and hash - CDataStream ss(SER_GETHASH); + CDataStream ss(SER_GETHASH, 0); ss.reserve(10000); ss << txTmp << nHashType; return Hash(ss.begin(), ss.end()); @@ -1186,10 +1143,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 +1216,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 +1307,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) { @@ -1390,9 +1360,9 @@ bool IsStandard(const CScript& scriptPubKey) } -int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) +unsigned int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) { - int nResult = 0; + unsigned int nResult = 0; BOOST_FOREACH(const valtype& pubkey, pubkeys) { CBitcoinAddress address; @@ -1479,8 +1449,7 @@ 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 +1518,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 +1560,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; } +unsigned int CScript::GetSigOpCount(bool fAccurate) const +{ + unsigned 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; +} + +unsigned 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 +1642,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; }