diff options
Diffstat (limited to 'src/script')
| -rw-r--r-- | src/script/bitcoinconsensus.cpp | 21 | ||||
| -rw-r--r-- | src/script/bitcoinconsensus.h | 7 | ||||
| -rw-r--r-- | src/script/interpreter.cpp | 92 | ||||
| -rw-r--r-- | src/script/interpreter.h | 14 | ||||
| -rw-r--r-- | src/script/ismine.cpp | 62 | ||||
| -rw-r--r-- | src/script/ismine.h | 11 | ||||
| -rw-r--r-- | src/script/script_error.cpp | 6 | ||||
| -rw-r--r-- | src/script/script_error.h | 5 | ||||
| -rw-r--r-- | src/script/sigcache.cpp | 77 | ||||
| -rw-r--r-- | src/script/sigcache.h | 9 | ||||
| -rw-r--r-- | src/script/sign.cpp | 6 |
11 files changed, 220 insertions, 90 deletions
diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index b629f4278..036d6ca7b 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -23,7 +23,7 @@ public: m_remaining(txToLen) {} - TxInputStream& read(char* pch, size_t nSize) + void read(char* pch, size_t nSize) { if (nSize > m_remaining) throw std::ios_base::failure(std::string(__func__) + ": end of data"); @@ -37,16 +37,17 @@ public: memcpy(pch, m_data, nSize); m_remaining -= nSize; m_data += nSize; - return *this; } template<typename T> TxInputStream& operator>>(T& obj) { - ::Unserialize(*this, obj, m_type, m_version); + ::Unserialize(*this, obj); return *this; } + int GetVersion() const { return m_version; } + int GetType() const { return m_type; } private: const int m_type; const int m_version; @@ -69,17 +70,25 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } +/** Check that all specified flags are part of the libconsensus interface. */ +static bool verify_flags(unsigned int flags) +{ + return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0; +} + static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { + if (!verify_flags(flags)) { + return bitcoinconsensus_ERR_INVALID_FLAGS; + } try { TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, txTo, txToLen); - CTransaction tx; - stream >> tx; + CTransaction tx(deserialize, stream); if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); - if (tx.GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) != txToLen) + if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH); // Regardless of the verification result, the tx did not error. diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index f73a8e30b..1bef4fe9e 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -42,6 +42,7 @@ typedef enum bitcoinconsensus_error_t bitcoinconsensus_ERR_TX_SIZE_MISMATCH, bitcoinconsensus_ERR_TX_DESERIALIZE, bitcoinconsensus_ERR_AMOUNT_REQUIRED, + bitcoinconsensus_ERR_INVALID_FLAGS, } bitcoinconsensus_error; /** Script verification flags */ @@ -50,9 +51,13 @@ enum bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NONE = 0, bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH = (1U << 0), // evaluate P2SH (BIP16) subscripts bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG = (1U << 2), // enforce strict DER (BIP66) compliance + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY = (1U << 4), // enforce NULLDUMMY (BIP147) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY = (1U << 9), // enable CHECKLOCKTIMEVERIFY (BIP65) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY = (1U << 10), // enable CHECKSEQUENCEVERIFY (BIP112) bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS = (1U << 11), // enable WITNESS (BIP141) + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL = bitcoinconsensus_SCRIPT_FLAGS_VERIFY_P2SH | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_DERSIG | + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_NULLDUMMY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKLOCKTIMEVERIFY | + bitcoinconsensus_SCRIPT_FLAGS_VERIFY_CHECKSEQUENCEVERIFY | bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS }; /// Returns 1 if the input nIn of the serialized transaction pointed to by diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 47ea261e3..1410d0b73 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -79,8 +79,20 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { return false; } } else { - // Non-canonical public key: neither compressed nor uncompressed - return false; + // Non-canonical public key: neither compressed nor uncompressed + return false; + } + return true; +} + +bool static IsCompressedPubKey(const valtype &vchPubKey) { + if (vchPubKey.size() != 33) { + // Non-canonical public key: invalid length for compressed key + return false; + } + if (vchPubKey[0] != 0x02 && vchPubKey[0] != 0x03) { + // Non-canonical public key: invalid prefix for compressed key + return false; } return true; } @@ -199,10 +211,14 @@ bool CheckSignatureEncoding(const vector<unsigned char> &vchSig, unsigned int fl return true; } -bool static CheckPubKeyEncoding(const valtype &vchSig, unsigned int flags, ScriptError* serror) { - if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchSig)) { +bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, const SigVersion &sigversion, ScriptError* serror) { + if ((flags & SCRIPT_VERIFY_STRICTENC) != 0 && !IsCompressedOrUncompressedPubKey(vchPubKey)) { return set_error(serror, SCRIPT_ERR_PUBKEYTYPE); } + // Only compressed keys are accepted in segwit + if ((flags & SCRIPT_VERIFY_WITNESS_PUBKEYTYPE) != 0 && sigversion == SIGVERSION_WITNESS_V0 && !IsCompressedPubKey(vchPubKey)) { + return set_error(serror, SCRIPT_ERR_WITNESS_PUBKEYTYPE); + } return true; } @@ -428,6 +444,12 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (stack.size() < 1) return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL); valtype& vch = stacktop(-1); + if (sigversion == SIGVERSION_WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { + if (vch.size() > 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + if (vch.size() == 1 && vch[0] != 1) + return set_error(serror, SCRIPT_ERR_MINIMALIF); + } fValue = CastToBool(vch); if (opcode == OP_NOTIF) fValue = !fValue; @@ -834,15 +856,15 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un valtype& vch = stacktop(-1); valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); if (opcode == OP_RIPEMD160) - CRIPEMD160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CRIPEMD160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA1) - CSHA1().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA1().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_SHA256) - CSHA256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CSHA256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH160) - CHash160().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash160().Write(vch.data(), vch.size()).Finalize(vchHash.data()); else if (opcode == OP_HASH256) - CHash256().Write(begin_ptr(vch), vch.size()).Finalize(begin_ptr(vchHash)); + CHash256().Write(vch.data(), vch.size()).Finalize(vchHash.data()); popstack(stack); stack.push_back(vchHash); } @@ -868,17 +890,20 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signature, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SIGVERSION_BASE) { scriptCode.FindAndDelete(CScript(vchSig)); } - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { //serror is set return false; } bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion); + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + popstack(stack); popstack(stack); stack.push_back(fSuccess ? vchTrue : vchFalse); @@ -908,6 +933,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un if (nOpCount > MAX_OPS_PER_SCRIPT) return set_error(serror, SCRIPT_ERR_OP_COUNT); int ikey = ++i; + // ikey2 is the position of last non-signature item in the stack. Top stack item = 1. + // With SCRIPT_VERIFY_NULLFAIL, this is used for cleanup if operation fails. + int ikey2 = nKeysCount + 2; i += nKeysCount; if ((int)stack.size() < i) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); @@ -923,7 +951,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Subset of script starting at the most recent codeseparator CScript scriptCode(pbegincodehash, pend); - // Drop the signatures, since there's no way for a signature to sign itself + // Drop the signature in pre-segwit scripts but not segwit scripts for (int k = 0; k < nSigsCount; k++) { valtype& vchSig = stacktop(-isig-k); @@ -941,7 +969,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un // Note how this makes the exact order of pubkey/signature evaluation // distinguishable by CHECKMULTISIG NOT if the STRICTENC flag is set. // See the script_(in)valid tests for details. - if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, serror)) { + if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { // serror is set return false; } @@ -964,8 +992,14 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, un } // Clean up stack of actual arguments - while (i-- > 1) + while (i-- > 1) { + // If the operation failed, we require that all signatures must be empty vector + if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && !ikey2 && stacktop(-1).size()) + return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL); + if (ikey2 > 0) + ikey2--; popstack(stack); + } // A bug causes CHECKMULTISIG to consume one extra argument // whose contents were not checked in any way. @@ -1035,7 +1069,7 @@ public: /** Serialize the passed scriptCode, skipping OP_CODESEPARATORs */ template<typename S> - void SerializeScriptCode(S &s, int nType, int nVersion) const { + void SerializeScriptCode(S &s) const { CScript::const_iterator it = scriptCode.begin(); CScript::const_iterator itBegin = it; opcodetype opcode; @@ -1058,53 +1092,53 @@ public: /** Serialize an input of txTo */ template<typename S> - void SerializeInput(S &s, unsigned int nInput, int nType, int nVersion) const { + void SerializeInput(S &s, unsigned int nInput) const { // In case of SIGHASH_ANYONECANPAY, only the input being signed is serialized if (fAnyoneCanPay) nInput = nIn; // Serialize the prevout - ::Serialize(s, txTo.vin[nInput].prevout, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].prevout); // Serialize the script if (nInput != nIn) // Blank out other inputs' signatures - ::Serialize(s, CScriptBase(), nType, nVersion); + ::Serialize(s, CScriptBase()); else - SerializeScriptCode(s, nType, nVersion); + SerializeScriptCode(s); // Serialize the nSequence if (nInput != nIn && (fHashSingle || fHashNone)) // let the others update at will - ::Serialize(s, (int)0, nType, nVersion); + ::Serialize(s, (int)0); else - ::Serialize(s, txTo.vin[nInput].nSequence, nType, nVersion); + ::Serialize(s, txTo.vin[nInput].nSequence); } /** Serialize an output of txTo */ template<typename S> - void SerializeOutput(S &s, unsigned int nOutput, int nType, int nVersion) const { + void SerializeOutput(S &s, unsigned int nOutput) const { if (fHashSingle && nOutput != nIn) // Do not lock-in the txout payee at other indices as txin - ::Serialize(s, CTxOut(), nType, nVersion); + ::Serialize(s, CTxOut()); else - ::Serialize(s, txTo.vout[nOutput], nType, nVersion); + ::Serialize(s, txTo.vout[nOutput]); } /** Serialize txTo */ template<typename S> - void Serialize(S &s, int nType, int nVersion) const { + void Serialize(S &s) const { // Serialize nVersion - ::Serialize(s, txTo.nVersion, nType, nVersion); + ::Serialize(s, txTo.nVersion); // Serialize vin unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size(); ::WriteCompactSize(s, nInputs); for (unsigned int nInput = 0; nInput < nInputs; nInput++) - SerializeInput(s, nInput, nType, nVersion); + SerializeInput(s, nInput); // Serialize vout unsigned int nOutputs = fHashNone ? 0 : (fHashSingle ? nIn+1 : txTo.vout.size()); ::WriteCompactSize(s, nOutputs); for (unsigned int nOutput = 0; nOutput < nOutputs; nOutput++) - SerializeOutput(s, nOutput, nType, nVersion); + SerializeOutput(s, nOutput); // Serialize nLockTime - ::Serialize(s, txTo.nLockTime, nType, nVersion); + ::Serialize(s, txTo.nLockTime); } }; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index e5d7865cd..79894c530 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -94,6 +94,18 @@ enum // Making v1-v16 witness program non-standard // SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1U << 12), + + // Segwit script only: Require the argument of OP_IF/NOTIF to be exactly 0x01 or empty vector + // + SCRIPT_VERIFY_MINIMALIF = (1U << 13), + + // Signature(s) must be empty vector if an CHECK(MULTI)SIG operation failed + // + SCRIPT_VERIFY_NULLFAIL = (1U << 14), + + // Public keys in segregated witness scripts must be compressed + // + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), }; bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0bf180341..7467d23b2 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -29,13 +29,25 @@ unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore) return nResult; } -isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, scriptPubKey, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion) +{ + bool isInvalid = false; + return IsMine(keystore, dest, isInvalid, sigversion); +} + +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion) { CScript script = GetScriptForDestination(dest); - return IsMine(keystore, script); + return IsMine(keystore, script, isInvalid, sigversion); } -isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { vector<valtype> vSolutions; txnouttype whichType; @@ -53,12 +65,35 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); + if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; - case TX_PUBKEYHASH: case TX_WITNESS_V0_KEYHASH: + { + if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + // We do not support bare witness outputs unless the P2SH version of it would be + // acceptable as well. This protects against matching before segwit activates. + // This also applies to the P2WSH case. + break; + } + isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) + return ret; + break; + } + case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); + if (sigversion != SIGVERSION_BASE) { + CPubKey pubkey; + if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { + isInvalid = true; + return ISMINE_NO; + } + } if (keystore.HaveKey(keyID)) return ISMINE_SPENDABLE; break; @@ -67,21 +102,24 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; } case TX_WITNESS_V0_SCRIPTHASH: { + if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { + break; + } uint160 hash; CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin()); CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMine(keystore, subscript); - if (ret == ISMINE_SPENDABLE) + isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0); + if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) return ret; } break; @@ -95,6 +133,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); + if (sigversion != SIGVERSION_BASE) { + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i].size() != 33) { + isInvalid = true; + return ISMINE_NO; + } + } + } if (HaveKeys(keys, keystore) == keys.size()) return ISMINE_SPENDABLE; break; diff --git a/src/script/ismine.h b/src/script/ismine.h index 4b7db8802..ec7a620e3 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -28,7 +28,14 @@ enum isminetype /** used for bitflags of isminetype */ typedef uint8_t isminefilter; -isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); +/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion + * and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as + * different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed + * keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future + */ +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE); #endif // BITCOIN_SCRIPT_ISMINE_H diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index cef807edc..2c5359fe8 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -63,6 +63,10 @@ const char* ScriptErrorString(const ScriptError serror) return "Non-canonical signature: S value is unnecessarily high"; case SCRIPT_ERR_SIG_NULLDUMMY: return "Dummy CHECKMULTISIG argument must be zero"; + case SCRIPT_ERR_MINIMALIF: + return "OP_IF/NOTIF argument must be minimal"; + case SCRIPT_ERR_SIG_NULLFAIL: + return "Signature must be zero for failed CHECK(MULTI)SIG operation"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS: return "NOPx reserved for soft-fork upgrades"; case SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM: @@ -81,6 +85,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness requires only-redeemscript scriptSig"; case SCRIPT_ERR_WITNESS_UNEXPECTED: return "Witness provided for non-witness script"; + case SCRIPT_ERR_WITNESS_PUBKEYTYPE: + return "Using non-compressed keys in segwit"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 09dc6945a..430836991 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -39,7 +39,7 @@ typedef enum ScriptError_t SCRIPT_ERR_NEGATIVE_LOCKTIME, SCRIPT_ERR_UNSATISFIED_LOCKTIME, - /* BIP62 */ + /* Malleability */ SCRIPT_ERR_SIG_HASHTYPE, SCRIPT_ERR_SIG_DER, SCRIPT_ERR_MINIMALDATA, @@ -48,6 +48,8 @@ typedef enum ScriptError_t SCRIPT_ERR_SIG_NULLDUMMY, SCRIPT_ERR_PUBKEYTYPE, SCRIPT_ERR_CLEANSTACK, + SCRIPT_ERR_MINIMALIF, + SCRIPT_ERR_SIG_NULLFAIL, /* softfork safeness */ SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS, @@ -60,6 +62,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_MALLEATED, SCRIPT_ERR_WITNESS_MALLEATED_P2SH, SCRIPT_ERR_WITNESS_UNEXPECTED, + SCRIPT_ERR_WITNESS_PUBKEYTYPE, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index bdc0bfdc1..b78d7b607 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -11,20 +11,29 @@ #include "uint256.h" #include "util.h" +#include "cuckoocache.h" #include <boost/thread.hpp> -#include <boost/unordered_set.hpp> namespace { /** * We're hashing a nonce into the entries themselves, so we don't need extra * blinding in the set hash computation. + * + * This may exhibit platform endian dependent behavior but because these are + * nonced hashes (random) and this state is only ever used locally it is safe. + * All that matters is local consistency. */ -class CSignatureCacheHasher +class SignatureCacheHasher { public: - size_t operator()(const uint256& key) const { - return key.GetCheapHash(); + template <uint8_t hash_select> + uint32_t operator()(const uint256& key) const + { + static_assert(hash_select <8, "SignatureCacheHasher only has 8 hashes available."); + uint32_t u; + std::memcpy(&u, key.begin()+4*hash_select, 4); + return u; } }; @@ -38,11 +47,10 @@ class CSignatureCache private: //! Entries are SHA256(nonce || signature hash || public key || signature): uint256 nonce; - typedef boost::unordered_set<uint256, CSignatureCacheHasher> map_type; + typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type; map_type setValid; boost::shared_mutex cs_sigcache; - public: CSignatureCache() { @@ -56,58 +64,51 @@ public: } bool - Get(const uint256& entry) + Get(const uint256& entry, const bool erase) { boost::shared_lock<boost::shared_mutex> lock(cs_sigcache); - return setValid.count(entry); + return setValid.contains(entry, erase); } - void Erase(const uint256& entry) + void Set(uint256& entry) { boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); - setValid.erase(entry); + setValid.insert(entry); } - - void Set(const uint256& entry) + uint32_t setup_bytes(size_t n) { - size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); - while (memusage::DynamicUsage(setValid) > nMaxCacheSize) - { - map_type::size_type s = GetRand(setValid.bucket_count()); - map_type::local_iterator it = setValid.begin(s); - if (it != setValid.end(s)) { - setValid.erase(*it); - } - } - - setValid.insert(entry); + return setValid.setup_bytes(n); } }; +/* In previous versions of this code, signatureCache was a local static variable + * in CachingTransactionSignatureChecker::VerifySignature. We initialize + * signatureCache outside of VerifySignature to avoid the atomic operation per + * call overhead associated with local static variables even though + * signatureCache could be made local to VerifySignature. +*/ +static CSignatureCache signatureCache; } -bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +// To be called once in AppInit2/TestingSetup to initialize the signatureCache +void InitSignatureCache() { - static CSignatureCache signatureCache; + size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); + if (nMaxCacheSize <= 0) return; + size_t nElems = signatureCache.setup_bytes(nMaxCacheSize); + LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n", + (nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems); +} +bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +{ uint256 entry; signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey); - - if (signatureCache.Get(entry)) { - if (!store) { - signatureCache.Erase(entry); - } + if (signatureCache.Get(entry, !store)) return true; - } - if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; - - if (store) { + if (store) signatureCache.Set(entry); - } return true; } diff --git a/src/script/sigcache.h b/src/script/sigcache.h index 44551ec2b..5243fc0a4 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -10,9 +10,10 @@ #include <vector> -// DoS prevention: limit cache size to less than 40MB (over 500000 -// entries on 64-bit systems). -static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 40; +// DoS prevention: limit cache size to 32MB (over 1000000 entries on 64-bit +// systems). Due to how we count cache size, actual memory usage is slightly +// more (~32.25 MB) +static const unsigned int DEFAULT_MAX_SIG_CACHE_SIZE = 32; class CPubKey; @@ -27,4 +28,6 @@ public: bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; +void InitSignatureCache(); + #endif // BITCOIN_SCRIPT_SIGCACHE_H diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 87f38d9c7..f552ad5bb 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2015 The Bitcoin Core developers +// Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -26,6 +26,10 @@ bool TransactionSignatureCreator::CreateSig(std::vector<unsigned char>& vchSig, if (!keystore->GetKey(address, key)) return false; + // Signing with uncompressed keys is disabled in witness scripts + if (sigversion == SIGVERSION_WITNESS_V0 && !key.IsCompressed()) + return false; + uint256 hash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion); if (!key.Sign(hash, vchSig)) return false; |