diff --git a/crypto/keys/multisig/multisig.go b/crypto/keys/multisig/multisig.go index 10f7d2e04000..acd2dbd8553c 100644 --- a/crypto/keys/multisig/multisig.go +++ b/crypto/keys/multisig/multisig.go @@ -77,7 +77,7 @@ func (m *LegacyAminoPubKey) VerifyMultisignature(getSignBytes multisigtypes.GetS if err != nil { return err } - if !pubKeys[i].VerifySignature(msg, si.Signature) { + if !signing.VerifySig(msg, si.Signature, pubKeys[i]) { return fmt.Errorf("unable to verify signature at index %d", i) } case *signing.MultiSignatureData: diff --git a/crypto/types/tx_signature_cache.go b/crypto/types/tx_signature_cache.go new file mode 100644 index 000000000000..a783df038d9b --- /dev/null +++ b/crypto/types/tx_signature_cache.go @@ -0,0 +1,98 @@ +package types + +import ( + "sync/atomic" + + "github.com/VictoriaMetrics/fastcache" + "github.com/spf13/viper" +) + +var ( + signatureCache *Cache +) + +const ( + TxHashLen = 32 + AddressStringLen = 2 + 20*2 +) + +const FlagSigCacheSize = "signature-cache-size" + +func init() { + // used for ut + defaultCache := &Cache{ + data: nil, + readCount: 0, + hitCount: 0, + } + signatureCache = defaultCache +} + +func InitSignatureCache() { + fastCache := fastcache.New((TxHashLen + AddressStringLen) * viper.GetInt(FlagSigCacheSize)) + signatureCache = &Cache{ + data: fastCache, + } +} + +func SignatureCache() *Cache { + return signatureCache +} + +type Cache struct { + data *fastcache.Cache + readCount int64 + hitCount int64 +} + +func (c *Cache) Get(key []byte) ([]byte, bool) { + // validate + if !c.validate(key) { + return nil, false + } + atomic.AddInt64(&c.readCount, 1) + // get cache + value, ok := c.data.HasGet(nil, key) + if ok { + atomic.AddInt64(&c.hitCount, 1) + return value, true + } + return nil, false +} + +func (c *Cache) Add(key []byte, value []byte) { + // validate + if !c.validate(key) { + return + } + // add cache + c.data.Set(key, value) +} + +func (c *Cache) Remove(key []byte) { + // validate + if !c.validate(key) { + return + } + c.data.Del(key) +} + +func (c *Cache) ReadCount() int64 { + return atomic.LoadInt64(&c.readCount) +} + +func (c *Cache) HitCount() int64 { + return atomic.LoadInt64(&c.hitCount) +} + +func (c *Cache) validate(key []byte) bool { + // validate key + if len(key) == 0 { + return false + } + // validate lru cache + if c.data == nil { + return false + } + return true +} diff --git a/go.mod b/go.mod index a988b423c8b4..b862bccf0cf3 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc cosmossdk.io/x/tx v0.5.1-0.20230407182919-057d2e09bd63 github.com/99designs/keyring v1.2.1 + github.com/VictoriaMetrics/fastcache v1.12.1 github.com/armon/go-metrics v0.4.1 github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 github.com/bits-and-blooms/bitset v1.5.0 diff --git a/go.sum b/go.sum index af9f6a33b6f5..18f09b508f5d 100644 --- a/go.sum +++ b/go.sum @@ -78,6 +78,8 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= @@ -89,6 +91,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -1091,6 +1095,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/types/tx/signing/signature.go b/types/tx/signing/signature.go index c5faf4b8d1d4..28be2356e767 100644 --- a/types/tx/signing/signature.go +++ b/types/tx/signing/signature.go @@ -1,8 +1,9 @@ package signing import ( + "bytes" "fmt" - + "github.com/cometbft/cometbft/crypto/tmhash" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" ) @@ -104,3 +105,19 @@ func (sds *SignatureDescriptors) UnpackInterfaces(unpacker codectypes.AnyUnpacke func (sd *SignatureDescriptor) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { return unpacker.UnpackAny(sd.PublicKey, new(cryptotypes.PubKey)) } + +func VerifySig(signBytes, sig []byte, pubKey cryptotypes.PubKey) bool { + hash := tmhash.Sum(append(signBytes, sig...)) + cachePub, ok := cryptotypes.SignatureCache().Get(hash) + if ok { + cryptotypes.SignatureCache().Remove(hash) + return bytes.Equal(pubKey.Bytes(), cachePub) + } + if !pubKey.VerifySignature(signBytes, sig) { + return false + } + + cryptotypes.SignatureCache().Add(hash, pubKey.Bytes()) + + return true +} diff --git a/x/auth/signing/verify.go b/x/auth/signing/verify.go index 3ed7df854623..bac89e9396d6 100644 --- a/x/auth/signing/verify.go +++ b/x/auth/signing/verify.go @@ -3,7 +3,6 @@ package signing import ( "context" "fmt" - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/cosmos-sdk/crypto/types/multisig" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,7 +18,7 @@ func VerifySignature(ctx context.Context, pubKey cryptotypes.PubKey, signerData if err != nil { return err } - if !pubKey.VerifySignature(signBytes, data.Signature) { + if !signing.VerifySig(signBytes, data.Signature, pubKey) { return fmt.Errorf("unable to verify single signer signature") } return nil