From 5c1e798a8e9df15f8fbd120e57fc67e585f13843 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Wed, 10 Sep 2014 16:16:09 +0200 Subject: Make signature cache optional --- src/script/interpreter.cpp | 74 ++++---------------------------------- src/script/interpreter.h | 34 ++++++++++-------- src/script/sigcache.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++ src/script/sigcache.h | 34 ++++++++++++++++++ src/script/sign.cpp | 6 ++-- 5 files changed, 150 insertions(+), 86 deletions(-) create mode 100644 src/script/sigcache.cpp create mode 100644 src/script/sigcache.h (limited to 'src/script') diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 048d298f0b..f0b4268390 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -9,14 +9,10 @@ #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha2.h" -#include "random.h" #include "script/script.h" #include "uint256.h" #include "util.h" -#include -#include - using namespace std; typedef vector valtype; @@ -132,7 +128,7 @@ bool IsCanonicalSignature(const valtype &vchSig, unsigned int flags) { return true; } -bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const SignatureChecker& checker) +bool EvalScript(vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker) { CScript::const_iterator pc = script.begin(); CScript::const_iterator pend = script.end(); @@ -921,65 +917,13 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -// Valid signature cache, to avoid doing expensive ECDSA signature checking -// twice for every transaction (once when accepted into memory pool, and -// again when accepted into the block chain) -class CSignatureCache +bool SignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash, int flags) const { -private: - // sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, CPubKey> sigdata_type; - std::set< sigdata_type> setValid; - boost::shared_mutex cs_sigcache; - -public: - bool - Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) - { - boost::shared_lock lock(cs_sigcache); - - sigdata_type k(hash, vchSig, pubKey); - std::set::iterator mi = setValid.find(k); - if (mi != setValid.end()) - return true; - return false; - } - - void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) - { - // DoS prevention: limit cache size to less than 10MB - // (~200 bytes per cache entry times 50,000 entries) - // Since there are a maximum of 20,000 signature operations per block - // 50,000 is a reasonable default. - int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); - if (nMaxCacheSize <= 0) return; - - boost::unique_lock lock(cs_sigcache); - - while (static_cast(setValid.size()) > nMaxCacheSize) - { - // Evict a random entry. Random because that helps - // foil would-be DoS attackers who might try to pre-generate - // and re-use a set of valid signatures just-slightly-greater - // than our cache size. - uint256 randomHash = GetRandHash(); - std::vector unused; - std::set::iterator it = - setValid.lower_bound(sigdata_type(randomHash, unused, unused)); - if (it == setValid.end()) - it = setValid.begin(); - setValid.erase(*it); - } - - sigdata_type k(hash, vchSig, pubKey); - setValid.insert(k); - } -}; + return pubkey.Verify(sighash, vchSig); +} bool SignatureChecker::CheckSig(const vector& vchSigIn, const vector& vchPubKey, const CScript& scriptCode, int flags) const { - static CSignatureCache signatureCache; - CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) return false; @@ -993,19 +937,13 @@ bool SignatureChecker::CheckSig(const vector& vchSigIn, const vec uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); - if (signatureCache.Get(sighash, vchSig, pubkey)) - return true; - - if (!pubkey.Verify(sighash, vchSig)) + if (!VerifySignature(vchSig, pubkey, sighash, flags)) return false; - if (!(flags & SCRIPT_VERIFY_NOCACHE)) - signatureCache.Set(sighash, vchSig, pubkey); - return true; } -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const SignatureChecker& checker) +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker) { vector > stack, stackCopy; if (!EvalScript(stack, scriptSig, flags, checker)) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index f3fd49a3b6..801f7e0ce1 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -10,9 +10,10 @@ #include #include +class uint256; +class CPubKey; class CScript; class CTransaction; -class uint256; /** Signature hash types/flags */ enum @@ -39,29 +40,32 @@ bool IsCanonicalSignature(const std::vector &vchSig, unsigned int uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); -class SignatureChecker +class BaseSignatureChecker +{ +public: + virtual bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, int nFlags) const + { + return false; + } + + virtual ~BaseSignatureChecker() {} +}; + +class SignatureChecker : public BaseSignatureChecker { private: const CTransaction& txTo; unsigned int nIn; +protected: + virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash, int flags) const; + public: SignatureChecker(const CTransaction& txToIn, unsigned int nInIn) : txTo(txToIn), nIn(nInIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, int nFlags) const; }; -bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const SignatureChecker& checker); -bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const SignatureChecker& checker); - -// Wrappers using a default SignatureChecker. -bool inline EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags) -{ - return EvalScript(stack, script, flags, SignatureChecker(txTo, nIn)); -} - -bool inline VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags) -{ - return VerifyScript(scriptSig, scriptPubKey, flags, SignatureChecker(txTo, nIn)); -} +bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker); +bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, unsigned int flags, const BaseSignatureChecker& checker); #endif // H_BITCOIN_SCRIPT_INTERPRETER diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp new file mode 100644 index 0000000000..a1dec64dbc --- /dev/null +++ b/src/script/sigcache.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "sigcache.h" + +#include "key.h" +#include "random.h" +#include "uint256.h" +#include "util.h" + +#include +#include + +namespace { + +// Valid signature cache, to avoid doing expensive ECDSA signature checking +// twice for every transaction (once when accepted into memory pool, and +// again when accepted into the block chain) +class CSignatureCache +{ +private: + // sigdata_type is (signature hash, signature, public key): + typedef boost::tuple, CPubKey> sigdata_type; + std::set< sigdata_type> setValid; + boost::shared_mutex cs_sigcache; + +public: + bool + Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + boost::shared_lock lock(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + { + // DoS prevention: limit cache size to less than 10MB + // (~200 bytes per cache entry times 50,000 entries) + // Since there are a maximum of 20,000 signature operations per block + // 50,000 is a reasonable default. + int64_t nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + boost::unique_lock lock(cs_sigcache); + + while (static_cast(setValid.size()) > nMaxCacheSize) + { + // Evict a random entry. Random because that helps + // foil would-be DoS attackers who might try to pre-generate + // and re-use a set of valid signatures just-slightly-greater + // than our cache size. + uint256 randomHash = GetRandHash(); + std::vector unused; + std::set::iterator it = + setValid.lower_bound(sigdata_type(randomHash, unused, unused)); + if (it == setValid.end()) + it = setValid.begin(); + setValid.erase(*it); + } + + sigdata_type k(hash, vchSig, pubKey); + setValid.insert(k); + } +}; + +} + +bool CachingSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash, int flags) const +{ + static CSignatureCache signatureCache; + + if (signatureCache.Get(sighash, vchSig, pubkey)) + return true; + + if (!SignatureChecker::VerifySignature(vchSig, pubkey, sighash, flags)) + return false; + + if (!(flags & SCRIPT_VERIFY_NOCACHE)) + signatureCache.Set(sighash, vchSig, pubkey); + return true; +} diff --git a/src/script/sigcache.h b/src/script/sigcache.h new file mode 100644 index 0000000000..8faa7ae01f --- /dev/null +++ b/src/script/sigcache.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2013 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef H_BITCOIN_SCRIPT_SIGCACHE +#define H_BITCOIN_SCRIPT_SIGCACHE + +#include "script/interpreter.h" + +#include + +class CPubKey; + +class CachingSignatureChecker : public SignatureChecker +{ +public: + CachingSignatureChecker(const CTransaction& txToIn, unsigned int nInIn) : SignatureChecker(txToIn, nInIn) {} + + bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash, int flags) const; +}; + +// Wrappers using a default SignatureChecker. +bool inline EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags) +{ + return EvalScript(stack, script, flags, CachingSignatureChecker(txTo, nIn)); +} + +bool inline VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, unsigned int flags) +{ + return VerifyScript(scriptSig, scriptPubKey, flags, CachingSignatureChecker(txTo, nIn)); +} + +#endif diff --git a/src/script/sign.cpp b/src/script/sign.cpp index a17fb58787..a524ac8e5e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -123,7 +123,7 @@ bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutabl } // Test solution - return VerifyScript(txin.scriptSig, fromPubKey, txTo, nIn, STANDARD_SCRIPT_VERIFY_FLAGS); + return VerifyScript(txin.scriptSig, fromPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, SignatureChecker(txTo, nIn)); } bool SignSignature(const CKeyStore &keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType) @@ -252,9 +252,9 @@ CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, unsign Solver(scriptPubKey, txType, vSolutions); vector stack1; - EvalScript(stack1, scriptSig1, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC); + EvalScript(stack1, scriptSig1, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); vector stack2; - EvalScript(stack2, scriptSig2, CTransaction(), 0, SCRIPT_VERIFY_STRICTENC); + EvalScript(stack2, scriptSig2, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker()); return CombineSignatures(scriptPubKey, txTo, nIn, txType, vSolutions, stack1, stack2); } -- cgit v1.2.3