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