aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2012-05-22 13:56:14 -0400
committerGavin Andresen <gavinandresen@gmail.com>2012-05-22 14:15:11 -0400
commitacf513cfe75f43165ac626a608ce9cde817d97bc (patch)
tree4a9ca1fcc82b33e210813c1cdf71e1861cf0c26f
parentd1edab602a3ee729b308378d9d456f01e6933cfb (diff)
downloadbitcoin-acf513cfe75f43165ac626a608ce9cde817d97bc.tar.xz
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.
-rw-r--r--src/key.cpp65
-rw-r--r--src/script.cpp79
2 files changed, 75 insertions, 69 deletions
diff --git a/src/key.cpp b/src/key.cpp
index 4172d6be5e..9485b477cb 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -4,13 +4,10 @@
#include <map>
-#include <boost/tuple/tuple.hpp>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include "key.h"
-#include "sync.h"
-#include "util.h"
// Generate a private key from just the secret parameter
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
@@ -352,85 +349,23 @@ bool CKey::SetCompactSignature(uint256 hash, const std::vector<unsigned char>& v
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;
}
diff --git a/src/script.cpp b/src/script.cpp
index f7c2d316f2..0620fba234 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -3,6 +3,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING 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,8 @@ using namespace boost;
#include "bignum.h"
#include "key.h"
#include "main.h"
+#include "sync.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 +1102,67 @@ uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int
}
+// 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 +1173,20 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
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;
}