txscript: update SigCache to cache both ECDSA and Schnorr signatures

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.
This commit is contained in:
Olaoluwa Osuntokun 2022-01-06 17:30:02 -08:00
parent e781b66e2f
commit 1cd509d9a5
No known key found for this signature in database
GPG Key ID: 3BBD59E99B280306
2 changed files with 19 additions and 17 deletions

View File

@ -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 {
@ -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 {

View File

@ -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"
)
@ -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
@ -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'
@ -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()