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/sigcache.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/script/sigcache.cpp (limited to 'src/script/sigcache.cpp') 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; +} -- cgit v1.2.3 From e790c370b5971dd096d1bbfd55960ccf71b7594a Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 14 Sep 2014 04:48:32 +0200 Subject: Replace SCRIPT_VERIFY_NOCACHE by flag directly to checker --- src/script/sigcache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/script/sigcache.cpp') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index a1dec64dbc..981563b7ae 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -72,17 +72,17 @@ public: } -bool CachingSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash, int flags) const +bool CachingSignatureChecker::VerifySignature(const std::vector& vchSig, const CPubKey& pubkey, const uint256& sighash) const { static CSignatureCache signatureCache; if (signatureCache.Get(sighash, vchSig, pubkey)) return true; - if (!SignatureChecker::VerifySignature(vchSig, pubkey, sighash, flags)) + if (!SignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; - if (!(flags & SCRIPT_VERIFY_NOCACHE)) + if (store) signatureCache.Set(sighash, vchSig, pubkey); return true; } -- cgit v1.2.3