5
5
6
6
#include " sigcache.h"
7
7
8
+ #include " memusage.h"
8
9
#include " pubkey.h"
9
10
#include " random.h"
10
11
#include " uint256.h"
11
12
#include " util.h"
12
13
13
14
#include < boost/thread.hpp>
14
- #include < boost/tuple/tuple_comparison .hpp>
15
+ #include < boost/unordered_set .hpp>
15
16
16
17
namespace {
17
18
19
+ /* *
20
+ * We're hashing a nonce into the entries themselves, so we don't need extra
21
+ * blinding in the set hash computation.
22
+ */
23
+ class CSignatureCacheHasher
24
+ {
25
+ public:
26
+ size_t operator ()(const uint256& key) const {
27
+ return key.GetCheapHash ();
28
+ }
29
+ };
30
+
18
31
/* *
19
32
* Valid signature cache, to avoid doing expensive ECDSA signature checking
20
33
* twice for every transaction (once when accepted into memory pool, and
@@ -23,52 +36,48 @@ namespace {
23
36
class CSignatureCache
24
37
{
25
38
private:
26
- // ! sigdata_type is (signature hash, signature, public key):
27
- typedef boost::tuple<uint256, std::vector<unsigned char >, CPubKey> sigdata_type;
28
- std::set< sigdata_type> setValid;
39
+ // ! Entries are SHA256(nonce || signature hash || public key || signature):
40
+ uint256 nonce;
41
+ typedef boost::unordered_set<uint256, CSignatureCacheHasher> map_type;
42
+ map_type setValid;
29
43
boost::shared_mutex cs_sigcache;
30
44
45
+
31
46
public:
47
+ CSignatureCache ()
48
+ {
49
+ GetRandBytes (nonce.begin (), 32 );
50
+ }
51
+
52
+ void
53
+ ComputeEntry (uint256& entry, const uint256 &hash, const std::vector<unsigned char >& vchSig, const CPubKey& pubkey)
54
+ {
55
+ CSHA256 ().Write (nonce.begin (), 32 ).Write (hash.begin (), 32 ).Write (&pubkey[0 ], pubkey.size ()).Write (&vchSig[0 ], vchSig.size ()).Finalize (entry.begin ());
56
+ }
57
+
32
58
bool
33
- Get (const uint256 &hash, const std::vector< unsigned char >& vchSig, const CPubKey& pubKey )
59
+ Get (const uint256& entry )
34
60
{
35
61
boost::shared_lock<boost::shared_mutex> lock (cs_sigcache);
36
-
37
- sigdata_type k (hash, vchSig, pubKey);
38
- std::set<sigdata_type>::iterator mi = setValid.find (k);
39
- if (mi != setValid.end ())
40
- return true ;
41
- return false ;
62
+ return setValid.count (entry);
42
63
}
43
64
44
- void Set (const uint256 &hash, const std::vector< unsigned char >& vchSig, const CPubKey& pubKey )
65
+ void Set (const uint256& entry )
45
66
{
46
- // DoS prevention: limit cache size to less than 10MB
47
- // (~200 bytes per cache entry times 50,000 entries)
48
- // Since there are a maximum of 20,000 signature operations per block
49
- // 50,000 is a reasonable default.
50
- int64_t nMaxCacheSize = GetArg (" -maxsigcachesize" , 50000 );
67
+ size_t nMaxCacheSize = GetArg (" -maxsigcachesize" , DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t ) 1 << 20 );
51
68
if (nMaxCacheSize <= 0 ) return ;
52
69
53
70
boost::unique_lock<boost::shared_mutex> lock (cs_sigcache);
54
-
55
- while (static_cast <int64_t >(setValid.size ()) > nMaxCacheSize)
71
+ while (memusage::DynamicUsage (setValid) > nMaxCacheSize)
56
72
{
57
- // Evict a random entry. Random because that helps
58
- // foil would-be DoS attackers who might try to pre-generate
59
- // and re-use a set of valid signatures just-slightly-greater
60
- // than our cache size.
61
- uint256 randomHash = GetRandHash ();
62
- std::vector<unsigned char > unused;
63
- std::set<sigdata_type>::iterator it =
64
- setValid.lower_bound (sigdata_type (randomHash, unused, unused));
65
- if (it == setValid.end ())
66
- it = setValid.begin ();
67
- setValid.erase (*it);
73
+ map_type::size_type s = GetRand (setValid.bucket_count ());
74
+ map_type::local_iterator it = setValid.begin (s);
75
+ if (it != setValid.end (s)) {
76
+ setValid.erase (*it);
77
+ }
68
78
}
69
79
70
- sigdata_type k (hash, vchSig, pubKey);
71
- setValid.insert (k);
80
+ setValid.insert (entry);
72
81
}
73
82
};
74
83
@@ -78,13 +87,16 @@ bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsig
78
87
{
79
88
static CSignatureCache signatureCache;
80
89
81
- if (signatureCache.Get (sighash, vchSig, pubkey))
90
+ uint256 entry;
91
+ signatureCache.ComputeEntry (entry, sighash, vchSig, pubkey);
92
+
93
+ if (signatureCache.Get (entry))
82
94
return true ;
83
95
84
96
if (!TransactionSignatureChecker::VerifySignature (vchSig, pubkey, sighash))
85
97
return false ;
86
98
87
99
if (store)
88
- signatureCache.Set (sighash, vchSig, pubkey );
100
+ signatureCache.Set (entry );
89
101
return true ;
90
102
}
0 commit comments