From 2a45a494b0bec6a0f1fc6ab7f26c260b85e7ff3e Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Tue, 8 Nov 2011 13:20:29 -0500 Subject: [PATCH] Use block times for 'hard' OP_EVAL switchover, and refactored EvalScript so it takes a flag for how to interpret OP_EVAL. Also increased IsStandard size of scriptSigs to 500 bytes, so a 3-of-3 multisig transaction IsStandard. --- src/bitcoinrpc.cpp | 4 +- src/db.cpp | 2 +- src/db.h | 25 +++++----- src/keystore.cpp | 17 ++++--- src/keystore.h | 14 +++-- src/main.cpp | 52 ++++++++++++++------ src/main.h | 4 +- src/script.cpp | 95 +++++++++++++++---------------------- src/script.h | 14 +++--- src/test/multisig_tests.cpp | 31 ++++++------ src/test/script_op_eval_tests.cpp | 39 ++++----------- src/test/script_tests.cpp | 38 +++++++------- src/wallet.cpp | 14 ++++- src/wallet.h | 5 +- 14 files changed, 177 insertions(+), 177 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 47fd983..1a1c991 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -1674,10 +1674,10 @@ Value validateaddress(const Array& params, bool fHelp) pwalletMain->GetCScript(address.GetHash160(), subscript); ret.push_back(Pair("ismine", ::IsMine(*pwalletMain, subscript))); std::vector addresses; - txntype whichType; + txnouttype whichType; int nRequired; ExtractAddresses(subscript, pwalletMain, whichType, addresses, nRequired); - ret.push_back(Pair("script", GetTxnTypeName(whichType))); + ret.push_back(Pair("script", GetTxnOutputType(whichType))); Array a; BOOST_FOREACH(const CBitcoinAddress& addr, addresses) a.push_back(addr.ToString()); diff --git a/src/db.cpp b/src/db.cpp index 52ad695..f43b2a5 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -938,7 +938,7 @@ int CWalletDB::LoadWallet(CWallet* pwallet) { uint160 hash; ssKey >> hash; - std::vector script; + CScript script; ssValue >> script; if (!pwallet->LoadCScript(hash, script)) return DB_CORRUPT; diff --git a/src/db.h b/src/db.h index 99dd88b..e593ae2 100644 --- a/src/db.h +++ b/src/db.h @@ -13,17 +13,17 @@ #include -class CTxIndex; +class CAccount; +class CAccountingEntry; +class CAddress; +class CBlockLocator; class CDiskBlockIndex; class CDiskTxPos; +class CMasterKey; class COutPoint; -class CAddress; -class CWalletTx; +class CTxIndex; class CWallet; -class CAccount; -class CAccountingEntry; -class CBlockLocator; - +class CWalletTx; extern unsigned int nWalletDBUpdated; extern DbEnv dbenv; @@ -420,16 +420,17 @@ public: return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } - bool ReadCScript(const uint160 &hash, std::vector& data) + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + bool ReadCScript(const uint160 &hash, CScript& redeemScript) { - data.clear(); - return Read(std::make_pair(std::string("cscript"), hash), data); + redeemScript.clear(); + return Read(std::make_pair(std::string("cscript"), hash), redeemScript); } - bool WriteCScript(const uint160& hash, const std::vector& data) + bool WriteCScript(const uint160& hash, const CScript& redeemScript) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("cscript"), hash), data, false); + return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } bool WriteBestBlock(const CBlockLocator& locator) diff --git a/src/keystore.cpp b/src/keystore.cpp index c9b9b4a..21fb0f9 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -4,8 +4,9 @@ // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include "headers.h" -#include "db.h" #include "crypter.h" +#include "db.h" +#include "script.h" std::vector CKeyStore::GenerateNewKey() { @@ -33,10 +34,10 @@ bool CBasicKeyStore::AddKey(const CKey& key) return true; } -bool CBasicKeyStore::AddCScript(const uint160 &hash, const std::vector& data) +bool CBasicKeyStore::AddCScript(const uint160 &hash, const CScript& redeemScript) { CRITICAL_BLOCK(cs_KeyStore) - mapData[hash] = data; + mapScripts[hash] = redeemScript; return true; } @@ -44,19 +45,19 @@ bool CBasicKeyStore::HaveCScript(const uint160& hash) const { bool result; CRITICAL_BLOCK(cs_KeyStore) - result = (mapData.count(hash) > 0); + result = (mapScripts.count(hash) > 0); return result; } -bool CBasicKeyStore::GetCScript(const uint160 &hash, std::vector& dataOut) const +bool CBasicKeyStore::GetCScript(const uint160 &hash, CScript& redeemScriptOut) const { CRITICAL_BLOCK(cs_KeyStore) { - DataMap::const_iterator mi = mapData.find(hash); - if (mi != mapData.end()) + ScriptMap::const_iterator mi = mapScripts.find(hash); + if (mi != mapScripts.end()) { - dataOut = (*mi).second; + redeemScriptOut = (*mi).second; return true; } } diff --git a/src/keystore.h b/src/keystore.h index 942fb9a..669bf90 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -6,6 +6,7 @@ #define BITCOIN_KEYSTORE_H #include "crypter.h" +#include "script.h" // A virtual base class for key stores class CKeyStore @@ -31,9 +32,10 @@ public: virtual void GetKeys(std::set &setAddress) const =0; virtual bool GetPubKey(const CBitcoinAddress &address, std::vector& vchPubKeyOut) const; - virtual bool AddCScript(const uint160 &hash, const std::vector& data) =0; + // Support for BIP 0013 : see https://en.bitcoin.it/wiki/BIP_0013 + virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript) =0; virtual bool HaveCScript(const uint160 &hash) const =0; - virtual bool GetCScript(const uint160 &hash, std::vector& dataOut) const =0; + virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const =0; // Generate a new key, and add it to the store virtual std::vector GenerateNewKey(); @@ -48,14 +50,14 @@ public: }; typedef std::map KeyMap; -typedef std::map > DataMap; +typedef std::map ScriptMap; // Basic key store, that keeps keys in an address->secret map class CBasicKeyStore : public CKeyStore { protected: KeyMap mapKeys; - DataMap mapData; + ScriptMap mapScripts; public: bool AddKey(const CKey& key); @@ -92,9 +94,9 @@ public: } return false; } - virtual bool AddCScript(const uint160 &hash, const std::vector& data); + virtual bool AddCScript(const uint160 &hash, const CScript& redeemScript); virtual bool HaveCScript(const uint160 &hash) const; - virtual bool GetCScript(const uint160 &hash, std::vector& dataOut) const; + virtual bool GetCScript(const uint160 &hash, CScript& redeemScriptOut) const; }; typedef std::map, std::vector > > CryptedKeyMap; diff --git a/src/main.cpp b/src/main.cpp index 1b5bf52..81dd76f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -250,13 +250,13 @@ bool CTransaction::IsStandard() const { BOOST_FOREACH(const CTxIn& txin, vin) { - // Biggest 'standard' txin is a 2-signature 2-of-3 escrow - // in an OP_EVAL, which is 2 ~80-byte signatures, 3 + // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG + // in an OP_EVAL, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 400) - return error("nonstandard txin, size %d\n", txin.scriptSig.size()); + if (txin.scriptSig.size() > 500) + return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size()); if (!txin.scriptSig.IsPushOnly()) - return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str()); + return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str()); } BOOST_FOREACH(const CTxOut& txout, vout) if (!::IsStandard(txout.scriptPubKey)) @@ -275,7 +275,7 @@ bool CTransaction::IsStandard() const // expensive-to-check-upon-redemption script like: // DUP CHECKSIG DROP ... repeated 100 times... OP_1 // -bool CTransaction::IsStandardInputs(std::map > mapInputs) const +bool CTransaction::AreInputsStandard(std::map > mapInputs) const { if (fTestNet) return true; // Allow non-standard on testnet @@ -287,18 +287,20 @@ bool CTransaction::IsStandardInputs(std::map > vSolutions; - txntype whichType; - if (!Solver(txPrev.vout[vin[i].prevout.n].scriptPubKey, whichType, vSolutions)) - return false; + txnouttype whichType; + // get the scriptPubKey corresponding to this input: + CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey; + if (!Solver(prevScript, whichType, vSolutions)) + return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str()); if (whichType == TX_SCRIPTHASH) { vector > stack; int nUnused; - if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, nUnused)) - return false; - const vector& subscript = stack.back(); - if (!::IsStandard(CScript(subscript.begin(), subscript.end()))) + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused)) return false; + CScript subscript(stack.back().begin(), stack.back().end()); + if (!::IsStandard(subscript)) + return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str()); } } @@ -481,7 +483,7 @@ bool CTransaction::AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs, bool* pfMi } // Check for non-standard OP_EVALs in inputs - if (!IsStandardInputs(mapInputs)) + if (!AreInputsStandard(mapInputs)) return error("AcceptToMemoryPool() : nonstandard transaction input"); // Check against previous transactions @@ -978,9 +980,27 @@ bool CTransaction::ConnectInputs(map > inp // (before the last blockchain checkpoint). This is safe because block merkle hashes are // still computed and checked, and any change will be caught at the next checkpoint. if (!(fBlock && IsInitialBlockDownload())) + { + bool fStrictOpEval = true; + // This code should be removed when OP_EVAL has + // a majority of hashing power on the network. + if (fBlock) + { + // To avoid being on the short end of a block-chain split, + // interpret OP_EVAL as a NO_OP until blocks with timestamps + // after opevaltime: + int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012 + fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime); + } + // if !fBlock, then always be strict-- don't accept + // invalid-under-new-rules OP_EVAL transactions into + // our memory pool (don't relay them, don't include them + // in blocks we mine). + // Verify signature - if (!VerifySignature(txPrev, *this, i, nSigOpsRet)) + if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval)) return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str())); + } // Check for conflicts (double-spend) // This doesn't trigger the DoS code on purpose; if it did, it would make it easier @@ -1054,7 +1074,7 @@ bool CTransaction::ClientConnectInputs() // Verify signature int nUnused = 0; - if (!VerifySignature(txPrev, *this, i, nUnused)) + if (!VerifySignature(txPrev, *this, i, nUnused, false)) return error("ConnectInputs() : VerifySignature failed"); ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of diff --git a/src/main.h b/src/main.h index bf31234..26d14b0 100644 --- a/src/main.h +++ b/src/main.h @@ -494,7 +494,7 @@ public: } bool IsStandard() const; - bool IsStandardInputs(std::map > mapInputs) const; + bool AreInputsStandard(std::map > mapInputs) const; int64 GetValueOut() const { @@ -622,6 +622,8 @@ public: bool ReadFromDisk(CTxDB& txdb, COutPoint prevout); bool ReadFromDisk(COutPoint prevout); bool DisconnectInputs(CTxDB& txdb); + + // Fetch from memory and/or disk. inputsRet keys are transaction hashes. bool FetchInputs(CTxDB& txdb, const std::map& mapTestPool, bool fBlock, bool fMiner, std::map >& inputsRet); bool ConnectInputs(std::map > inputs, diff --git a/src/script.cpp b/src/script.cpp index e603991..5487c01 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -70,7 +70,7 @@ static inline void popstack(vector& stack) } -const char* GetTxnTypeName(txntype t) +const char* GetTxnOutputType(txnouttype t) { switch (t) { @@ -230,12 +230,12 @@ 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, int nRecurseDepth) + CScript::const_iterator pbegincodehash, CScript::const_iterator pendcodehash, int& nOpCount, int& nSigOpCount, + bool fStrictOpEval, int nRecurseDepth) { CAutoBN_CTX pctx; CScript::const_iterator pc = script.begin(); @@ -1014,17 +1014,9 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip case OP_EVAL: { - // This code should be removed when OP_EVAL has - // a majority of hashing power on the network. - // OP_EVAL behaves just like OP_NOP until - // opevaltime : - if (!fTestNet || fDebug) - { - // 1328054400 is Feb 1, 2012 - int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); - if (GetTime() < nEvalSwitchTime) - break; - } + 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] @@ -1034,12 +1026,14 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip CScript subscript(vchScript.begin(), vchScript.end()); popstack(stack); - // Codeseparators not allowed + // 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, nRecurseDepth++)) + pbegincodehash, pendcodehash, nOpCount, nSigOpCount, fStrictOpEval, nRecurseDepth++)) return false; } break; @@ -1066,14 +1060,15 @@ bool EvalScriptInner(vector >& stack, const CScript& scrip } bool EvalScript(vector >& stack, const CScript& script, - const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet) + 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, 0); + nOpCount, nSigOpCountRet, fStrictOpEval, 0); } @@ -1177,10 +1172,10 @@ bool CheckSig(vector vchSig, vector vchPubKey, CSc // // Return public keys or hashes from scriptPubKey, for 'standard' transaction types. // -bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector >& vSolutionsRet) +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector >& vSolutionsRet) { // Templates - static map mTemplates; + static map mTemplates; if (mTemplates.empty()) { // Standard tx, sender provides pubkey, receiver adds signature @@ -1199,7 +1194,7 @@ bool Solver(const CScript& scriptPubKey, txntype& typeRet, vector vSolutions; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; CBitcoinAddress address; - valtype subscript; + CScript subscript; switch (whichType) { case TX_NONSTANDARD: @@ -1359,10 +1354,11 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash case TX_SCRIPTHASH: if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) return false; - if (!Solver(keystore, CScript(subscript.begin(), subscript.end()), hash, nHashType, scriptSigRet)) + if (!Solver(keystore, subscript, hash, nHashType, scriptSigRet)) return false; if (hash != 0) - scriptSigRet << subscript; // signatures AND serialized script + // static_cast to get vector.operator<< instead of CScript.operator<< + scriptSigRet << static_cast(subscript); // signatures AND serialized script break; case TX_MULTISIG: scriptSigRet << OP_0; // workaround CHECKMULTISIG bug @@ -1375,7 +1371,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash bool IsStandard(const CScript& scriptPubKey) { vector vSolutions; - txntype whichType; + txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1410,7 +1406,7 @@ int HaveKeys(const vector& pubkeys, const CKeyStore& keystore) bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { vector vSolutions; - txntype whichType; + txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1427,10 +1423,10 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) return keystore.HaveKey(address); case TX_SCRIPTHASH: { - valtype subscript; + CScript subscript; if (!keystore.GetCScript(uint160(vSolutions[0]), subscript)) return false; - return IsMine(keystore, CScript(subscript.begin(), subscript.end())); + return IsMine(keystore, subscript); } case TX_MULTISIG: { @@ -1449,7 +1445,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBitcoinAddress& addressRet) { vector vSolutions; - txntype whichType; + txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1472,7 +1468,7 @@ bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* keystore, CBit return false; } -bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txntype& typeRet, vector& addressRet, int& nRequiredRet) +bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, txnouttype& typeRet, vector& addressRet, int& nRequiredRet) { addressRet.clear(); typeRet = TX_NONSTANDARD; @@ -1484,10 +1480,10 @@ bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, tx { nRequiredRet = vSolutions.front()[0]; int n = vSolutions.back()[0]; - for (vector::const_iterator it = vSolutions.begin()+1; it != vSolutions.begin()+vSolutions.size()-1; it++) + for (int i = 1; i < vSolutions.size()-1; i++) { CBitcoinAddress address; - address.SetPubKey(*it); + address.SetPubKey(vSolutions[i]); addressRet.push_back(address); } } @@ -1507,12 +1503,13 @@ bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* keystore, tx return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, + int nHashType, bool fStrictOpEval) { vector > stack; - if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, nSigOpCountRet)) + if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) return false; - if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, nSigOpCountRet)) + if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType, fStrictOpEval, nSigOpCountRet)) return false; if (stack.empty()) return false; @@ -1521,24 +1518,8 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // 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 (scriptSig.Find(OP_EVAL)+scriptPubKey.Find(OP_EVAL) > 0) - { - int nUnused = 0; - stack.clear(); - CScript sigCopy = scriptSig; - sigCopy.FindAndDelete(CScript(OP_EVAL)); - CScript pubKeyCopy = scriptPubKey; - pubKeyCopy.FindAndDelete(CScript(OP_EVAL)); - - if (!EvalScript(stack, sigCopy, txTo, nIn, nHashType, nUnused)) - return false; - if (!EvalScript(stack, pubKeyCopy, txTo, nIn, nHashType, nUnused)) - return false; - if (stack.empty()) - return false; - if (fResult != CastToBool(stack.back())) - return false; - } + if (fResult && fStrictOpEval && (scriptPubKey.Find(OP_EVAL) || scriptSig.Find(OP_EVAL))) + return VerifyScript(scriptSig, scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, false); return fResult; } @@ -1563,14 +1544,14 @@ bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CTrans // Test solution int nUnused = 0; if (scriptPrereq.empty()) - if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0)) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nUnused, 0, true)) return false; return true; } -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType) +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType, bool fStrictOpEval) { assert(nIn < txTo.vin.size()); const CTxIn& txin = txTo.vin[nIn]; @@ -1581,7 +1562,7 @@ 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)) + if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nSigOpCountRet, nHashType, fStrictOpEval)) return false; return true; diff --git a/src/script.h b/src/script.h index ee0be02..b671e15 100644 --- a/src/script.h +++ b/src/script.h @@ -6,7 +6,6 @@ #define H_BITCOIN_SCRIPT #include "base58.h" -#include "keystore.h" #include #include @@ -14,6 +13,7 @@ #include class CTransaction; +class CKeyStore; enum { @@ -24,7 +24,7 @@ enum }; -enum txntype +enum txnouttype { TX_NONSTANDARD, // 'standard' transaction types: @@ -34,7 +34,7 @@ enum txntype TX_MULTISIG, }; -const char* GetTxnTypeName(txntype t); +const char* GetTxnOutputType(txnouttype t); enum opcodetype { @@ -567,14 +567,14 @@ public: -bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, int& nSigOpCountRet); +bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType, bool fStrictOpEval, int& nSigOpCountRet); -bool Solver(const CScript& scriptPubKey, txntype& typeRet, std::vector >& vSolutionsRet); +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); bool IsStandard(const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, CBitcoinAddress& addressRet); -bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txntype& typeRet, std::vector& addressRet, int& nRequiredRet); +bool ExtractAddresses(const CScript& scriptPubKey, const CKeyStore* pkeystore, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript()); -bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0); +bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int& nSigOpCountRet, int nHashType=0, bool fStrictOpEval=true); #endif diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 75c764d..58f62b9 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -20,7 +20,8 @@ using namespace boost::assign; typedef vector valtype; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOpCount, + int nHashType, bool fStrictOpEval); BOOST_AUTO_TEST_SUITE(multisig_tests) @@ -80,19 +81,19 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys.clear(); keys += key[0],key[1]; // magic operator+= from boost.assign s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true)); for (int i = 0; i < 4; i++) { keys.clear(); keys += key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 1: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 1: %d", i)); keys.clear(); keys += key[1],key[i]; s = sign_multisig(a_and_b, keys, txTo[0], 0); - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0), strprintf("a&b 2: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_and_b, txTo[0], 0, nUnused, 0, true), strprintf("a&b 2: %d", i)); } // Test a OR b: @@ -102,16 +103,16 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i]; s = sign_multisig(a_or_b, keys, txTo[1], 0); if (i == 0 || i == 1) - BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0), strprintf("a|b: %d", i)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true), strprintf("a|b: %d", i)); } s.clear(); s << OP_0 << OP_0; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true)); s.clear(); s << OP_0 << OP_1; - BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(s, a_or_b, txTo[1], 0, nUnused, 0, true)); for (int i = 0; i < 4; i++) @@ -121,9 +122,9 @@ BOOST_AUTO_TEST_CASE(multisig_verify) keys += key[i],key[j]; s = sign_multisig(escrow, keys, txTo[2], 0); if (i < j && i < 3 && j < 3) - BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 1: %d %d", i, j)); + BOOST_CHECK_MESSAGE(VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 1: %d %d", i, j)); else - BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0), strprintf("escrow 2: %d %d", i, j)); + BOOST_CHECK_MESSAGE(!VerifyScript(s, escrow, txTo[2], 0, nUnused, 0, true), strprintf("escrow 2: %d %d", i, j)); } } @@ -185,7 +186,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) { vector solutions; - txntype whichType; + txnouttype whichType; CScript s; s << key[0].GetPubKey() << OP_CHECKSIG; BOOST_CHECK(Solver(s, whichType, solutions)); @@ -198,7 +199,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) } { vector solutions; - txntype whichType; + txnouttype whichType; CScript s; s << OP_DUP << OP_HASH160 << Hash160(key[0].GetPubKey()) << OP_EQUALVERIFY << OP_CHECKSIG; BOOST_CHECK(Solver(s, whichType, solutions)); @@ -211,7 +212,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) } { vector solutions; - txntype whichType; + txnouttype whichType; CScript s; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); @@ -223,7 +224,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) } { vector solutions; - txntype whichType; + txnouttype whichType; CScript s; s << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); @@ -239,7 +240,7 @@ BOOST_AUTO_TEST_CASE(multisig_Solver1) } { vector solutions; - txntype whichType; + txnouttype whichType; CScript s; s << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; BOOST_CHECK(Solver(s, whichType, solutions)); diff --git a/src/test/script_op_eval_tests.cpp b/src/test/script_op_eval_tests.cpp index 6c683b5..c44642c 100644 --- a/src/test/script_op_eval_tests.cpp +++ b/src/test/script_op_eval_tests.cpp @@ -13,24 +13,10 @@ using namespace std; // Test routines internal to script.cpp: extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, + int nHashType, bool fStrictOpEval); -static const int64 nEvalSwitchover = 1328054400; - -struct CEvalFixture { - CEvalFixture() - { - // Set mock time to AFTER OP_EVAL deployed - SetMockTime(nEvalSwitchover+1); - } - ~CEvalFixture() - { - // Reset back to use-real-time - SetMockTime(0); - } -}; - -BOOST_FIXTURE_TEST_SUITE(script_op_eval_tests, CEvalFixture) +BOOST_AUTO_TEST_SUITE(script_op_eval_tests) BOOST_AUTO_TEST_CASE(script_op_eval1) { @@ -130,8 +116,8 @@ BOOST_AUTO_TEST_CASE(script_op_eval2) txTo.vout[0].nValue = 1; int nUnused = 0; - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); - BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); } BOOST_AUTO_TEST_CASE(script_op_eval3) @@ -212,13 +198,13 @@ BOOST_AUTO_TEST_CASE(script_op_eval_backcompat1) txTo.vout[0].nValue = 1; int nUnused = 0; - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); - BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused)); + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); + BOOST_CHECK(!VerifySignature(txFrom, txTo, 0, nUnused, true)); } BOOST_AUTO_TEST_CASE(script_op_eval_switchover) { - // Use SetMockTime to test OP_EVAL switchover code + // Test OP_EVAL switchover code CScript notValid; notValid << OP_11 << OP_12 << OP_EQUALVERIFY; @@ -238,14 +224,11 @@ BOOST_AUTO_TEST_CASE(script_op_eval_switchover) txTo.vin[0].scriptSig = CScript() << static_cast >(notValid); txTo.vout[0].nValue = 1; - SetMockTime(nEvalSwitchover-1); - int nUnused = 0; - BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, false)); - // After eval switchover time, it should validate: - SetMockTime(nEvalSwitchover); - BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0)); + // Under strict op_eval switchover, it should be considered invalid: + BOOST_CHECK(!VerifyScript(txTo.vin[0].scriptSig, txFrom.vout[0].scriptPubKey, txTo, 0, nUnused, 0, true)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 3d1c218..22885a6 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -7,8 +7,8 @@ using namespace std; extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, int nHashType); -extern bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType); +extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int& nSigOps, + int nHashType, bool fStrictOpEval); BOOST_AUTO_TEST_SUITE(script_tests) @@ -24,18 +24,18 @@ BOOST_AUTO_TEST_CASE(script_PushData) int nUnused = 0; vector > directStack; - BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, nUnused)); + BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0, true, nUnused)); vector > pushdata1Stack; - BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, nUnused)); + BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata1Stack == directStack); vector > pushdata2Stack; - BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, nUnused)); + BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata2Stack == directStack); vector > pushdata4Stack; - BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, nUnused)); + BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0, true, nUnused)); BOOST_CHECK(pushdata4Stack == directStack); } @@ -94,15 +94,15 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) txTo12.vout[0].nValue = 1; CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); txTo12.vout[0].nValue = 2; - BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, nUnused, 0, true)); CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, nUnused, 0, true)); } BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) @@ -131,46 +131,46 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) std::vector keys; keys.push_back(key1); keys.push_back(key2); CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key1); keys.push_back(key3); CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key3); CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key2); // Can't re-use sig CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, nUnused, 0, true)); keys.clear(); // Must have signatures CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23); - BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0)); + BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, nUnused, 0, true)); } diff --git a/src/wallet.cpp b/src/wallet.cpp index 5d44b1f..25eb624 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -42,13 +42,13 @@ bool CWallet::AddCryptedKey(const vector &vchPubKey, const vector return false; } -bool CWallet::AddCScript(const uint160 &hash, const std::vector& data) +bool CWallet::AddCScript(const uint160 &hash, const CScript& redeemScript) { - if (!CCryptoKeyStore::AddCScript(hash, data)) + if (!CCryptoKeyStore::AddCScript(hash, redeemScript)) return false; if (!fFileBacked) return true; - return CWalletDB(strWalletFile).WriteCScript(hash, data); + return CWalletDB(strWalletFile).WriteCScript(hash, redeemScript); } bool CWallet::Unlock(const SecureString& strWalletPassphrase) @@ -386,6 +386,14 @@ int64 CWallet::GetDebit(const CTxIn &txin) const bool CWallet::IsChange(const CTxOut& txout) const { CBitcoinAddress address; + + // TODO: fix handling of 'change' outputs. The assumption is that any + // payment to a TX_PUBKEYHASH that is mine but isn't in the address book + // is change. That assumption is likely to break when we implement multisignature + // wallets that return change back into a multi-signature-protected address; + // a better way of identifying which outputs are 'the send' and which are + // 'the change' will need to be implemented (maybe extend CWalletTx to remember + // which output, if any, was change). if (ExtractAddress(txout.scriptPubKey, this, address) && !address.IsScript()) CRITICAL_BLOCK(cs_wallet) if (!mapAddressBook.count(address)) diff --git a/src/wallet.h b/src/wallet.h index 34090ec..a7d07c9 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -7,6 +7,7 @@ #include "bignum.h" #include "key.h" +#include "keystore.h" #include "script.h" class CWalletTx; @@ -69,8 +70,8 @@ public: bool AddCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret); // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) bool LoadCryptedKey(const std::vector &vchPubKey, const std::vector &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } - bool AddCScript(const uint160& hash, const std::vector& data); - bool LoadCScript(const uint160& hash, const std::vector& data) { return CCryptoKeyStore::AddCScript(hash, data); } + bool AddCScript(const uint160& hash, const CScript& redeemScript); + bool LoadCScript(const uint160& hash, const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(hash, redeemScript); } bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); -- 1.7.1