sigcache.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2022 The Bitcoin Core developers 3 // Distributed under the MIT software license, see the accompanying 4 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 #include <script/sigcache.h> 7 8 #include <common/system.h> 9 #include <logging.h> 10 #include <pubkey.h> 11 #include <random.h> 12 #include <uint256.h> 13 14 #include <cuckoocache.h> 15 16 #include <algorithm> 17 #include <mutex> 18 #include <optional> 19 #include <shared_mutex> 20 #include <vector> 21 22 namespace { 23 /** 24 * Valid signature cache, to avoid doing expensive ECDSA signature checking 25 * twice for every transaction (once when accepted into memory pool, and 26 * again when accepted into the block chain) 27 */ 28 class CSignatureCache 29 { 30 private: 31 //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature): 32 CSHA256 m_salted_hasher_ecdsa; 33 CSHA256 m_salted_hasher_schnorr; 34 typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type; 35 map_type setValid; 36 std::shared_mutex cs_sigcache; 37 38 public: 39 CSignatureCache() 40 { 41 uint256 nonce = GetRandHash(); 42 // We want the nonce to be 64 bytes long to force the hasher to process 43 // this chunk, which makes later hash computations more efficient. We 44 // just write our 32-byte entropy, and then pad with 'E' for ECDSA and 45 // 'S' for Schnorr (followed by 0 bytes). 46 static constexpr unsigned char PADDING_ECDSA[32] = {'E'}; 47 static constexpr unsigned char PADDING_SCHNORR[32] = {'S'}; 48 m_salted_hasher_ecdsa.Write(nonce.begin(), 32); 49 m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32); 50 m_salted_hasher_schnorr.Write(nonce.begin(), 32); 51 m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32); 52 } 53 54 void 55 ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const 56 { 57 CSHA256 hasher = m_salted_hasher_ecdsa; 58 hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(vchSig.data(), vchSig.size()).Finalize(entry.begin()); 59 } 60 61 void 62 ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const 63 { 64 CSHA256 hasher = m_salted_hasher_schnorr; 65 hasher.Write(hash.begin(), 32).Write(pubkey.data(), pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin()); 66 } 67 68 bool 69 Get(const uint256& entry, const bool erase) 70 { 71 std::shared_lock<std::shared_mutex> lock(cs_sigcache); 72 return setValid.contains(entry, erase); 73 } 74 75 void Set(const uint256& entry) 76 { 77 std::unique_lock<std::shared_mutex> lock(cs_sigcache); 78 setValid.insert(entry); 79 } 80 std::optional<std::pair<uint32_t, size_t>> setup_bytes(size_t n) 81 { 82 return setValid.setup_bytes(n); 83 } 84 }; 85 86 /* In previous versions of this code, signatureCache was a local static variable 87 * in CachingTransactionSignatureChecker::VerifySignature. We initialize 88 * signatureCache outside of VerifySignature to avoid the atomic operation per 89 * call overhead associated with local static variables even though 90 * signatureCache could be made local to VerifySignature. 91 */ 92 static CSignatureCache signatureCache; 93 } // namespace 94 95 // To be called once in AppInitMain/BasicTestingSetup to initialize the 96 // signatureCache. 97 bool InitSignatureCache(size_t max_size_bytes) 98 { 99 auto setup_results = signatureCache.setup_bytes(max_size_bytes); 100 if (!setup_results) return false; 101 102 const auto [num_elems, approx_size_bytes] = *setup_results; 103 LogPrintf("Using %zu MiB out of %zu MiB requested for signature cache, able to store %zu elements\n", 104 approx_size_bytes >> 20, max_size_bytes >> 20, num_elems); 105 return true; 106 } 107 108 bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const 109 { 110 uint256 entry; 111 signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey); 112 if (signatureCache.Get(entry, !store)) 113 return true; 114 if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash)) 115 return false; 116 if (store) 117 signatureCache.Set(entry); 118 return true; 119 } 120 121 bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const 122 { 123 uint256 entry; 124 signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey); 125 if (signatureCache.Get(entry, !store)) return true; 126 if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false; 127 if (store) signatureCache.Set(entry); 128 return true; 129 }