From 4ceaea0cca2d91ff104b661aab1293ed4d185461 Mon Sep 17 00:00:00 2001 From: Evan Han Date: Fri, 25 Feb 2022 14:43:21 +0800 Subject: [PATCH] Merge PR: refactor tx signature cache (#1595) * refactor tx signature cache * fix ut * optimize code Co-authored-by: Zhong Qiu <36867992+zhongqiuwood@users.noreply.github.com> --- app/app.go | 6 +- app/elapse_info.go | 3 +- cmd/client/flags.go | 2 + libs/cosmos-sdk/baseapp/abci.go | 4 + libs/tendermint/mempool/clist_mempool.go | 3 + libs/tendermint/trace/trace.go | 1 + libs/tendermint/types/tx_signature_cache.go | 122 ++++++++++++++++++++ x/evm/types/msg_evm.go | 39 ++----- x/evm/types/msg_sig_cache.go | 57 --------- 9 files changed, 149 insertions(+), 88 deletions(-) create mode 100644 libs/tendermint/types/tx_signature_cache.go delete mode 100644 x/evm/types/msg_sig_cache.go diff --git a/app/app.go b/app/app.go index 945be16f22..ef0cf07486 100644 --- a/app/app.go +++ b/app/app.go @@ -2,12 +2,13 @@ package app import ( "fmt" - "github.com/okex/exchain/app/utils/sanity" "io" "math/big" "os" "sync" + "github.com/okex/exchain/app/utils/sanity" + "github.com/okex/exchain/app/ante" okexchaincodec "github.com/okex/exchain/app/codec" appconfig "github.com/okex/exchain/app/config" @@ -607,5 +608,8 @@ func PreRun(ctx *server.Context) error { if viper.GetBool(FlagEnableRepairState) { repairStateOnStart(ctx) } + + // init tx signature cache + tmtypes.InitSignatureCache() return nil } diff --git a/app/elapse_info.go b/app/elapse_info.go index 3da6f96ac8..50646976ad 100644 --- a/app/elapse_info.go +++ b/app/elapse_info.go @@ -100,7 +100,7 @@ func (e *ElapsedTimeInfos) Dump(logger log.Logger) { } } - info := fmt.Sprintf("%s<%s>, %s<%s>, %s<%s>, %s<%s>, %s<%s>, %s<%s>, %s[%s], %s[%s], %s<%s>, %s<%s>", + info := fmt.Sprintf("%s<%s>, %s<%s>, %s<%s>, %s<%s>, %s<%s>, %s<%s>, %s[%s], %s[%s], %s<%s>, %s<%s>, %s<%s>", trace.Height, e.infoMap[trace.Height], trace.Tx, e.infoMap[trace.Tx], trace.BlockSize, e.infoMap[trace.BlockSize], @@ -111,6 +111,7 @@ func (e *ElapsedTimeInfos) Dump(logger log.Logger) { trace.Prerun, e.infoMap[trace.Prerun], trace.MempoolCheckTxCnt, e.infoMap[trace.MempoolCheckTxCnt], trace.MempoolTxsCnt, e.infoMap[trace.MempoolTxsCnt], + trace.SigCacheRatio, e.infoMap[trace.SigCacheRatio], ) if len(detailInfo) > 0 { diff --git a/cmd/client/flags.go b/cmd/client/flags.go index c3cf65a776..7ab87fd093 100644 --- a/cmd/client/flags.go +++ b/cmd/client/flags.go @@ -10,6 +10,7 @@ import ( "github.com/okex/exchain/app/utils/sanity" "github.com/okex/exchain/libs/tendermint/consensus" "github.com/okex/exchain/libs/tendermint/libs/automation" + tmtypes "github.com/okex/exchain/libs/tendermint/types" tmdb "github.com/okex/exchain/libs/tm-db" "github.com/okex/exchain/x/common/analyzer" evmtypes "github.com/okex/exchain/x/evm/types" @@ -109,4 +110,5 @@ func RegisterAppFlag(cmd *cobra.Command) { cmd.Flags().Bool(analyzer.FlagEnableAnalyzer, true, "Enable auto open log analyzer") cmd.Flags().Bool(sanity.FlagDisableSanity, false, "Disable sanity check") + cmd.Flags().Int(tmtypes.FlagSigCacheSize, 200000, "Maximum number of signatures in the cache") } diff --git a/libs/cosmos-sdk/baseapp/abci.go b/libs/cosmos-sdk/baseapp/abci.go index 5d684e50e3..a49f9674de 100644 --- a/libs/cosmos-sdk/baseapp/abci.go +++ b/libs/cosmos-sdk/baseapp/abci.go @@ -261,6 +261,10 @@ func (app *BaseApp) Commit(req abci.RequestCommit) abci.ResponseCommit { trace.GetElapsedInfo().AddInfo(trace.WtxRatio, fmt.Sprintf("%.2f", wtx/(wtx+rtx))) + readCache := float64(tmtypes.SignatureCache().ReadCount()) + hitCache := float64(tmtypes.SignatureCache().HitCount()) + trace.GetElapsedInfo().AddInfo(trace.SigCacheRatio, fmt.Sprintf("%.2f", hitCache/readCache)) + trace.GetElapsedInfo().AddInfo(trace.AnteChainDetail, app.anteTracer.FormatRepeatingPins(sdk.AnteTerminatorTag)) app.cms.ResetCount() diff --git a/libs/tendermint/mempool/clist_mempool.go b/libs/tendermint/mempool/clist_mempool.go index db0bedbd11..c3ce6db2ce 100644 --- a/libs/tendermint/mempool/clist_mempool.go +++ b/libs/tendermint/mempool/clist_mempool.go @@ -868,6 +868,9 @@ func (mem *CListMempool) Update( if mem.pendingPool != nil { mem.pendingPool.removeTxByHash(txID(tx, height)) } + + // remove tx signature cache + types.SignatureCache().Remove(types.Bytes2Hash(tx, height)) } mem.metrics.GasUsed.Set(float64(gasUsed)) trace.GetElapsedInfo().AddInfo(trace.GasUsed, fmt.Sprintf("%d", gasUsed)) diff --git a/libs/tendermint/trace/trace.go b/libs/tendermint/trace/trace.go index 52d086739a..15280b8e3b 100644 --- a/libs/tendermint/trace/trace.go +++ b/libs/tendermint/trace/trace.go @@ -21,6 +21,7 @@ const ( Iavl = "Iavl" FlatKV = "FlatKV" WtxRatio = "WtxRatio" + SigCacheRatio = "SigCacheRatio" DeliverTxs = "DeliverTxs" EvmHandlerDetail = "EvmHandlerDetail" RunAnteDetail = "RunAnteDetail" diff --git a/libs/tendermint/types/tx_signature_cache.go b/libs/tendermint/types/tx_signature_cache.go new file mode 100644 index 0000000000..58659c8e60 --- /dev/null +++ b/libs/tendermint/types/tx_signature_cache.go @@ -0,0 +1,122 @@ +package types + +import ( + "sync/atomic" + + "github.com/spf13/viper" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + + lru "github.com/hashicorp/golang-lru" +) + +var ( + signatureCache *Cache +) + +const FlagSigCacheSize = "signature-cache-size" + +func init() { + // used for ut + defaultCache := &Cache{ + data: nil, + readCount: 0, + hitCount: 0, + } + signatureCache = defaultCache +} + +func InitSignatureCache() { + lruCache, err := lru.New(viper.GetInt(FlagSigCacheSize)) + if err != nil { + panic(err) + } + signatureCache = &Cache{ + data: lruCache, + } +} + +func SignatureCache() *Cache { + return signatureCache +} + +type Cache struct { + data *lru.Cache + readCount int64 + hitCount int64 +} + +func (c *Cache) Get(key string) (*TxSigCache, bool) { + // validate + if !c.validate(key) { + return nil, false + } + atomic.AddInt64(&c.readCount, 1) + // get cache + value, ok := c.data.Get(key) + if ok { + sigCache, ok := value.(*TxSigCache) + if ok { + atomic.AddInt64(&c.hitCount, 1) + return sigCache, true + } + } + return nil, false +} + +func (c *Cache) Add(key string, value *TxSigCache) { + // validate + if !c.validate(key) { + return + } + // add cache + c.data.Add(key, value) +} + +func (c *Cache) Remove(key string) { + // validate + if !c.validate(key) { + return + } + c.data.Remove(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 string) bool { + // validate key + if key == "" { + return false + } + // validate lru cache + if c.data == nil { + return false + } + return true +} + +// TxSignatureCache is used to cache the derived sender and contains the signer used +// to derive it. +type TxSigCache struct { + Signer ethtypes.Signer + From ethcmn.Address +} + +func (s TxSigCache) GetFrom() ethcmn.Address { + return s.From +} + +func (s TxSigCache) GetSigner() ethtypes.Signer { + return s.Signer +} + +func (s TxSigCache) EqualSiger(siger ethtypes.Signer) bool { + return s.Signer.Equal(siger) +} diff --git a/x/evm/types/msg_evm.go b/x/evm/types/msg_evm.go index 8edf09c268..20423b8876 100644 --- a/x/evm/types/msg_evm.go +++ b/x/evm/types/msg_evm.go @@ -51,7 +51,7 @@ func (tx MsgEthereumTx) GetType() sdk.TransactionType { func (tx *MsgEthereumTx) SetFrom(addr string) { // only cache from but not signer - tx.from.Store(ðSigCache{from: ethcmn.HexToAddress(addr)}) + tx.from.Store(&tmtypes.TxSigCache{From: ethcmn.HexToAddress(addr)}) } func (msg MsgEthereumTx) GetFee() sdk.Coins { @@ -70,25 +70,6 @@ func (msg MsgEthereumTx) FeePayer(ctx sdk.Context) sdk.AccAddress { return msg.From() } -// ethSigCache is used to cache the derived sender and contains the signer used -// to derive it. -type ethSigCache struct { - signer ethtypes.Signer - from ethcmn.Address -} - -func (s ethSigCache) GetFrom() ethcmn.Address { - return s.from -} - -func (s ethSigCache) GetSigner() ethtypes.Signer { - return s.signer -} - -func (s ethSigCache) EqualSiger(siger ethtypes.Signer) bool { - return s.signer.Equal(siger) -} - // NewMsgEthereumTx returns a reference to a new Ethereum transaction message. func NewMsgEthereumTx( nonce uint64, to *ethcmn.Address, amount *big.Int, @@ -282,7 +263,7 @@ func (msg *MsgEthereumTx) VerifySig(chainID *big.Int, height int64, txBytes []by // get sender from cache if txBytes != nil { cacheKey = tmtypes.Bytes2Hash(txBytes, height) - if sigCache, ok := verifySigCache.Get(cacheKey); ok { + if sigCache, ok := tmtypes.SignatureCache().Get(cacheKey); ok { msg.from.Store(sigCache) return sigCache, nil } @@ -299,11 +280,11 @@ func (msg *MsgEthereumTx) VerifySig(chainID *big.Int, height int64, txBytes []by signer = ethtypes.HomesteadSigner{} } if sc := msg.from.Load(); sc != nil { - sigCache := sc.(*ethSigCache) + sigCache := sc.(*tmtypes.TxSigCache) // If the signer used to derive from in a previous call is not the same as // used current, invalidate the cache. - if sigCache.signer.Equal(signer) { - verifySigCache.Add(cacheKey, sigCache) + if sigCache.Signer.Equal(signer) { + tmtypes.SignatureCache().Add(cacheKey, sigCache) return sigCache, nil } } @@ -331,9 +312,9 @@ func (msg *MsgEthereumTx) VerifySig(chainID *big.Int, height int64, txBytes []by if err != nil { return nil, err } - sigCache := ðSigCache{signer: signer, from: sender} + sigCache := &tmtypes.TxSigCache{Signer: signer, From: sender} msg.from.Store(sigCache) - verifySigCache.Add(cacheKey, sigCache) + tmtypes.SignatureCache().Add(cacheKey, sigCache) return sigCache, nil } @@ -383,13 +364,13 @@ func (msg *MsgEthereumTx) From() sdk.AccAddress { return nil } - sigCache := sc.(*ethSigCache) + sigCache := sc.(*tmtypes.TxSigCache) - if len(sigCache.from.Bytes()) == 0 { + if len(sigCache.From.Bytes()) == 0 { return nil } - return sdk.AccAddress(sigCache.from.Bytes()) + return sdk.AccAddress(sigCache.From.Bytes()) } // deriveChainID derives the chain id from the given v parameter diff --git a/x/evm/types/msg_sig_cache.go b/x/evm/types/msg_sig_cache.go deleted file mode 100644 index 4c53f043be..0000000000 --- a/x/evm/types/msg_sig_cache.go +++ /dev/null @@ -1,57 +0,0 @@ -package types - -import ( - lru "github.com/hashicorp/golang-lru" -) - -var ( - verifySigCache *Cache -) - -const cacheSize = 1000000 - -func init() { - lruCache, err := lru.New(cacheSize) - if err != nil { - panic(err) - } - verifySigCache = &Cache{ - data: lruCache, - } -} - -type Cache struct { - data *lru.Cache -} - -func (c *Cache) Get(key string) (*ethSigCache, bool) { - // validate key - if !validateKey(key) { - return nil, false - } - // get cache - value, ok := c.data.Get(key) - if ok { - sigCache, ok := value.(*ethSigCache) - if ok { - return sigCache, true - } - } - return nil, false -} - -func (c *Cache) Add(key string, value *ethSigCache) { - // validate key - if !validateKey(key) { - return - } - // add cache - c.data.Add(key, value) -} - -func validateKey(key string) bool { - if key == "" { - return false - } - return true -}