Skip to content

Commit

Permalink
txscript: update SigCache to cache both ECDSA and Schnorr signatures
Browse files Browse the repository at this point in the history
In this commit, we make the sigCache slightly more general in order to
be able to cache both ECDSA and Schnorr signatures. The cache is now
based off of byte slices (the values) rather than the direct objects. We
rely on the fact that the sighash for ecdsa and the schnorr types are
distinct, so we can keep using the same top-level sighash key.

In the future with Go type params, we can use a type param here instead
as they all have an `IsEqual` method.
  • Loading branch information
Roasbeef committed Mar 16, 2022
1 parent e781b66 commit 1cd509d
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 17 deletions.
8 changes: 4 additions & 4 deletions txscript/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -1954,9 +1954,9 @@ func opcodeCheckSig(op *opcode, data []byte, vm *Engine) error {
var sigHash chainhash.Hash
copy(sigHash[:], hash)

valid = vm.sigCache.Exists(sigHash, signature, pubKey)
valid = vm.sigCache.Exists(sigHash, sigBytes, pkBytes)
if !valid && signature.Verify(hash, pubKey) {
vm.sigCache.Add(sigHash, signature, pubKey)
vm.sigCache.Add(sigHash, sigBytes, pkBytes)
valid = true
}
} else {
Expand Down Expand Up @@ -2202,9 +2202,9 @@ func opcodeCheckMultiSig(op *opcode, data []byte, vm *Engine) error {
var sigHash chainhash.Hash
copy(sigHash[:], hash)

valid = vm.sigCache.Exists(sigHash, parsedSig, parsedPubKey)
valid = vm.sigCache.Exists(sigHash, signature, pubKey)
if !valid && parsedSig.Verify(hash, parsedPubKey) {
vm.sigCache.Add(sigHash, parsedSig, parsedPubKey)
vm.sigCache.Add(sigHash, signature, pubKey)
valid = true
}
} else {
Expand Down
28 changes: 15 additions & 13 deletions txscript/sigcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
package txscript

import (
"bytes"
"sync"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg/chainhash"
)

Expand All @@ -19,20 +18,23 @@ import (
// match. In the occasion that two sigHashes collide, the newer sigHash will
// simply overwrite the existing entry.
type sigCacheEntry struct {
sig *ecdsa.Signature
pubKey *btcec.PublicKey
sig []byte
pubKey []byte
}

// SigCache implements an ECDSA signature verification cache with a randomized
// entry eviction policy. Only valid signatures will be added to the cache. The
// benefits of SigCache are two fold. Firstly, usage of SigCache mitigates a DoS
// attack wherein an attack causes a victim's client to hang due to worst-case
// behavior triggered while processing attacker crafted invalid transactions. A
// detailed description of the mitigated DoS attack can be found here:
// SigCache implements an Schnorr+ECDSA signature verification cache with a
// randomized entry eviction policy. Only valid signatures will be added to the
// cache. The benefits of SigCache are two fold. Firstly, usage of SigCache
// mitigates a DoS attack wherein an attack causes a victim's client to hang
// due to worst-case behavior triggered while processing attacker crafted
// invalid transactions. A detailed description of the mitigated DoS attack can
// be found here:
// https://bitslog.wordpress.com/2013/01/23/fixed-bitcoin-vulnerability-explanation-why-the-signature-cache-is-a-dos-protection/.
// Secondly, usage of the SigCache introduces a signature verification
// optimization which speeds up the validation of transactions within a block,
// if they've already been seen and verified within the mempool.
//
// TODO(roasbeef): use type params here after Go 1.18
type SigCache struct {
sync.RWMutex
validSigs map[chainhash.Hash]sigCacheEntry
Expand All @@ -56,12 +58,12 @@ func NewSigCache(maxEntries uint) *SigCache {
//
// NOTE: This function is safe for concurrent access. Readers won't be blocked
// unless there exists a writer, adding an entry to the SigCache.
func (s *SigCache) Exists(sigHash chainhash.Hash, sig *ecdsa.Signature, pubKey *btcec.PublicKey) bool {
func (s *SigCache) Exists(sigHash chainhash.Hash, sig []byte, pubKey []byte) bool {
s.RLock()
entry, ok := s.validSigs[sigHash]
s.RUnlock()

return ok && entry.pubKey.IsEqual(pubKey) && entry.sig.IsEqual(sig)
return ok && bytes.Equal(entry.pubKey, pubKey) && bytes.Equal(entry.sig, sig)
}

// Add adds an entry for a signature over 'sigHash' under public key 'pubKey'
Expand All @@ -71,7 +73,7 @@ func (s *SigCache) Exists(sigHash chainhash.Hash, sig *ecdsa.Signature, pubKey *
//
// NOTE: This function is safe for concurrent access. Writers will block
// simultaneous readers until function execution has concluded.
func (s *SigCache) Add(sigHash chainhash.Hash, sig *ecdsa.Signature, pubKey *btcec.PublicKey) {
func (s *SigCache) Add(sigHash chainhash.Hash, sig []byte, pubKey []byte) {
s.Lock()
defer s.Unlock()

Expand Down

0 comments on commit 1cd509d

Please sign in to comment.