aboutsummaryrefslogtreecommitdiff
path: root/src/script/sigcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/script/sigcache.cpp')
-rw-r--r--src/script/sigcache.cpp113
1 files changed, 69 insertions, 44 deletions
diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp
index 099b4ad0e..6f364e42d 100644
--- a/src/script/sigcache.cpp
+++ b/src/script/sigcache.cpp
@@ -1,21 +1,43 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 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.
#include "sigcache.h"
+#include "memusage.h"
#include "pubkey.h"
#include "random.h"
#include "uint256.h"
#include "util.h"
+#include "cuckoocache.h"
#include <boost/thread.hpp>
-#include <boost/tuple/tuple_comparison.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 SignatureCacheHasher
+{
+public:
+ 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;
+ }
+};
+
+/**
* 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)
@@ -23,68 +45,71 @@ namespace {
class CSignatureCache
{
private:
- //! sigdata_type is (signature hash, signature, public key):
- typedef boost::tuple<uint256, std::vector<unsigned char>, CPubKey> sigdata_type;
- std::set< sigdata_type> setValid;
+ //! Entries are SHA256(nonce || signature hash || public key || signature):
+ uint256 nonce;
+ typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
+ map_type setValid;
boost::shared_mutex cs_sigcache;
public:
- bool
- Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
+ CSignatureCache()
{
- boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
+ GetRandBytes(nonce.begin(), 32);
+ }
- sigdata_type k(hash, vchSig, pubKey);
- std::set<sigdata_type>::iterator mi = setValid.find(k);
- if (mi != setValid.end())
- return true;
- return false;
+ void
+ ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& 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());
}
- void Set(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey)
+ bool
+ Get(const uint256& entry, const bool erase)
{
- // 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::shared_lock<boost::shared_mutex> lock(cs_sigcache);
+ return setValid.contains(entry, erase);
+ }
+ void Set(uint256& entry)
+ {
boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
-
- while (static_cast<int64_t>(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<unsigned char> unused;
- std::set<sigdata_type>::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);
+ setValid.insert(entry);
+ }
+ uint32_t setup_bytes(size_t n)
+ {
+ 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;
+ // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
+ // setup_bytes creates the minimum possible cache (2 elements).
+ size_t nMaxCacheSize = std::min(std::max((int64_t)0, GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE)), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
+ 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);
+}
- if (signatureCache.Get(sighash, vchSig, pubkey))
+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, !store))
return true;
-
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
return false;
-
if (store)
- signatureCache.Set(sighash, vchSig, pubkey);
+ signatureCache.Set(entry);
return true;
}