From 98161d91b563e5b302e06579c645b971639f8fb4 Mon Sep 17 00:00:00 2001 From: HuangYi Date: Fri, 5 Apr 2024 11:47:08 +0800 Subject: [PATCH] Problem: params not cached in object store Solution: - save the cost of repeated encoding/decoding during the block execution - also migrate existing transient stores to object stores Update CHANGELOG.md Signed-off-by: yihuang Update x/feemarket/types/keys.go Signed-off-by: yihuang renmae fix test --- CHANGELOG.md | 1 + app/app.go | 9 ++++--- x/evm/keeper/abci.go | 4 ++++ x/evm/keeper/bloom.go | 10 ++++---- x/evm/keeper/keeper.go | 19 +++++++-------- x/evm/keeper/params.go | 41 +++++++++++++++++++++++--------- x/evm/keeper/state_transition.go | 2 +- x/evm/statedb/statedb_test.go | 22 +++++++---------- x/evm/types/key.go | 28 ++++++++++++---------- x/feemarket/keeper/keeper.go | 17 ++++++------- x/feemarket/keeper/params.go | 35 +++++++++++++++++---------- x/feemarket/types/keys.go | 13 ++++++++++ 12 files changed, 121 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c635346082..269d4c3597 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (evm) [#447](https://github.com/crypto-org-chain/ethermint/pull/447) Deduct fee through virtual bank transfer. * (evm) [#448](https://github.com/crypto-org-chain/ethermint/pull/448) Refactor the evm transfer to be more efficient. * (evm) [#450](https://github.com/crypto-org-chain/ethermint/pull/450) Refactor transient stores to be compatible with parallel tx execution. +* (evm) [#454](https://github.com/crypto-org-chain/ethermint/pull/454) Migrate transient stores to object stores. ### State Machine Breaking diff --git a/app/app.go b/app/app.go index ed87141357..e078157472 100644 --- a/app/app.go +++ b/app/app.go @@ -331,9 +331,9 @@ func NewEthermintApp( ) // Add the EVM transient store key - tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey, evmtypes.TransientKey) + tkeys := storetypes.NewTransientStoreKeys(paramstypes.TStoreKey) memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) - okeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey) + okeys := storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey, evmtypes.ObjectStoreKey, feemarkettypes.ObjectStoreKey) // load state streaming if enabled if err := bApp.RegisterStreamingServices(appOpts, keys); err != nil { @@ -495,9 +495,8 @@ func NewEthermintApp( feeMarketSs := app.GetSubspace(feemarkettypes.ModuleName) app.FeeMarketKeeper = feemarketkeeper.NewKeeper( appCodec, - runtime.NewKVStoreService(keys[feemarkettypes.StoreKey]), authtypes.NewModuleAddress(govtypes.ModuleName), - keys[feemarkettypes.StoreKey], + keys[feemarkettypes.StoreKey], okeys[feemarkettypes.ObjectStoreKey], ) // Set authority to x/gov module account to only expect the module account to update params @@ -505,7 +504,7 @@ func NewEthermintApp( app.EvmKeeper = evmkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), + keys[evmtypes.StoreKey], okeys[evmtypes.ObjectStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.FeeMarketKeeper, tracer, nil, diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index 2678edb98b..9b04f0f1b4 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -24,6 +24,10 @@ import ( // BeginBlock sets the sdk Context and EIP155 chain id to the Keeper. func (k *Keeper) BeginBlock(ctx sdk.Context) error { k.WithChainID(ctx) + + // cache params object + _ = k.GetParams(ctx) + return nil } diff --git a/x/evm/keeper/bloom.go b/x/evm/keeper/bloom.go index 3f08cbd03c..193e20a1b9 100644 --- a/x/evm/keeper/bloom.go +++ b/x/evm/keeper/bloom.go @@ -8,19 +8,19 @@ import ( "github.com/evmos/ethermint/x/evm/types" ) -func (k Keeper) SetTxBloom(ctx sdk.Context, bloom []byte) { - store := ctx.KVStore(k.transientKey) - store.Set(types.TransientBloomKey(ctx.TxIndex(), ctx.MsgIndex()), bloom) +func (k Keeper) SetTxBloom(ctx sdk.Context, bloom *big.Int) { + store := ctx.ObjectStore(k.objectKey) + store.Set(types.ObjectBloomKey(ctx.TxIndex(), ctx.MsgIndex()), bloom) } func (k Keeper) CollectTxBloom(ctx sdk.Context) { - store := prefix.NewStore(ctx.KVStore(k.transientKey), types.KeyPrefixTransientBloom) + store := prefix.NewObjStore(ctx.ObjectStore(k.objectKey), types.KeyPrefixObjectBloom) it := store.Iterator(nil, nil) defer it.Close() bloom := new(big.Int) for ; it.Valid(); it.Next() { - bloom.Or(bloom, big.NewInt(0).SetBytes(it.Value())) + bloom.Or(bloom, it.Value().(*big.Int)) } k.EmitBlockBloomEvent(ctx, bloom.Bytes()) diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index bf2eafca25..60d46d4c71 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -50,7 +50,7 @@ type Keeper struct { storeKey storetypes.StoreKey // key to access the transient store, which is reset on every block during Commit - transientKey storetypes.StoreKey + objectKey storetypes.StoreKey // the address capable of executing a MsgUpdateParams message. Typically, this should be the x/gov module account. authority sdk.AccAddress @@ -79,7 +79,7 @@ type Keeper struct { func NewKeeper( cdc codec.Codec, storeService corestoretypes.KVStoreService, - storeKey, transientKey storetypes.StoreKey, + storeKey, objectKey storetypes.StoreKey, authority sdk.AccAddress, ak types.AccountKeeper, bankKeeper types.BankKeeper, @@ -108,7 +108,7 @@ func NewKeeper( stakingKeeper: sk, feeMarketKeeper: fmk, storeKey: storeKey, - transientKey: transientKey, + objectKey: objectKey, tracer: tracer, customContractFns: customContractFns, } @@ -287,19 +287,18 @@ func (k Keeper) getBaseFee(ctx sdk.Context, london bool) *big.Int { // GetTransientGasUsed returns the gas used by current cosmos tx. func (k Keeper) GetTransientGasUsed(ctx sdk.Context) uint64 { - store := ctx.TransientStore(k.transientKey) - bz := store.Get(types.TransientGasUsedKey(ctx.TxIndex())) - if len(bz) == 0 { + store := ctx.ObjectStore(k.objectKey) + v := store.Get(types.ObjectGasUsedKey(ctx.TxIndex())) + if v == nil { return 0 } - return sdk.BigEndianToUint64(bz) + return v.(uint64) } // SetTransientGasUsed sets the gas used by current cosmos tx. func (k Keeper) SetTransientGasUsed(ctx sdk.Context, gasUsed uint64) { - store := ctx.TransientStore(k.transientKey) - bz := sdk.Uint64ToBigEndian(gasUsed) - store.Set(types.TransientGasUsedKey(ctx.TxIndex()), bz) + store := ctx.ObjectStore(k.objectKey) + store.Set(types.ObjectGasUsedKey(ctx.TxIndex()), gasUsed) } // AddTransientGasUsed accumulate gas used by each eth msgs included in current cosmos tx. diff --git a/x/evm/keeper/params.go b/x/evm/keeper/params.go index 9ca5787db3..eff1a9c5bf 100644 --- a/x/evm/keeper/params.go +++ b/x/evm/keeper/params.go @@ -21,17 +21,27 @@ import ( ) // GetParams returns the total set of evm parameters. -func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { - store := k.storeService.OpenKVStore(ctx) - bz, err := store.Get(types.KeyPrefixParams) - if err != nil { - panic(err) - } - if bz == nil { - return p +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + var params *types.Params + objStore := ctx.ObjectStore(k.objectKey) + v := objStore.Get(types.KeyPrefixObjectParams) + if v == nil { + store := k.storeService.OpenKVStore(ctx) + bz, err := store.Get(types.KeyPrefixParams) + if err != nil { + panic(err) + } + params = new(types.Params) + if bz != nil { + k.cdc.MustUnmarshal(bz, params) + } + + objStore.Set(types.KeyPrefixObjectParams, params) + } else { + params = v.(*types.Params) } - k.cdc.MustUnmarshal(bz, &p) - return p + + return *params } // SetParams sets the EVM params each in their individual key for better get performance @@ -41,5 +51,14 @@ func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { } store := k.storeService.OpenKVStore(ctx) bz := k.cdc.MustMarshal(&p) - return store.Set(types.KeyPrefixParams, bz) + if err := store.Set(types.KeyPrefixParams, bz); err != nil { + return err + } + + // set to cache as well, decode again to be compatible with the previous behavior + var params types.Params + k.cdc.MustUnmarshal(bz, ¶ms) + ctx.ObjectStore(k.objectKey).Set(types.KeyPrefixObjectParams, ¶ms) + + return nil } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index f491599408..f1c466085f 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -200,7 +200,7 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, msgEth *types.MsgEthereumTx) // Compute block bloom filter if len(logs) > 0 { - k.SetTxBloom(tmpCtx, ethtypes.LogsBloom(logs)) + k.SetTxBloom(tmpCtx, new(big.Int).SetBytes(ethtypes.LogsBloom(logs))) } var contractAddr common.Address diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index de2b1d8042..0b384afce0 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -592,7 +592,7 @@ func (suite *StateDBTestSuite) TestIterateStorage() { func (suite *StateDBTestSuite) TestNativeAction() { _, ctx, keeper := setupTestEnv(suite.T()) storeKey := testStoreKeys["testnative"] - transientKey := testTransientKeys[evmtypes.TransientKey] + objStoreKey := testObjKeys[evmtypes.ObjectStoreKey] memKey := testMemKeys[capabilitytypes.MemStoreKey] eventConverter := func(event sdk.Event) (*ethtypes.Log, error) { @@ -619,8 +619,8 @@ func (suite *StateDBTestSuite) TestNativeAction() { store.Set([]byte("success1"), []byte("value")) ctx.EventManager().EmitEvent(sdk.NewEvent("success1")) - transient := ctx.KVStore(transientKey) - transient.Set([]byte("transient"), []byte("value")) + objStore := ctx.ObjectStore(objStoreKey) + objStore.Set([]byte("transient"), "value") mem := ctx.KVStore(memKey) mem.Set([]byte("mem"), []byte("value")) @@ -632,8 +632,8 @@ func (suite *StateDBTestSuite) TestNativeAction() { store.Set([]byte("failure1"), []byte("value")) ctx.EventManager().EmitEvent(sdk.NewEvent("failure1")) - transient := ctx.KVStore(transientKey) - suite.Require().Equal([]byte("value"), transient.Get([]byte("transient"))) + objStore := ctx.ObjectStore(objStoreKey) + suite.Require().Equal("value", objStore.Get([]byte("transient")).(string)) mem := ctx.KVStore(memKey) suite.Require().Equal([]byte("value"), mem.Get([]byte("mem"))) @@ -771,10 +771,9 @@ func CollectContractStorage(db vm.StateDB, address common.Address) statedb.Stora } var ( - testStoreKeys = storetypes.NewKVStoreKeys(authtypes.StoreKey, banktypes.StoreKey, evmtypes.StoreKey, "testnative") - testObjKeys = storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey) - testTransientKeys = storetypes.NewTransientStoreKeys(evmtypes.TransientKey) - testMemKeys = storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) + testStoreKeys = storetypes.NewKVStoreKeys(authtypes.StoreKey, banktypes.StoreKey, evmtypes.StoreKey, "testnative") + testObjKeys = storetypes.NewObjectStoreKeys(banktypes.ObjectStoreKey, evmtypes.ObjectStoreKey) + testMemKeys = storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) ) func cloneRawState(t *testing.T, cms storetypes.MultiStore) map[string]map[string][]byte { @@ -822,7 +821,7 @@ func newTestKeeper(t *testing.T, cms storetypes.MultiStore) (sdk.Context, *evmke evmKeeper := evmkeeper.NewKeeper( appCodec, runtime.NewKVStoreService(testStoreKeys[evmtypes.StoreKey]), - testStoreKeys[evmtypes.StoreKey], testTransientKeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), + testStoreKeys[evmtypes.StoreKey], testObjKeys[evmtypes.ObjectStoreKey], authtypes.NewModuleAddress(govtypes.ModuleName), accountKeeper, bankKeeper, nil, nil, "", nil, @@ -838,9 +837,6 @@ func setupTestEnv(t *testing.T) (storetypes.MultiStore, sdk.Context, *evmkeeper. for _, key := range testStoreKeys { cms.MountStoreWithDB(key, storetypes.StoreTypeIAVL, nil) } - for _, key := range testTransientKeys { - cms.MountStoreWithDB(key, storetypes.StoreTypeTransient, nil) - } for _, key := range testMemKeys { cms.MountStoreWithDB(key, storetypes.StoreTypeMemory, nil) } diff --git a/x/evm/types/key.go b/x/evm/types/key.go index 6b30408e54..73f896dbd6 100644 --- a/x/evm/types/key.go +++ b/x/evm/types/key.go @@ -30,9 +30,9 @@ const ( // The EVM module should use a prefix store. StoreKey = ModuleName - // TransientKey is the key to access the EVM transient store, that is reset + // ObjectStoreKey is the key to access the EVM object store, that is reset // during the Commit phase. - TransientKey = "transient_" + ModuleName + ObjectStoreKey = "object:" + ModuleName // RouterKey uses module name for routing RouterKey = ModuleName @@ -45,10 +45,11 @@ const ( prefixParams ) -// prefix bytes for the EVM transient store +// prefix bytes for the EVM object store const ( - prefixTransientBloom = iota + 1 - prefixTransientGasUsed + prefixObjectBloom = iota + 1 + prefixObjectGasUsed + prefixObjectParams ) // KVStore key prefixes @@ -58,10 +59,11 @@ var ( KeyPrefixParams = []byte{prefixParams} ) -// Transient Store key prefixes +// Object Store key prefixes var ( - KeyPrefixTransientBloom = []byte{prefixTransientBloom} - KeyPrefixTransientGasUsed = []byte{prefixTransientGasUsed} + KeyPrefixObjectBloom = []byte{prefixObjectBloom} + KeyPrefixObjectGasUsed = []byte{prefixObjectGasUsed} + KeyPrefixObjectParams = []byte{prefixObjectParams} ) // AddressStoragePrefix returns a prefix to iterate over a given account storage. @@ -74,16 +76,16 @@ func StateKey(address common.Address, key []byte) []byte { return append(AddressStoragePrefix(address), key...) } -func TransientGasUsedKey(txIndex int) []byte { - var key [9]byte - key[0] = prefixTransientGasUsed +func ObjectGasUsedKey(txIndex int) []byte { + var key [1 + 8]byte + key[0] = prefixObjectGasUsed binary.BigEndian.PutUint64(key[1:], uint64(txIndex)) return key[:] } -func TransientBloomKey(txIndex, msgIndex int) []byte { +func ObjectBloomKey(txIndex, msgIndex int) []byte { var key [1 + 8 + 8]byte - key[0] = prefixTransientBloom + key[0] = prefixObjectBloom binary.BigEndian.PutUint64(key[1:], uint64(txIndex)) binary.BigEndian.PutUint64(key[9:], uint64(msgIndex)) return key[:] diff --git a/x/feemarket/keeper/keeper.go b/x/feemarket/keeper/keeper.go index 52428e9a3b..ecda4dd22d 100644 --- a/x/feemarket/keeper/keeper.go +++ b/x/feemarket/keeper/keeper.go @@ -18,7 +18,6 @@ package keeper import ( "math/big" - corestoretypes "cosmossdk.io/core/store" "cosmossdk.io/log" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" @@ -33,10 +32,9 @@ var KeyPrefixBaseFeeV1 = []byte{2} // Keeper grants access to the Fee Market module state. type Keeper struct { // Protobuf codec - cdc codec.BinaryCodec - storeService corestoretypes.KVStoreService + cdc codec.BinaryCodec // Store key required for the Fee Market Prefix KVStore. - storeKey storetypes.StoreKey + storeKey, objectKey storetypes.StoreKey // the address capable of executing a MsgUpdateParams message. Typically, this should be the x/gov module account. authority sdk.AccAddress } @@ -44,9 +42,8 @@ type Keeper struct { // NewKeeper generates new fee market module keeper func NewKeeper( cdc codec.BinaryCodec, - storeService corestoretypes.KVStoreService, authority sdk.AccAddress, - storeKey storetypes.StoreKey, + storeKey, objectKey storetypes.StoreKey, ) Keeper { // ensure authority account is correctly formatted if err := sdk.VerifyAddressFormat(authority); err != nil { @@ -54,10 +51,10 @@ func NewKeeper( } return Keeper{ - cdc: cdc, - storeService: storeService, - storeKey: storeKey, - authority: authority, + cdc: cdc, + storeKey: storeKey, + objectKey: objectKey, + authority: authority, } } diff --git a/x/feemarket/keeper/params.go b/x/feemarket/keeper/params.go index 9a04a04e5f..01c9381f66 100644 --- a/x/feemarket/keeper/params.go +++ b/x/feemarket/keeper/params.go @@ -25,17 +25,21 @@ import ( ) // GetParams returns the total set of fee market parameters. -func (k Keeper) GetParams(ctx sdk.Context) (p types.Params) { - store := k.storeService.OpenKVStore(ctx) - bz, err := store.Get(types.ParamsKey) - if err != nil { - panic(err) - } - if bz == nil { - return p +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + var params *types.Params + objStore := ctx.ObjectStore(k.objectKey) + v := objStore.Get(types.KeyPrefixObjectParams) + if v == nil { + params = new(types.Params) + bz := ctx.KVStore(k.storeKey).Get(types.ParamsKey) + if bz != nil { + k.cdc.MustUnmarshal(bz, params) + } + objStore.Set(types.KeyPrefixObjectParams, params) + } else { + params = v.(*types.Params) } - k.cdc.MustUnmarshal(bz, &p) - return p + return *params } // SetParams sets the fee market params in a single key @@ -43,9 +47,16 @@ func (k Keeper) SetParams(ctx sdk.Context, p types.Params) error { if err := p.Validate(); err != nil { return err } - store := k.storeService.OpenKVStore(ctx) + store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshal(&p) - return store.Set(types.ParamsKey, bz) + store.Set(types.ParamsKey, bz) + + // set to cache as well, decode again to be compatible with the previous behavior + var params types.Params + k.cdc.MustUnmarshal(bz, ¶ms) + ctx.ObjectStore(k.objectKey).Set(types.KeyPrefixObjectParams, ¶ms) + + return nil } // ---------------------------------------------------------------------------- diff --git a/x/feemarket/types/keys.go b/x/feemarket/types/keys.go index 2ca00de870..82b7514cbe 100644 --- a/x/feemarket/types/keys.go +++ b/x/feemarket/types/keys.go @@ -25,6 +25,9 @@ const ( // RouterKey uses module name for routing RouterKey = ModuleName + + // ObjectStoreKey is the key to access the Fee Market object store + ObjectStoreKey = "object:" + ModuleName ) // prefix bytes for the feemarket persistent store @@ -33,7 +36,17 @@ const ( deprecatedPrefixBaseFee // unused ) +// prefix bytes for the feemarket object store +const ( + prefixObjectParams = iota + 1 +) + // KVStore key prefixes var ( KeyPrefixBlockGasWanted = []byte{prefixBlockGasWanted} ) + +// Object store key prefixes +var ( + KeyPrefixObjectParams = []byte{prefixObjectParams} +)