sigcache.go 4.0 KB

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