From 5154fe716ac9b8a41ba1895418e33ae46e1c34e8 Mon Sep 17 00:00:00 2001 From: beer-1 Date: Tue, 19 Nov 2024 17:39:24 +0900 Subject: [PATCH] use memstore instead of block wise transient store --- app/ibc-hooks/common_test.go | 1 - app/ibc-hooks/receive_test.go | 3 +- app/keepers/keepers.go | 1 - app/keepers/keys.go | 2 +- x/bank/keeper/common_test.go | 1 - x/evm/keeper/common_test.go | 1 - x/evm/keeper/context.go | 53 ++++--- x/evm/keeper/keeper.go | 31 ---- x/evm/precompiles/cosmos/common_test.go | 13 +- .../precompiles/erc20_registry/common_test.go | 13 +- x/evm/state/common_test.go | 1 - x/evm/state/context.go | 45 ++++++ x/evm/state/keys.go | 11 ++ x/evm/state/memdb.go | 80 ++++++++++ x/evm/state/snapshot.go | 15 +- x/evm/state/statedb.go | 150 +++++++++--------- x/evm/types/keys.go | 9 +- 17 files changed, 264 insertions(+), 166 deletions(-) create mode 100644 x/evm/state/context.go create mode 100644 x/evm/state/memdb.go diff --git a/app/ibc-hooks/common_test.go b/app/ibc-hooks/common_test.go index fa5d0db..19414fd 100644 --- a/app/ibc-hooks/common_test.go +++ b/app/ibc-hooks/common_test.go @@ -309,7 +309,6 @@ func _createTestInput( ac, appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - runtime.NewTransientStoreService(tkeys[evmtypes.TStoreKey]), accountKeeper, bankKeeper, communityPoolKeeper, diff --git a/app/ibc-hooks/receive_test.go b/app/ibc-hooks/receive_test.go index 9f6265b..fb0f764 100644 --- a/app/ibc-hooks/receive_test.go +++ b/app/ibc-hooks/receive_test.go @@ -18,7 +18,6 @@ import ( nfttransfertypes "github.com/initia-labs/initia/x/ibc/nft-transfer/types" evm_hooks "github.com/initia-labs/minievm/app/ibc-hooks" "github.com/initia-labs/minievm/x/evm/contracts/counter" - "github.com/initia-labs/minievm/x/evm/types" evmtypes "github.com/initia-labs/minievm/x/evm/types" ) @@ -111,7 +110,7 @@ func Test_onReceiveIcs20Packet_memo(t *testing.T) { require.Equal(t, uint256.NewInt(1).Bytes32(), [32]byte(queryRes)) // check allowance - erc20Addr, err := types.DenomToContractAddr(ctx, input.EVMKeeper, localDenom) + erc20Addr, err := evmtypes.DenomToContractAddr(ctx, input.EVMKeeper, localDenom) require.NoError(t, err) queryInputBz, err = input.EVMKeeper.ERC20Keeper().GetERC20ABI().Pack("allowance", common.BytesToAddress(intermediateSender.Bytes()), contractAddr) require.NoError(t, err) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 89a88aa..d6d2b35 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -553,7 +553,6 @@ func NewAppKeeper( ac, appCodec, runtime.NewKVStoreService(appKeepers.keys[evmtypes.StoreKey]), - runtime.NewTransientStoreService(appKeepers.tkeys[evmtypes.TStoreKey]), accountKeeper, bankKeeper, communityPoolKeeper, diff --git a/app/keepers/keys.go b/app/keepers/keys.go index 54aa290..a2d62a4 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -57,7 +57,7 @@ func (appKeepers *AppKeepers) GenerateKeys() { ) // Define transient store keys - appKeepers.tkeys = storetypes.NewTransientStoreKeys(evmtypes.TStoreKey, forwardingtypes.TransientStoreKey) + appKeepers.tkeys = storetypes.NewTransientStoreKeys(forwardingtypes.TransientStoreKey) // MemKeys are for information that is stored only in RAM. appKeepers.memKeys = storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) diff --git a/x/bank/keeper/common_test.go b/x/bank/keeper/common_test.go index 11b2f4b..faf22c3 100644 --- a/x/bank/keeper/common_test.go +++ b/x/bank/keeper/common_test.go @@ -301,7 +301,6 @@ func _createTestInput( ac, appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - runtime.NewTransientStoreService(tkeys[evmtypes.TStoreKey]), accountKeeper, bankKeeper, communityPoolKeeper, diff --git a/x/evm/keeper/common_test.go b/x/evm/keeper/common_test.go index 1673422..6dbd950 100644 --- a/x/evm/keeper/common_test.go +++ b/x/evm/keeper/common_test.go @@ -262,7 +262,6 @@ func _createTestInput( ac, appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - runtime.NewTransientStoreService(tkeys[evmtypes.TStoreKey]), accountKeeper, bankKeeper, communityPoolKeeper, diff --git a/x/evm/keeper/context.go b/x/evm/keeper/context.go index 18eefb0..4abefec 100644 --- a/x/evm/keeper/context.go +++ b/x/evm/keeper/context.go @@ -27,11 +27,8 @@ import ( func (k Keeper) NewStateDB(ctx context.Context, evm *vm.EVM, fee types.Fee) (*evmstate.StateDB, error) { return evmstate.NewStateDB( // delegate gas meter to the EVM - sdk.UnwrapSDKContext(ctx).WithGasMeter(storetypes.NewInfiniteGasMeter()), k.Logger(ctx), - k.accountKeeper, k.VMStore, k.TransientVMStore, k.TransientCreated, - k.TransientSelfDestruct, k.TransientLogs, k.TransientLogSize, - k.TransientAccessList, k.TransientRefund, k.execIndex, - evm, k.ERC20Keeper().GetERC20ABI(), fee.Contract(), + sdk.UnwrapSDKContext(ctx).WithGasMeter(storetypes.NewInfiniteGasMeter()), k.cdc, k.Logger(ctx), + k.accountKeeper, k.VMStore, evm, k.ERC20Keeper().GetERC20ABI(), fee.Contract(), ) } @@ -159,48 +156,54 @@ func (k Keeper) CreateEVM(ctx context.Context, caller common.Address, tracer *tr } // prepare SDK context for EVM execution - ctx, err = prepareSDKContext(sdk.UnwrapSDKContext(ctx)) + sdkCtx, err := prepareSDKContext(sdk.UnwrapSDKContext(ctx)) if err != nil { return ctx, nil, err } - evm := &vm.EVM{} - blockContext, err := k.buildBlockContext(ctx, evm, fee) + chainConfig := types.DefaultChainConfig(sdkCtx) + vmConfig := vm.Config{Tracer: tracer, ExtraEips: extraEIPs, NumRetainBlockHashes: ¶ms.NumRetainBlockHashes} + + // use dummy block context for chain rules in EVM creation + dummyBlockContext, err := k.buildBlockContext(sdkCtx, nil, fee) if err != nil { return ctx, nil, err } - txContext, err := k.buildTxContext(ctx, caller, fee) + txContext, err := k.buildTxContext(sdkCtx, caller, fee) if err != nil { return ctx, nil, err } - stateDB, err := k.NewStateDB(ctx, evm, fee) + + // NOTE: need to check if the EVM is correctly initialized with empty context and stateDB + evm := vm.NewEVM( + dummyBlockContext, + txContext, + nil, + chainConfig, + vmConfig, + ) + // customize EVM contexts and stateDB and precompiles + evm.Context, err = k.buildBlockContext(sdkCtx, evm, fee) if err != nil { return ctx, nil, err } - - chainConfig := types.DefaultChainConfig(ctx) - rules := chainConfig.Rules(blockContext.BlockNumber, blockContext.Random != nil, blockContext.Time) - vmConfig := vm.Config{Tracer: tracer, ExtraEips: extraEIPs, NumRetainBlockHashes: ¶ms.NumRetainBlockHashes} - precompiles, err := k.precompiles(rules, stateDB) + evm.StateDB, err = k.NewStateDB(sdkCtx, evm, fee) if err != nil { return ctx, nil, err } - - *evm = *vm.NewEVMWithPrecompiles( - blockContext, - txContext, - stateDB, - chainConfig, - vmConfig, - precompiles, - ) + rules := chainConfig.Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) + precompiles, err := k.precompiles(rules, evm.StateDB.(types.StateDB)) + if err != nil { + return ctx, nil, err + } + evm.SetPrecompiles(precompiles) if tracer != nil { // register vm context to tracer tracer.OnTxStart(evm.GetVMContext(), nil, caller) } - return ctx, evm, nil + return sdkCtx, evm, nil } // prepare SDK context for EVM execution diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 5d69733..317fd81 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -48,20 +48,6 @@ type Keeper struct { Params collections.Item[types.Params] VMStore collections.Map[[]byte, []byte] - // execIndex is unique index for each execution, which is used - // unique key for transient stores. - execIndex *atomic.Uint64 - - // transient store - TSchema collections.Schema - TransientVMStore collections.Map[collections.Pair[uint64, []byte], []byte] - TransientLogs collections.Map[collections.Pair[uint64, uint64], types.Log] - TransientLogSize collections.Map[uint64, uint64] - TransientRefund collections.Map[uint64, uint64] - TransientCreated collections.KeySet[collections.Pair[uint64, []byte]] - TransientSelfDestruct collections.KeySet[collections.Pair[uint64, []byte]] - TransientAccessList collections.KeySet[collections.Pair[uint64, []byte]] - // erc20 stores of users ERC20FactoryAddr collections.Item[[]byte] ERC20WrapperAddr collections.Item[[]byte] @@ -86,7 +72,6 @@ func NewKeeper( ac address.Codec, cdc codec.Codec, storeService corestoretypes.KVStoreService, - transientService corestoretypes.TransientStoreService, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, communityPoolKeeper types.CommunityPoolKeeper, @@ -99,7 +84,6 @@ func NewKeeper( queryCosmosWhitelist types.QueryCosmosWhitelist, ) *Keeper { sb := collections.NewSchemaBuilder(storeService) - tsb := collections.NewSchemaBuilderFromAccessor(transientService.OpenTransientStore) if evmConfig.ContractSimulationGasLimit == 0 { evmConfig.ContractSimulationGasLimit = evmconfig.DefaultContractSimulationGasLimit @@ -132,16 +116,6 @@ func NewKeeper( Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), VMStore: collections.NewMap(sb, types.VMStorePrefix, "vm_store", collections.BytesKey, collections.BytesValue), - execIndex: execIndex, - - TransientVMStore: collections.NewMap(tsb, types.TransientVMStorePrefix, "transient_vm_store", collections.PairKeyCodec(collections.Uint64Key, collections.BytesKey), collections.BytesValue), - TransientCreated: collections.NewKeySet(tsb, types.TransientCreatedPrefix, "transient_created", collections.PairKeyCodec(collections.Uint64Key, collections.BytesKey)), - TransientSelfDestruct: collections.NewKeySet(tsb, types.TransientSelfDestructPrefix, "transient_self_destruct", collections.PairKeyCodec(collections.Uint64Key, collections.BytesKey)), - TransientLogs: collections.NewMap(tsb, types.TransientLogsPrefix, "transient_logs", collections.PairKeyCodec(collections.Uint64Key, collections.Uint64Key), codec.CollValue[types.Log](cdc)), - TransientLogSize: collections.NewMap(tsb, types.TransientLogSizePrefix, "transient_log_size", collections.Uint64Key, collections.Uint64Value), - TransientAccessList: collections.NewKeySet(tsb, types.TransientAccessListPrefix, "transient_access_list", collections.PairKeyCodec(collections.Uint64Key, collections.BytesKey)), - TransientRefund: collections.NewMap(tsb, types.TransientRefundPrefix, "transient_refund", collections.Uint64Key, collections.Uint64Value), - ERC20WrapperAddr: collections.NewItem(sb, types.ERC20WrapperAddrKey, "erc20_wrapper_addr", collections.BytesValue), ERC20FactoryAddr: collections.NewItem(sb, types.ERC20FactoryAddrKey, "erc20_factory_addr", collections.BytesValue), ERC20s: collections.NewKeySet(sb, types.ERC20sPrefix, "erc20s", collections.BytesKey), @@ -164,13 +138,8 @@ func NewKeeper( if err != nil { panic(err) } - tSchema, err := tsb.Build() - if err != nil { - panic(err) - } k.Schema = schema - k.TSchema = tSchema k.erc20StoresKeeper = NewERC20StoresKeeper(k) k.erc20Keeper, err = NewERC20Keeper(k) if err != nil { diff --git a/x/evm/precompiles/cosmos/common_test.go b/x/evm/precompiles/cosmos/common_test.go index 1849cff..ea20cb1 100644 --- a/x/evm/precompiles/cosmos/common_test.go +++ b/x/evm/precompiles/cosmos/common_test.go @@ -24,14 +24,15 @@ import ( var _ evmtypes.StateDB = &MockStateDB{} type MockStateDB struct { - ctx sdk.Context - initialCtx sdk.Context + ctx state.Context + initialCtx state.Context // Snapshot stack snaps []*state.Snapshot } -func NewMockStateDB(ctx sdk.Context) *MockStateDB { +func NewMockStateDB(sdkCtx sdk.Context) *MockStateDB { + ctx := state.NewContext(sdkCtx) return &MockStateDB{ ctx: ctx, initialCtx: ctx, @@ -73,15 +74,15 @@ func (m *MockStateDB) RevertToSnapshot(i int) { // ContextOfSnapshot implements types.StateDB. func (m *MockStateDB) ContextOfSnapshot(i int) sdk.Context { if i == -1 { - return m.initialCtx + return m.initialCtx.Context } - return m.snaps[i].Context() + return m.snaps[i].Context().Context } // Context implements types.StateDB. func (m *MockStateDB) Context() sdk.Context { - return m.ctx + return m.ctx.Context } //////////////////////// MOCKED METHODS //////////////////////// diff --git a/x/evm/precompiles/erc20_registry/common_test.go b/x/evm/precompiles/erc20_registry/common_test.go index 8a8f09a..b384de9 100644 --- a/x/evm/precompiles/erc20_registry/common_test.go +++ b/x/evm/precompiles/erc20_registry/common_test.go @@ -18,14 +18,15 @@ import ( var _ evmtypes.StateDB = &MockStateDB{} type MockStateDB struct { - ctx sdk.Context - initialCtx sdk.Context + ctx state.Context + initialCtx state.Context // Snapshot stack snaps []*state.Snapshot } -func NewMockStateDB(ctx sdk.Context) *MockStateDB { +func NewMockStateDB(sdkCtx sdk.Context) *MockStateDB { + ctx := state.NewContext(sdkCtx) return &MockStateDB{ ctx: ctx, initialCtx: ctx, @@ -67,15 +68,15 @@ func (m *MockStateDB) RevertToSnapshot(i int) { // ContextOfSnapshot implements types.StateDB. func (m *MockStateDB) ContextOfSnapshot(i int) sdk.Context { if i == -1 { - return m.initialCtx + return m.initialCtx.Context } - return m.snaps[i].Context() + return m.snaps[i].Context().Context } // Context implements types.StateDB. func (m *MockStateDB) Context() sdk.Context { - return m.ctx + return m.ctx.Context } //////////////////////// MOCKED METHODS //////////////////////// diff --git a/x/evm/state/common_test.go b/x/evm/state/common_test.go index f779984..dab485f 100644 --- a/x/evm/state/common_test.go +++ b/x/evm/state/common_test.go @@ -261,7 +261,6 @@ func _createTestInput( ac, appCodec, runtime.NewKVStoreService(keys[evmtypes.StoreKey]), - runtime.NewTransientStoreService(tkeys[evmtypes.TStoreKey]), accountKeeper, bankKeeper, communityPoolKeeper, diff --git a/x/evm/state/context.go b/x/evm/state/context.go new file mode 100644 index 0000000..c989ea9 --- /dev/null +++ b/x/evm/state/context.go @@ -0,0 +1,45 @@ +package state + +import ( + storetypes "cosmossdk.io/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type Context struct { + sdk.Context + + memStore storetypes.MultiStore + memStoreKey storetypes.StoreKey +} + +func NewContext(ctx sdk.Context) Context { + memStore, memStoreKey := newMemStore() + return Context{ + Context: ctx, + memStore: memStore, + memStoreKey: memStoreKey, + } +} + +func (c Context) WithSDKContext(sdkCtx sdk.Context) Context { + c.Context = sdkCtx + return c +} + +func (c Context) WithMemStore(memStore storetypes.MultiStore) Context { + c.memStore = memStore + return c +} + +func (c Context) CacheContext() (cc Context, writeCache func()) { + cacheCtx, commit := c.Context.CacheContext() + cacheMemStore := c.memStore.CacheMultiStore() + + cc = c.WithSDKContext(cacheCtx).WithMemStore(cacheMemStore) + writeCache = func() { + commit() + cacheMemStore.Write() + } + + return cc, writeCache +} diff --git a/x/evm/state/keys.go b/x/evm/state/keys.go index 1919804..bfae5ed 100644 --- a/x/evm/state/keys.go +++ b/x/evm/state/keys.go @@ -37,3 +37,14 @@ func uint64ToBytes(v uint64) []byte { func bytesToUint64(bz []byte) uint64 { return binary.BigEndian.Uint64(bz) } + +// Keys for transient store +var ( + memStoreVMStorePrefix = []byte{0x01} // prefix for transient vm store + memStoreCreatedPrefix = []byte{0x02} // prefix for transient created accounts + memStoreSelfDestructPrefix = []byte{0x03} // prefix for transient self destruct accounts + memStoreLogsPrefix = []byte{0x04} // prefix for transient logs + memStoreLogSizePrefix = []byte{0x05} // prefix for transient log size + memStoreAccessListPrefix = []byte{0x06} // prefix for transient access list + memStoreRefundPrefix = []byte{0x07} // prefix for transient refund +) diff --git a/x/evm/state/memdb.go b/x/evm/state/memdb.go new file mode 100644 index 0000000..6f8f6c0 --- /dev/null +++ b/x/evm/state/memdb.go @@ -0,0 +1,80 @@ +package state + +import ( + "fmt" + + corestoretypes "cosmossdk.io/core/store" + "cosmossdk.io/log" + "cosmossdk.io/store" + storemetrics "cosmossdk.io/store/metrics" + storetypes "cosmossdk.io/store/types" + dbm "github.com/cosmos/cosmos-db" +) + +const ( + storeKey = "stateDB" +) + +func newMemStore() (storetypes.MultiStore, storetypes.StoreKey) { + memStoreKey := storetypes.NewMemoryStoreKey(storeKey) + memStore := store.NewCommitMultiStore(dbm.NewMemDB(), log.NewNopLogger(), storemetrics.NewNoOpMetrics()) + memStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil) + if err := memStore.LoadLatestVersion(); err != nil { + panic(fmt.Sprintf("failed to initialize memory store: %v", err)) + } + + return memStore, memStoreKey +} + +// CoreKVStore is a wrapper of Core/Store kvstore interface +// Remove after https://github.com/cosmos/cosmos-sdk/issues/14714 is closed +type coreKVStore struct { + kvStore storetypes.KVStore +} + +// NewKVStore returns a wrapper of Core/Store kvstore interface +// Remove once store migrates to core/store kvstore interface +func newKVStore(store storetypes.KVStore) corestoretypes.KVStore { + return coreKVStore{kvStore: store} +} + +// Get returns nil iff key doesn't exist. Errors on nil key. +func (store coreKVStore) Get(key []byte) ([]byte, error) { + return store.kvStore.Get(key), nil +} + +// Has checks if a key exists. Errors on nil key. +func (store coreKVStore) Has(key []byte) (bool, error) { + return store.kvStore.Has(key), nil +} + +// Set sets the key. Errors on nil key or value. +func (store coreKVStore) Set(key, value []byte) error { + store.kvStore.Set(key, value) + return nil +} + +// Delete deletes the key. Errors on nil key. +func (store coreKVStore) Delete(key []byte) error { + store.kvStore.Delete(key) + return nil +} + +// Iterator iterates over a domain of keys in ascending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// To iterate over entire domain, use store.Iterator(nil, nil) +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (store coreKVStore) Iterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.Iterator(start, end), nil +} + +// ReverseIterator iterates over a domain of keys in descending order. End is exclusive. +// Start must be less than end, or the Iterator is invalid. +// Iterator must be closed by caller. +// CONTRACT: No writes may happen within a domain while an iterator exists over it. +// Exceptionally allowed for cachekv.Store, safe to write in the modules. +func (store coreKVStore) ReverseIterator(start, end []byte) (store.Iterator, error) { + return store.kvStore.ReverseIterator(start, end), nil +} diff --git a/x/evm/state/snapshot.go b/x/evm/state/snapshot.go index 51a246a..f6edb54 100644 --- a/x/evm/state/snapshot.go +++ b/x/evm/state/snapshot.go @@ -1,19 +1,12 @@ package state -import ( - "context" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - type Snapshot struct { - ctx sdk.Context + ctx Context commit func() } -func NewSnapshot(ctx context.Context) *Snapshot { - sdkCtx := sdk.UnwrapSDKContext(ctx) - cacheCtx, commit := sdkCtx.CacheContext() +func NewSnapshot(ctx Context) *Snapshot { + cacheCtx, commit := ctx.CacheContext() return &Snapshot{ ctx: cacheCtx, commit: commit, @@ -25,6 +18,6 @@ func (s *Snapshot) Commit() { } // for mock testing -func (s *Snapshot) Context() sdk.Context { +func (s *Snapshot) Context() Context { return s.ctx } diff --git a/x/evm/state/statedb.go b/x/evm/state/statedb.go index 1c6fd49..dbf1ae6 100644 --- a/x/evm/state/statedb.go +++ b/x/evm/state/statedb.go @@ -1,15 +1,17 @@ package state import ( + "context" "errors" "fmt" "math/big" - "sync/atomic" "github.com/holiman/uint256" "cosmossdk.io/collections" + corestoretypes "cosmossdk.io/core/store" "cosmossdk.io/log" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi" @@ -28,20 +30,22 @@ import ( var _ vm.StateDB = &StateDB{} type StateDB struct { - ctx sdk.Context - initialCtx sdk.Context + ctx Context + initialCtx Context logger log.Logger accountKeeper evmtypes.AccountKeeper - vmStore collections.Map[[]byte, []byte] - transientVMStore collections.Map[collections.Pair[uint64, []byte], []byte] - transientCreated collections.KeySet[collections.Pair[uint64, []byte]] - transientSelfDestruct collections.KeySet[collections.Pair[uint64, []byte]] - transientLogs collections.Map[collections.Pair[uint64, uint64], evmtypes.Log] - transientLogSize collections.Map[uint64, uint64] - transientAccessList collections.KeySet[collections.Pair[uint64, []byte]] - transientRefund collections.Map[uint64, uint64] - execIndex uint64 + vmStore collections.Map[[]byte, []byte] + + // transient memory store for the current execution + memStoreVMStore collections.Map[[]byte, []byte] + memStoreCreated collections.KeySet[[]byte] + memStoreSelfDestruct collections.KeySet[[]byte] + memStoreLogs collections.Map[uint64, evmtypes.Log] + memStoreLogSize collections.Item[uint64] + memStoreAccessList collections.KeySet[[]byte] + memStoreRefund collections.Item[uint64] + schema collections.Schema evm *vm.EVM erc20ABI *abi.ABI @@ -56,55 +60,59 @@ const ( ) func NewStateDB( - ctx sdk.Context, + sdkCtx sdk.Context, + cdc codec.Codec, logger log.Logger, accountKeeper evmtypes.AccountKeeper, // store params vmStore collections.Map[[]byte, []byte], - transientVMStore collections.Map[collections.Pair[uint64, []byte], []byte], - transientCreated collections.KeySet[collections.Pair[uint64, []byte]], - transientSelfDestruct collections.KeySet[collections.Pair[uint64, []byte]], - transientLogs collections.Map[collections.Pair[uint64, uint64], evmtypes.Log], - transientLogSize collections.Map[uint64, uint64], - transientAccessList collections.KeySet[collections.Pair[uint64, []byte]], - transientRefund collections.Map[uint64, uint64], - execIndex *atomic.Uint64, // erc20 params evm *vm.EVM, erc20ABI *abi.ABI, feeContractAddr common.Address, ) (*StateDB, error) { - eidx := execIndex.Add(1) - - err := transientLogSize.Set(ctx, eidx, 0) - if err != nil { - return nil, err - } - err = transientRefund.Set(ctx, eidx, 0) - if err != nil { - return nil, err - } - + sb := collections.NewSchemaBuilderFromAccessor( + func(ctx context.Context) corestoretypes.KVStore { + stateCtx := ctx.(Context) + return newKVStore(stateCtx.memStore.GetKVStore(stateCtx.memStoreKey)) + }, + ) + + ctx := NewContext(sdkCtx) s := &StateDB{ ctx: ctx, initialCtx: ctx, logger: logger, accountKeeper: accountKeeper, - vmStore: vmStore, - transientVMStore: transientVMStore, - transientCreated: transientCreated, - transientSelfDestruct: transientSelfDestruct, - transientLogs: transientLogs, - transientLogSize: transientLogSize, - transientAccessList: transientAccessList, - transientRefund: transientRefund, - execIndex: eidx, + vmStore: vmStore, + + memStoreVMStore: collections.NewMap(sb, memStoreVMStorePrefix, "mem_store_vm_store", collections.BytesKey, collections.BytesValue), + memStoreCreated: collections.NewKeySet(sb, memStoreCreatedPrefix, "mem_store_created", collections.BytesKey), + memStoreSelfDestruct: collections.NewKeySet(sb, memStoreSelfDestructPrefix, "mem_store_self_destruct", collections.BytesKey), + memStoreLogs: collections.NewMap(sb, memStoreLogsPrefix, "mem_store_logs", collections.Uint64Key, codec.CollValue[evmtypes.Log](cdc)), + memStoreLogSize: collections.NewItem(sb, memStoreLogSizePrefix, "mem_store_log_size", collections.Uint64Value), + memStoreAccessList: collections.NewKeySet(sb, memStoreAccessListPrefix, "mem_store_access_list", collections.BytesKey), + memStoreRefund: collections.NewItem(sb, memStoreRefundPrefix, "mem_store_refund", collections.Uint64Value), evm: evm, erc20ABI: erc20ABI, feeContractAddr: feeContractAddr, } + schema, err := sb.Build() + if err != nil { + panic(err) + } + s.schema = schema + + err = s.memStoreLogSize.Set(ctx, 0) + if err != nil { + return nil, err + } + err = s.memStoreRefund.Set(ctx, 0) + if err != nil { + return nil, err + } return s, nil } @@ -176,12 +184,12 @@ func (s *StateDB) GetBalance(addr common.Address) *uint256.Int { // AddRefund implements vm.StateDB. func (s *StateDB) AddRefund(gas uint64) { - refund, err := s.transientRefund.Get(s.ctx, s.execIndex) + refund, err := s.memStoreRefund.Get(s.ctx) if err != nil { panic(err) } - err = s.transientRefund.Set(s.ctx, s.execIndex, refund+gas) + err = s.memStoreRefund.Set(s.ctx, refund+gas) if err != nil { panic(err) } @@ -189,7 +197,7 @@ func (s *StateDB) AddRefund(gas uint64) { // SubRefund implements vm.StateDB. func (s *StateDB) SubRefund(gas uint64) { - refund, err := s.transientRefund.Get(s.ctx, s.execIndex) + refund, err := s.memStoreRefund.Get(s.ctx) if err != nil { panic(err) } @@ -198,7 +206,7 @@ func (s *StateDB) SubRefund(gas uint64) { panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, refund)) } - err = s.transientRefund.Set(s.ctx, s.execIndex, refund-gas) + err = s.memStoreRefund.Set(s.ctx, refund-gas) if err != nil { panic(err) } @@ -206,7 +214,7 @@ func (s *StateDB) SubRefund(gas uint64) { // AddAddressToAccessList adds the given address to the access list func (s *StateDB) AddAddressToAccessList(addr common.Address) { - err := s.transientAccessList.Set(s.ctx, collections.Join(s.execIndex, addr.Bytes())) + err := s.memStoreAccessList.Set(s.ctx, addr.Bytes()) if err != nil { panic(err) } @@ -219,7 +227,7 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { s.AddAddressToAccessList(addr) } - err := s.transientAccessList.Set(s.ctx, collections.Join(s.execIndex, append(addr.Bytes(), slot[:]...))) + err := s.memStoreAccessList.Set(s.ctx, append(addr.Bytes(), slot[:]...)) if err != nil { panic(err) } @@ -227,7 +235,7 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) { // AddressInAccessList returns true if the given address is in the access list func (s *StateDB) AddressInAccessList(addr common.Address) bool { - ok, err := s.transientAccessList.Has(s.ctx, collections.Join(s.execIndex, addr.Bytes())) + ok, err := s.memStoreAccessList.Has(s.ctx, addr.Bytes()) if err != nil { panic(err) } @@ -237,14 +245,14 @@ func (s *StateDB) AddressInAccessList(addr common.Address) bool { // SlotInAccessList returns true if the given (address, slot)-tuple is in the access list func (s *StateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) { - ok, err := s.transientAccessList.Has(s.ctx, collections.Join(s.execIndex, addr.Bytes())) + ok, err := s.memStoreAccessList.Has(s.ctx, addr.Bytes()) if err != nil { panic(err) } else if !ok { return false, false } - ok, err = s.transientAccessList.Has(s.ctx, collections.Join(s.execIndex, append(addr.Bytes(), slot[:]...))) + ok, err = s.memStoreAccessList.Has(s.ctx, append(addr.Bytes(), slot[:]...)) if err != nil { panic(err) } @@ -260,7 +268,7 @@ func (s *StateDB) CreateAccount(addr common.Address) { // CreateContract creates a contract account with the given address func (s *StateDB) CreateContract(contractAddr common.Address) { - if err := s.transientCreated.Set(s.ctx, collections.Join(s.execIndex, contractAddr.Bytes())); err != nil { + if err := s.memStoreCreated.Set(s.ctx, contractAddr.Bytes()); err != nil { panic(err) } @@ -441,7 +449,7 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64) { // GetRefund returns the refund func (s *StateDB) GetRefund() uint64 { - refund, err := s.transientRefund.Get(s.ctx, s.execIndex) + refund, err := s.memStoreRefund.Get(s.ctx) if err != nil { panic(err) } @@ -470,7 +478,7 @@ func (s *StateDB) GetState(addr common.Address, slot common.Hash) common.Hash { func (s *StateDB) HasSelfDestructed(addr common.Address) bool { acc := s.getAccount(addr) if acc != nil { - ok, err := s.transientSelfDestruct.Has(s.ctx, collections.Join(s.execIndex, addr.Bytes())) + ok, err := s.memStoreSelfDestruct.Has(s.ctx, addr.Bytes()) if err != nil { panic(err) } @@ -493,7 +501,7 @@ func (s *StateDB) SelfDestruct(addr common.Address) { } // mark the account as self-destructed - if err := s.transientSelfDestruct.Set(s.ctx, collections.Join(s.execIndex, addr.Bytes())); err != nil { + if err := s.memStoreSelfDestruct.Set(s.ctx, addr.Bytes()); err != nil { panic(err) } @@ -508,7 +516,7 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) { return } - ok, err := s.transientCreated.Has(s.ctx, collections.Join(s.execIndex, addr.Bytes())) + ok, err := s.memStoreCreated.Has(s.ctx, addr.Bytes()) if err != nil { panic(err) } else if ok { @@ -523,21 +531,21 @@ func (s *StateDB) SetState(addr common.Address, slot common.Hash, value common.H } } -// SetTransientState sets transient storage for a given account. +// SetTransientState sets memStore storage for a given account. func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) { prev := s.GetTransientState(addr, key) if prev == value { return } - if err := s.transientVMStore.Set(s.ctx, collections.Join(s.execIndex, key[:]), value[:]); err != nil { + if err := s.memStoreVMStore.Set(s.ctx, key[:], value[:]); err != nil { panic(err) } } -// GetTransientState gets transient storage for a given account. +// GetTransientState gets memStore storage for a given account. func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common.Hash { - data, err := s.transientVMStore.Get(s.ctx, collections.Join(s.execIndex, key[:])) + data, err := s.memStoreVMStore.Get(s.ctx, key[:]) if err != nil && errors.Is(err, collections.ErrNotFound) { return common.Hash{} } else if err != nil { @@ -582,15 +590,15 @@ func (s *StateDB) RevertToSnapshot(i int) { // ContextOfSnapshot returns the context of the snapshot with the given id func (s *StateDB) ContextOfSnapshot(i int) sdk.Context { if i == -1 { - return s.initialCtx + return s.initialCtx.Context } - return s.snaps[i].ctx + return s.snaps[i].ctx.Context } // Context returns the current context func (s *StateDB) Context() sdk.Context { - return s.ctx + return s.ctx.Context } // Prepare handles the preparatory steps for executing a state transition with. @@ -605,7 +613,7 @@ func (s *StateDB) Context() sdk.Context { // Potential EIPs: // - Reset access list (Berlin) // - Add coinbase to access list (EIP-3651) -// - Reset transient storage (EIP-1153) +// - Reset memStore storage (EIP-1153) func (s *StateDB) Prepare(rules params.Rules, sender common.Address, coinbase common.Address, dst *common.Address, precompiles []common.Address, list types.AccessList) { if rules.IsBerlin { // Clear out any leftover from previous executions @@ -632,8 +640,8 @@ func (s *StateDB) Prepare(rules params.Rules, sender common.Address, coinbase co func (s *StateDB) Commit() error { // clear destructed accounts - err := s.transientSelfDestruct.Walk(s.ctx, collections.NewPrefixedPairRange[uint64, []byte](s.execIndex), func(key collections.Pair[uint64, []byte]) (stop bool, err error) { - addr := common.BytesToAddress(key.K2()) + err := s.memStoreSelfDestruct.Walk(s.ctx, nil, func(key []byte) (stop bool, err error) { + addr := common.BytesToAddress(key) // If ether was sent to account post-selfdestruct it is burnt. if bal := s.GetBalance(addr); bal.Sign() != 0 { @@ -663,24 +671,24 @@ func (s *StateDB) Commit() error { // AddLog implements vm.StateDB. func (s *StateDB) AddLog(log *types.Log) { - logSize, err := s.transientLogSize.Get(s.ctx, s.execIndex) + logSize, err := s.memStoreLogSize.Get(s.ctx) if err != nil { panic(err) } - err = s.transientLogSize.Set(s.ctx, s.execIndex, logSize+1) + err = s.memStoreLogSize.Set(s.ctx, logSize+1) if err != nil { panic(err) } - err = s.transientLogs.Set(s.ctx, collections.Join(s.execIndex, logSize), evmtypes.NewLog(log)) + err = s.memStoreLogs.Set(s.ctx, logSize, evmtypes.NewLog(log)) if err != nil { panic(err) } } func (s *StateDB) Logs() evmtypes.Logs { - logSize, err := s.transientLogSize.Get(s.ctx, s.execIndex) + logSize, err := s.memStoreLogSize.Get(s.ctx) if err != nil { panic(err) } else if logSize == 0 { @@ -688,8 +696,8 @@ func (s *StateDB) Logs() evmtypes.Logs { } logs := make([]evmtypes.Log, logSize) - err = s.transientLogs.Walk(s.ctx, collections.NewPrefixedPairRange[uint64, uint64](s.execIndex), func(key collections.Pair[uint64, uint64], log evmtypes.Log) (stop bool, err error) { - logs[key.K2()] = log + err = s.memStoreLogs.Walk(s.ctx, nil, func(key uint64, log evmtypes.Log) (stop bool, err error) { + logs[key] = log return false, nil }) if err != nil { diff --git a/x/evm/types/keys.go b/x/evm/types/keys.go index ee25eeb..b679453 100644 --- a/x/evm/types/keys.go +++ b/x/evm/types/keys.go @@ -20,14 +20,7 @@ const ( // Keys for move store // Items are stored with the following key: values var ( - VMStorePrefix = []byte{0x21} // prefix for vm - TransientVMStorePrefix = []byte{0x22} // prefix for transient vm store - TransientCreatedPrefix = []byte{0x23} // prefix for transient created accounts - TransientSelfDestructPrefix = []byte{0x24} // prefix for transient self destruct accounts - TransientLogsPrefix = []byte{0x25} // prefix for transient logs - TransientLogSizePrefix = []byte{0x26} // prefix for transient log size - TransientAccessListPrefix = []byte{0x27} // prefix for transient access list - TransientRefundPrefix = []byte{0x28} // prefix for transient refund + VMStorePrefix = []byte{0x21} // prefix for vm ERC20sPrefix = []byte{0x31} // prefix for erc20 stores ERC20StoresPrefix = []byte{0x32} // prefix for erc20 stores