Mercurial > hg > bitcoin
changeset 2809:caa08770229d draft
Move signature cache from CKey::Verify to CheckSig in script.cpp
More than doubles the speed of verifying already-cached signatures
that use compressed pubkeys:
Before: ~200 microseconds
After: ~80 microseconds
(no caching at all: ~3,300 microseconds per signature)
Also encapsulates the signature cache code in a class
and fixes a signed/unsigned comparison warning.
author | Gavin Andresen <gavinandresen@gmail.com> |
---|---|
date | Tue, 22 May 2012 13:56:14 -0400 |
parents | cadfaef4bc6e |
children | a2f7bc9841e0 |
files | src/key.cpp src/script.cpp |
diffstat | 2 files changed, 74 insertions(+), 67 deletions(-) [+] |
line wrap: on
line diff
--- a/src/key.cpp +++ b/src/key.cpp @@ -4,7 +4,6 @@ #include <map> -#include <boost/tuple/tuple.hpp> #include <openssl/ecdsa.h> #include <openssl/obj_mac.h> @@ -351,85 +350,23 @@ return false; } -// 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) - -// sigdata_type is (signature hash, signature, public key): -typedef boost::tuple<uint256, std::vector<unsigned char>, std::vector<unsigned char> > sigdata_type; -static std::set< sigdata_type> setValidSigCache; -static CCriticalSection cs_sigcache; - -static bool -GetValidSigCache(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey) -{ - LOCK(cs_sigcache); - - sigdata_type k(hash, vchSig, pubKey); - std::set<sigdata_type>::iterator mi = setValidSigCache.find(k); - if (mi != setValidSigCache.end()) - return true; - return false; -} - -static void -SetValidSigCache(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& 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 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); - if (nMaxCacheSize <= 0) return; - - LOCK(cs_sigcache); - - while (setValidSigCache.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 = - setValidSigCache.lower_bound(sigdata_type(randomHash, unused, unused)); - if (it == setValidSigCache.end()) - it = setValidSigCache.begin(); - setValidSigCache.erase(*it); - } - - sigdata_type k(hash, vchSig, pubKey); - setValidSigCache.insert(k); -} - - bool CKey::Verify(uint256 hash, const std::vector<unsigned char>& vchSig) { - if (GetValidSigCache(hash, vchSig, GetPubKey())) - return true; - // -1 = error, 0 = bad sig, 1 = good if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1) return false; - // good sig - SetValidSigCache(hash, vchSig, GetPubKey()); return true; } bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig) { - if (GetValidSigCache(hash, vchSig, GetPubKey())) - return true; - CKey key; if (!key.SetCompactSignature(hash, vchSig)) return false; if (GetPubKey() != key.GetPubKey()) return false; - SetValidSigCache(hash, vchSig, GetPubKey()); return true; }
--- a/src/script.cpp +++ b/src/script.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #include <boost/foreach.hpp> +#include <boost/tuple/tuple.hpp> using namespace std; using namespace boost; @@ -12,6 +13,7 @@ #include "bignum.h" #include "key.h" #include "main.h" +#include "util.h" bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); @@ -1099,12 +1101,67 @@ } +// 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<uint256, std::vector<unsigned char>, std::vector<unsigned char> > sigdata_type; + std::set< sigdata_type> setValid; + CCriticalSection cs_sigcache; + +public: + bool + Get(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& pubKey) + { + LOCK(cs_sigcache); + + sigdata_type k(hash, vchSig, pubKey); + std::set<sigdata_type>::iterator mi = setValid.find(k); + if (mi != setValid.end()) + return true; + return false; + } + + void + Set(uint256 hash, const std::vector<unsigned char>& vchSig, const std::vector<unsigned char>& 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 nMaxCacheSize = GetArg("-maxsigcachesize", 50000); + if (nMaxCacheSize <= 0) return; + + LOCK(cs_sigcache); + + while (static_cast<int64>(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); + } +}; + bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) { - CKey key; - if (!key.SetPubKey(vchPubKey)) - return false; + static CSignatureCache signatureCache; // Hash type is one byte tacked on to the end of the signature if (vchSig.empty()) @@ -1115,7 +1172,20 @@ return false; vchSig.pop_back(); - return key.Verify(SignatureHash(scriptCode, txTo, nIn, nHashType), vchSig); + uint256 sighash = SignatureHash(scriptCode, txTo, nIn, nHashType); + + if (signatureCache.Get(sighash, vchSig, vchPubKey)) + return true; + + CKey key; + if (!key.SetPubKey(vchPubKey)) + return false; + + if (!key.Verify(sighash, vchSig)) + return false; + + signatureCache.Set(sighash, vchSig, vchPubKey); + return true; }