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.
This commit is contained in:
parent
d1edab602a
commit
acf513cfe7
65
src/key.cpp
65
src/key.cpp
@ -4,13 +4,10 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <boost/tuple/tuple.hpp>
|
|
||||||
#include <openssl/ecdsa.h>
|
#include <openssl/ecdsa.h>
|
||||||
#include <openssl/obj_mac.h>
|
#include <openssl/obj_mac.h>
|
||||||
|
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "sync.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
// Generate a private key from just the secret parameter
|
// Generate a private key from just the secret parameter
|
||||||
int EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
|
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;
|
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)
|
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
|
// -1 = error, 0 = bad sig, 1 = good
|
||||||
if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
|
if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// good sig
|
|
||||||
SetValidSigCache(hash, vchSig, GetPubKey());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
|
bool CKey::VerifyCompact(uint256 hash, const std::vector<unsigned char>& vchSig)
|
||||||
{
|
{
|
||||||
if (GetValidSigCache(hash, vchSig, GetPubKey()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
CKey key;
|
CKey key;
|
||||||
if (!key.SetCompactSignature(hash, vchSig))
|
if (!key.SetCompactSignature(hash, vchSig))
|
||||||
return false;
|
return false;
|
||||||
if (GetPubKey() != key.GetPubKey())
|
if (GetPubKey() != key.GetPubKey())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SetValidSigCache(hash, vchSig, GetPubKey());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/tuple/tuple.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
@ -12,6 +13,8 @@ using namespace boost;
|
|||||||
#include "bignum.h"
|
#include "bignum.h"
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "main.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);
|
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,
|
bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CScript scriptCode,
|
||||||
const CTransaction& txTo, unsigned int nIn, int nHashType)
|
const CTransaction& txTo, unsigned int nIn, int nHashType)
|
||||||
{
|
{
|
||||||
CKey key;
|
static CSignatureCache signatureCache;
|
||||||
if (!key.SetPubKey(vchPubKey))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Hash type is one byte tacked on to the end of the signature
|
// Hash type is one byte tacked on to the end of the signature
|
||||||
if (vchSig.empty())
|
if (vchSig.empty())
|
||||||
@ -1115,7 +1173,20 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
|
|||||||
return false;
|
return false;
|
||||||
vchSig.pop_back();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user