From 830e3f3d027ba5c8121eed0f6a9ce99961352572 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 30 Oct 2015 23:14:38 +0100 Subject: Make sigcache faster and more efficient --- src/script/sigcache.cpp | 80 ++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 34 deletions(-) (limited to 'src/script/sigcache.cpp') diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 099b4ad0e3..9dc7f0fcd5 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,16 +5,29 @@ #include "sigcache.h" +#include "memusage.h" #include "pubkey.h" #include "random.h" #include "uint256.h" #include "util.h" #include -#include +#include namespace { +/** + * We're hashing a nonce into the entries themselves, so we don't need extra + * blinding in the set hash computation. + */ +class CSignatureCacheHasher +{ +public: + size_t operator()(const uint256& key) const { + return key.GetCheapHash(); + } +}; + /** * Valid signature cache, to avoid doing expensive ECDSA signature checking * twice for every transaction (once when accepted into memory pool, and @@ -23,52 +36,48 @@ namespace { class CSignatureCache { private: - //! sigdata_type is (signature hash, signature, public key): - typedef boost::tuple, CPubKey> sigdata_type; - std::set< sigdata_type> setValid; + //! Entries are SHA256(nonce || signature hash || public key || signature): + uint256 nonce; + typedef boost::unordered_set map_type; + map_type setValid; boost::shared_mutex cs_sigcache; + public: + CSignatureCache() + { + GetRandBytes(nonce.begin(), 32); + } + + void + ComputeEntry(uint256& entry, const uint256 &hash, const std::vector& vchSig, const CPubKey& pubkey) + { + CSHA256().Write(nonce.begin(), 32).Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin()); + } + bool - Get(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + Get(const uint256& entry) { 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; + return setValid.count(entry); } - void Set(const uint256 &hash, const std::vector& vchSig, const CPubKey& pubKey) + void Set(const uint256& entry) { - // 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); + size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20); if (nMaxCacheSize <= 0) return; boost::unique_lock lock(cs_sigcache); - - while (static_cast(setValid.size()) > nMaxCacheSize) + while (memusage::DynamicUsage(setValid) > 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); + 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); + } } - sigdata_type k(hash, vchSig, pubKey); - setValid.insert(k); + setValid.insert(entry); } }; @@ -78,13 +87,16 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector