diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2015-11-12 17:37:42 +0100 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2015-11-12 17:37:52 +0100 |
commit | eb6172a8ca7e0474457c1206c9907514348243ea (patch) | |
tree | a1c38ef1b59210deb1c2d60ebe0d13869f48efe6 /src | |
parent | 54e8bfec8387ee154f8b15b41ae0b29bc2cd9bbc (diff) | |
parent | 69d373ff6693de204bdf58cbc90f8a26d8f711c8 (diff) |
Merge pull request #6918
69d373f Don't wipe the sigcache in TestBlockValidity (Pieter Wuille)
0b9e9dc Evict sigcache entries that are seen in a block (Pieter Wuille)
830e3f3 Make sigcache faster and more efficient (Pieter Wuille)
Diffstat (limited to 'src')
-rw-r--r-- | src/init.cpp | 3 | ||||
-rw-r--r-- | src/main.cpp | 3 | ||||
-rw-r--r-- | src/script/sigcache.cpp | 91 | ||||
-rw-r--r-- | src/script/sigcache.h | 4 |
4 files changed, 65 insertions, 36 deletions
diff --git a/src/init.cpp b/src/init.cpp index 5f2dc8bf2f..921fc0b64a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -25,6 +25,7 @@ #include "policy/policy.h" #include "rpcserver.h" #include "script/standard.h" +#include "script/sigcache.h" #include "scheduler.h" #include "txdb.h" #include "txmempool.h" @@ -437,7 +438,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); strUsage += HelpMessageOpt("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to <n>*1000 bytes per minute (default: %u)", 15)); strUsage += HelpMessageOpt("-relaypriority", strprintf("Require high priority for relaying free or low-fee transactions (default: %u)", 1)); - strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> entries (default: %u)", 50000)); + strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); } strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); diff --git a/src/main.cpp b/src/main.cpp index 6e5a81f306..3703701917 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1851,7 +1851,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin nFees += view.GetValueIn(tx)-tx.GetValueOut(); std::vector<CScriptCheck> vChecks; - if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) + bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ + if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, nScriptCheckThreads ? &vChecks : NULL)) return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetHash().ToString(), FormatStateMessage(state)); control.Add(vChecks); diff --git a/src/script/sigcache.cpp b/src/script/sigcache.cpp index 099b4ad0e3..eee96e7c2d 100644 --- a/src/script/sigcache.cpp +++ b/src/script/sigcache.cpp @@ -5,17 +5,30 @@ #include "sigcache.h" +#include "memusage.h" #include "pubkey.h" #include "random.h" #include "uint256.h" #include "util.h" #include <boost/thread.hpp> -#include <boost/tuple/tuple_comparison.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. + */ +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 * again when accepted into the block chain) @@ -23,52 +36,54 @@ 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 boost::unordered_set<uint256, CSignatureCacheHasher> 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<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()); + } + bool - Get(const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubKey) + Get(const uint256& entry) { boost::shared_lock<boost::shared_mutex> lock(cs_sigcache); + return setValid.count(entry); + } - sigdata_type k(hash, vchSig, pubKey); - std::set<sigdata_type>::iterator mi = setValid.find(k); - if (mi != setValid.end()) - return true; - return false; + void Erase(const uint256& entry) + { + boost::unique_lock<boost::shared_mutex> lock(cs_sigcache); + setValid.erase(entry); } - void Set(const uint256 &hash, const std::vector<unsigned char>& 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<boost::shared_mutex> lock(cs_sigcache); - - while (static_cast<int64_t>(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<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); + 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 +93,21 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsig { static CSignatureCache signatureCache; - if (signatureCache.Get(sighash, vchSig, pubkey)) + uint256 entry; + signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey); + + if (signatureCache.Get(entry)) { + if (!store) { + signatureCache.Erase(entry); + } return true; + } if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash)) return false; - if (store) - signatureCache.Set(sighash, vchSig, pubkey); + if (store) { + signatureCache.Set(entry); + } return true; } diff --git a/src/script/sigcache.h b/src/script/sigcache.h index b299038daa..2269972560 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -10,6 +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; + class CPubKey; class CachingTransactionSignatureChecker : public TransactionSignatureChecker |