github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/txscript/sigcache.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package txscript 6 7 import ( 8 "sync" 9 10 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 11 "github.com/mit-dci/lit/crypto/koblitz" 12 ) 13 14 // sigCacheEntry represents an entry in the SigCache. Entries within the 15 // SigCache are keyed according to the sigHash of the signature. In the 16 // scenario of a cache-hit (according to the sigHash), an additional comparison 17 // of the signature, and public key will be executed in order to ensure a complete 18 // match. In the occasion that two sigHashes collide, the newer sigHash will 19 // simply overwrite the existing entry. 20 type sigCacheEntry struct { 21 sig *koblitz.Signature 22 pubKey *koblitz.PublicKey 23 } 24 25 // SigCache implements an ECDSA signature verification cache with a randomized 26 // entry eviction policy. Only valid signatures will be added to the cache. The 27 // benefits of SigCache are two fold. Firstly, usage of SigCache mitigates a DoS 28 // attack wherein an attack causes a victim's client to hang due to worst-case 29 // behavior triggered while processing attacker crafted invalid transactions. A 30 // detailed description of the mitigated DoS attack can be found here: 31 // https://bitslog.wordpress.com/2013/01/23/fixed-bitcoin-vulnerability-explanation-why-the-signature-cache-is-a-dos-protection/. 32 // Secondly, usage of the SigCache introduces a signature verification 33 // optimization which speeds up the validation of transactions within a block, 34 // if they've already been seen and verified within the mempool. 35 type SigCache struct { 36 sync.RWMutex 37 validSigs map[chainhash.Hash]sigCacheEntry 38 maxEntries uint 39 } 40 41 // NewSigCache creates and initializes a new instance of SigCache. Its sole 42 // parameter 'maxEntries' represents the maximum number of entries allowed to 43 // exist in the SigCache at any particular moment. Random entries are evicted 44 // to make room for new entries that would cause the number of entries in the 45 // cache to exceed the max. 46 func NewSigCache(maxEntries uint) *SigCache { 47 return &SigCache{ 48 validSigs: make(map[chainhash.Hash]sigCacheEntry, maxEntries), 49 maxEntries: maxEntries, 50 } 51 } 52 53 // Exists returns true if an existing entry of 'sig' over 'sigHash' for public 54 // key 'pubKey' is found within the SigCache. Otherwise, false is returned. 55 // 56 // NOTE: This function is safe for concurrent access. Readers won't be blocked 57 // unless there exists a writer, adding an entry to the SigCache. 58 func (s *SigCache) Exists(sigHash chainhash.Hash, sig *koblitz.Signature, pubKey *koblitz.PublicKey) bool { 59 s.RLock() 60 defer s.RUnlock() 61 62 if entry, ok := s.validSigs[sigHash]; ok { 63 return entry.pubKey.IsEqual(pubKey) && entry.sig.IsEqual(sig) 64 } 65 66 return false 67 } 68 69 // Add adds an entry for a signature over 'sigHash' under public key 'pubKey' 70 // to the signature cache. In the event that the SigCache is 'full', an 71 // existing entry is randomly chosen to be evicted in order to make space for 72 // the new entry. 73 // 74 // NOTE: This function is safe for concurrent access. Writers will block 75 // simultaneous readers until function execution has concluded. 76 func (s *SigCache) Add(sigHash chainhash.Hash, sig *koblitz.Signature, pubKey *koblitz.PublicKey) { 77 s.Lock() 78 defer s.Unlock() 79 80 if s.maxEntries <= 0 { 81 return 82 } 83 84 // If adding this new entry will put us over the max number of allowed 85 // entries, then evict an entry. 86 if uint(len(s.validSigs)+1) > s.maxEntries { 87 // Remove a random entry from the map. Relying on the random 88 // starting point of Go's map iteration. It's worth noting that 89 // the random iteration starting point is not 100% guaranteed 90 // by the spec, however most Go compilers support it. 91 // Ultimately, the iteration order isn't important here because 92 // in order to manipulate which items are evicted, an adversary 93 // would need to be able to execute preimage attacks on the 94 // hashing function in order to start eviction at a specific 95 // entry. 96 for sigEntry := range s.validSigs { 97 delete(s.validSigs, sigEntry) 98 break 99 } 100 } 101 s.validSigs[sigHash] = sigCacheEntry{sig, pubKey} 102 }