diff --git a/giga/deps/xevm/state/balance.go b/giga/deps/xevm/state/balance.go index dfa0a6dc60..2a7c1734ed 100644 --- a/giga/deps/xevm/state/balance.go +++ b/giga/deps/xevm/state/balance.go @@ -55,7 +55,6 @@ func (s *DBImpl) SubBalance(evmAddr common.Address, amtUint256 *uint256.Int, rea surplus := sdk.NewIntFromBigInt(amt) s.tempState.surplus = s.tempState.surplus.Add(surplus) s.journal = append(s.journal, &surplusChange{delta: surplus}) - s.journal = append(s.journal, &balanceChange{evmAddr: evmAddr, seiAddr: addr, usei: usei, wei: wei, isAdd: false}) return *ZeroInt } @@ -98,7 +97,6 @@ func (s *DBImpl) AddBalance(evmAddr common.Address, amtUint256 *uint256.Int, rea surplus := sdk.NewIntFromBigInt(amt).Neg() s.tempState.surplus = s.tempState.surplus.Add(surplus) s.journal = append(s.journal, &surplusChange{delta: surplus}) - s.journal = append(s.journal, &balanceChange{evmAddr: evmAddr, seiAddr: addr, usei: usei, wei: wei, isAdd: true}) return *ZeroInt } diff --git a/giga/deps/xevm/state/code.go b/giga/deps/xevm/state/code.go index 726dc77c47..dedb2ba33c 100644 --- a/giga/deps/xevm/state/code.go +++ b/giga/deps/xevm/state/code.go @@ -1,11 +1,8 @@ package state import ( - "slices" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/sei-protocol/sei-chain/giga/deps/xevm/types" ) func (s *DBImpl) GetCodeHash(addr common.Address) common.Hash { @@ -18,10 +15,6 @@ func (s *DBImpl) GetCode(addr common.Address) []byte { func (s *DBImpl) SetCode(addr common.Address, code []byte) []byte { oldCode := s.GetCode(addr) - codeStore := s.k.PrefixStore(s.ctx, types.CodeKeyPrefix) - prevCode := codeStore.Get(addr[:]) - prevCodeExists := prevCode != nil - prevMapping := captureAddressMapping(s, addr) if s.logger != nil && s.logger.OnCodeChange != nil { // The SetCode method could be modified to return the old code/hash directly. oldHash := s.GetCodeHash(addr) @@ -30,12 +23,6 @@ func (s *DBImpl) SetCode(addr common.Address, code []byte) []byte { } s.k.SetCode(s.ctx, addr, code) - s.journal = append(s.journal, &codeChange{ - addr: addr, - prevCode: slices.Clone(prevCode), - prevCodeExists: prevCodeExists, - prevMapping: prevMapping, - }) return oldCode } diff --git a/giga/deps/xevm/state/journal.go b/giga/deps/xevm/state/journal.go index 5316ad4f95..9bfcc73e04 100644 --- a/giga/deps/xevm/state/journal.go +++ b/giga/deps/xevm/state/journal.go @@ -2,7 +2,6 @@ package state import ( "encoding/binary" - "fmt" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -19,6 +18,7 @@ type journalEntry interface { type revision struct { id int journalIndex int + ctxIndex int } type ( @@ -52,6 +52,10 @@ type ( delta sdk.Int } + watermark struct { + revision int + } + // storageChange records a KV storage mutation so it can be reverted. storageChange struct { addr common.Address @@ -180,54 +184,19 @@ func (e *accountStatusChange) revert(s *DBImpl) { } } -func (e *storageChange) revert(s *DBImpl) { - s.k.SetState(s.ctx, e.addr, e.key, e.prev) -} +func (e *watermark) revert(s *DBImpl) {} -func (e *codeChange) revert(s *DBImpl) { - restoreCode(s, e.addr, e.prevCode, e.prevCodeExists) - e.prevMapping.restore(s, e.addr) -} +func (e *storageChange) revert(s *DBImpl) {} -func (e *nonceChange) revert(s *DBImpl) { - restoreNonce(s, e.addr, e.prev, e.prevExists) -} +func (e *codeChange) revert(s *DBImpl) {} -func (e *balanceChange) revert(s *DBImpl) { - // Suppress events on revert - ctx := s.ctx.WithEventManager(sdk.NewEventManager()) - denom := s.k.GetBaseDenom(s.ctx) - if e.isAdd { - // Was AddBalance: reverse by subtracting - if err := s.k.BankKeeper().SubUnlockedCoins(ctx, e.seiAddr, sdk.NewCoins(sdk.NewCoin(denom, e.usei)), true); err != nil { - panic(fmt.Sprintf("balanceChange revert SubUnlockedCoins: %v", err)) - } - if err := s.k.BankKeeper().SubWei(ctx, e.seiAddr, e.wei); err != nil { - panic(fmt.Sprintf("balanceChange revert SubWei: %v", err)) - } - } else { - // Was SubBalance: reverse by adding - if err := s.k.BankKeeper().AddCoins(ctx, e.seiAddr, sdk.NewCoins(sdk.NewCoin(denom, e.usei)), true); err != nil { - panic(fmt.Sprintf("balanceChange revert AddCoins: %v", err)) - } - if err := s.k.BankKeeper().AddWei(ctx, e.seiAddr, e.wei); err != nil { - panic(fmt.Sprintf("balanceChange revert AddWei: %v", err)) - } - } -} +func (e *nonceChange) revert(s *DBImpl) {} -func (e *createAccountChange) revert(s *DBImpl) { - restoreCode(s, e.addr, e.prevCode, e.prevCodeExists) - restoreNonce(s, e.addr, e.prevNonce, e.prevNonceExists) - for k, v := range e.prevSlots { - s.k.SetState(s.ctx, e.addr, k, v) - } -} +func (e *balanceChange) revert(s *DBImpl) {} -func (e *deleteMappingChange) revert(s *DBImpl) { - ctx := s.ctx.WithEventManager(sdk.NewEventManager()) - s.k.SetAddressMapping(ctx, e.seiAddr, e.evmAddr) -} +func (e *createAccountChange) revert(s *DBImpl) {} + +func (e *deleteMappingChange) revert(s *DBImpl) {} func captureAddressMapping(s *DBImpl, addr common.Address) addressMappingState { seiAddr, ok := s.k.GetSeiAddress(s.ctx, addr) diff --git a/giga/deps/xevm/state/nonce.go b/giga/deps/xevm/state/nonce.go index 71bcff5f37..4e6b8564af 100644 --- a/giga/deps/xevm/state/nonce.go +++ b/giga/deps/xevm/state/nonce.go @@ -3,7 +3,6 @@ package state import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/tracing" - "github.com/sei-protocol/sei-chain/giga/deps/xevm/types" ) func (s *DBImpl) GetNonce(addr common.Address) uint64 { @@ -12,12 +11,10 @@ func (s *DBImpl) GetNonce(addr common.Address) uint64 { func (s *DBImpl) SetNonce(addr common.Address, nonce uint64, reason tracing.NonceChangeReason) { prevNonce := s.GetNonce(addr) - prevExists := s.k.PrefixStore(s.ctx, types.NonceKeyPrefix).Has(addr[:]) if s.logger != nil && s.logger.OnNonceChangeV2 != nil { // The SetCode method could be modified to return the old code/hash directly. s.logger.OnNonceChangeV2(addr, prevNonce, nonce, reason) } s.k.SetNonce(s.ctx, addr, nonce) - s.journal = append(s.journal, &nonceChange{addr: addr, prev: prevNonce, prevExists: prevExists}) } diff --git a/giga/deps/xevm/state/state.go b/giga/deps/xevm/state/state.go index 9867a7c5fe..820e70c088 100644 --- a/giga/deps/xevm/state/state.go +++ b/giga/deps/xevm/state/state.go @@ -2,7 +2,6 @@ package state import ( "bytes" - "slices" "sort" "github.com/ethereum/go-ethereum/common" @@ -15,11 +14,11 @@ import ( ) func (s *DBImpl) CreateAccount(acc common.Address) { - // clear any existing state but keep balance untouched, journaled for revert + // clear any existing state but keep balance untouched if !s.ctx.IsTracing() { // too slow on historical DB so not doing it for tracing for now. // could cause tracing to be incorrect in theory. - s.clearAccountStateJournaled(acc) + s.clearAccountState(acc) } s.MarkAccount(acc, AccountCreated) } @@ -43,7 +42,6 @@ func (s *DBImpl) SetState(addr common.Address, key common.Hash, val common.Hash) } s.k.SetState(s.ctx, addr, key, val) - s.journal = append(s.journal, &storageChange{addr: addr, key: key, prev: old}) return old } @@ -77,7 +75,6 @@ func (s *DBImpl) SelfDestruct(acc common.Address) uint256.Int { if seiAddr, ok := s.k.GetSeiAddress(s.ctx, acc); ok { // remove the association s.k.DeleteAddressMapping(s.ctx, seiAddr, acc) - s.journal = append(s.journal, &deleteMappingChange{evmAddr: acc, seiAddr: seiAddr}) } b := s.GetBalance(acc) s.SubBalance(acc, b, tracing.BalanceDecreaseSelfdestruct) @@ -105,25 +102,22 @@ func (s *DBImpl) HasSelfDestructed(acc common.Address) bool { return bytes.Equal(val, AccountDeleted) } -// Snapshot records the current journal length as a revision and pushes the current -// EventManager onto the stack, creating a fresh one for subsequent events. func (s *DBImpl) Snapshot() int { id := s.nextRevisionId s.nextRevisionId++ + newCtx := s.ctx.WithMultiStore(s.ctx.MultiStore().CacheMultiStore()).WithEventManager(sdk.NewEventManager()) + s.snapshottedCtxs = append(s.snapshottedCtxs, s.ctx) s.validRevisions = append(s.validRevisions, revision{ id: id, journalIndex: len(s.journal), + ctxIndex: len(s.snapshottedCtxs) - 1, }) - // Push current EM and create a fresh one so reverted events are discarded. - s.snapshottedEventManagers = append(s.snapshottedEventManagers, s.ctx.EventManager()) - s.ctx = s.ctx.WithEventManager(sdk.NewEventManager()) + s.ctx = newCtx + s.journal = append(s.journal, &watermark{revision: id}) return id } -// RevertToSnapshot reverts all journal entries back to the snapshot identified by rev, -// restores the EventManager, and truncates the revision list. func (s *DBImpl) RevertToSnapshot(rev int) { - // Binary-search for the revision with the given id (like go-ethereum). idx := sort.Search(len(s.validRevisions), func(i int) bool { return s.validRevisions[i].id >= rev }) @@ -132,18 +126,14 @@ func (s *DBImpl) RevertToSnapshot(rev int) { } snapshot := s.validRevisions[idx] - // Revert journal entries in reverse order down to the snapshot point. + s.ctx = s.snapshottedCtxs[snapshot.ctxIndex] + s.snapshottedCtxs = s.snapshottedCtxs[:snapshot.ctxIndex] + for i := len(s.journal) - 1; i >= snapshot.journalIndex; i-- { s.journal[i].revert(s) } s.journal = s.journal[:snapshot.journalIndex] - // Restore the EventManager that was active when the snapshot was taken. - // snapshottedEventManagers has one entry per snapshot; idx corresponds to this snapshot. - s.ctx = s.ctx.WithEventManager(s.snapshottedEventManagers[idx]) - s.snapshottedEventManagers = s.snapshottedEventManagers[:idx] - - // Truncate the revision list (removing this snapshot and any taken after it). s.validRevisions = s.validRevisions[:idx] } @@ -184,56 +174,6 @@ func (s *DBImpl) clearAccountState(acc common.Address) { } } -// clearAccountStateJournaled wipes code, nonce, and storage for acc, recording -// the previous values in the journal so a RevertToSnapshot can restore them. -// Called from CreateAccount (when not tracing). -func (s *DBImpl) clearAccountStateJournaled(acc common.Address) { - // Only clear if a code hash exists (mirrors clearAccountState logic). - codeHashStore := s.k.PrefixStore(s.ctx, types.CodeHashKeyPrefix) - if !codeHashStore.Has(acc[:]) { - return - } - - // Save previous state for potential revert. - codeStore := s.k.PrefixStore(s.ctx, types.CodeKeyPrefix) - prevCode := codeStore.Get(acc[:]) - prevCodeExists := prevCode != nil - prevNonce := s.k.GetNonce(s.ctx, acc) - prevNonceExists := s.k.PrefixStore(s.ctx, types.NonceKeyPrefix).Has(acc[:]) - - // Collect all storage slots for this account using GetAllKeyStrsInRange. - // The prefix store's GetAllKeyStrsInRange returns raw parent-store keys, - // so we strip the per-address state prefix to obtain each slot hash. - prevSlots := make(map[common.Hash]common.Hash) - statePrefix := types.StateKey(acc) - stateStore := s.k.PrefixStore(s.ctx, statePrefix) - rawKeys := stateStore.GetAllKeyStrsInRange(nil, nil) - prefixLen := len(statePrefix) - for _, raw := range rawKeys { - if len(raw) <= prefixLen { - continue - } - slotKey := common.BytesToHash([]byte(raw)[prefixLen:]) - slotVal := s.k.GetState(s.ctx, acc, slotKey) - if slotVal != (common.Hash{}) { - prevSlots[slotKey] = slotVal - } - } - - // Append journal entry before making changes. - s.journal = append(s.journal, &createAccountChange{ - addr: acc, - prevCode: slices.Clone(prevCode), - prevCodeExists: prevCodeExists, - prevNonce: prevNonce, - prevNonceExists: prevNonceExists, - prevSlots: prevSlots, - }) - - // Clear the account state. - s.clearAccountState(acc) -} - func (s *DBImpl) MarkAccount(acc common.Address, status []byte) { prev, ok := s.tempState.transientAccounts[acc.Hex()] if !ok { diff --git a/giga/deps/xevm/state/state_test.go b/giga/deps/xevm/state/state_test.go index e6d77926d5..5a41930d86 100644 --- a/giga/deps/xevm/state/state_test.go +++ b/giga/deps/xevm/state/state_test.go @@ -17,7 +17,8 @@ import ( type countingCacheKVStore struct { sdk.CacheKVStore - sets int + sets int + deletes int } func (s *countingCacheKVStore) Set(key, value []byte) { @@ -25,6 +26,11 @@ func (s *countingCacheKVStore) Set(key, value []byte) { s.CacheKVStore.Set(key, value) } +func (s *countingCacheKVStore) Delete(key []byte) { + s.deletes++ + s.CacheKVStore.Delete(key) +} + func TestState(t *testing.T) { k, ctx := testkeeper.MockEVMKeeper(t) ctx = ctx.WithBlockTime(time.Now()) @@ -336,3 +342,66 @@ func TestSetState_NoopStillWritesThrough(t *testing.T) { sdb.SetState(evmAddr, key, val) require.Equal(t, 1, counter.sets) } + +func TestSnapshotRevertDoesNotFlushStorageWrites(t *testing.T) { + tests := []struct { + name string + writeVal common.Hash + }{ + { + name: "same value", + writeVal: common.BytesToHash([]byte("v")), + }, + { + name: "changed value", + writeVal: common.BytesToHash([]byte("changed")), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + k, ctx := testkeeper.MockEVMKeeper(t) + ctx = ctx.WithBlockTime(time.Now()) + _, evmAddr := testkeeper.MockAddressPair() + + key := common.BytesToHash([]byte("k")) + val := common.BytesToHash([]byte("v")) + seedDB := state.NewDBImpl(ctx, k, false) + seedDB.SetState(evmAddr, key, val) + _, err := seedDB.Finalize() + require.NoError(t, err) + + counter := &countingCacheKVStore{} + ms := ctx.MultiStore().(interface { + SetKVStores(func(sdk.StoreKey, sdk.KVStore) sdk.CacheWrap) sdk.MultiStore + }).SetKVStores(func(sk sdk.StoreKey, store sdk.KVStore) sdk.CacheWrap { + if sk == k.GetStoreKey() { + cacheStore, ok := store.(sdk.CacheKVStore) + require.True(t, ok) + counter.CacheKVStore = cacheStore + return counter + } + cacheWrap, ok := store.(sdk.CacheWrap) + require.True(t, ok) + return cacheWrap + }) + ctx = ctx.WithMultiStore(ms) + require.NotNil(t, counter.CacheKVStore) + + sdb := state.NewDBImpl(ctx, k, false) + rev := sdb.Snapshot() + sdb.SetState(evmAddr, key, tt.writeVal) + require.Equal(t, tt.writeVal, sdb.GetState(evmAddr, key)) + + sdb.RevertToSnapshot(rev) + require.Equal(t, val, sdb.GetState(evmAddr, key)) + + _, err = sdb.Finalize() + require.NoError(t, err) + require.Zero(t, counter.sets+counter.deletes) + + checkDB := state.NewDBImpl(ctx, k, false) + require.Equal(t, val, checkDB.GetState(evmAddr, key)) + }) + } +} diff --git a/giga/deps/xevm/state/statedb.go b/giga/deps/xevm/state/statedb.go index f39b7f4a21..c308511cac 100644 --- a/giga/deps/xevm/state/statedb.go +++ b/giga/deps/xevm/state/statedb.go @@ -19,18 +19,16 @@ var logger = seilog.NewLogger("giga", "deps", "xevm", "state") // Initialized for each transaction individually type DBImpl struct { - // ctx is the single CacheMultiStore context used for all KV mutations within this stateDB. ctx sdk.Context // committedCtx is the pre-stateDB context, used for GetCommittedState reads and event flushing. committedCtx sdk.Context + // snapshottedCtxs holds the parent context for each active EVM snapshot. + snapshottedCtxs []sdk.Context - // validRevisions tracks snapshot points (journal index) for RevertToSnapshot. + // validRevisions tracks snapshot points for RevertToSnapshot. validRevisions []revision nextRevisionId int - // snapshottedEventManagers holds EMs from prior snapshots that survived (not reverted). - snapshottedEventManagers []*sdk.EventManager - tempState *TemporaryState journal []journalEntry @@ -58,19 +56,17 @@ type DBImpl struct { func NewDBImpl(ctx sdk.Context, k EVMKeeper, simulation bool) *DBImpl { feeCollector, _ := k.GetFeeCollectorAddress(ctx) - // Create a single CacheMultiStore layer for all KV mutations within this stateDB. - cacheCtx := ctx.WithMultiStore(ctx.MultiStore().CacheMultiStore()).WithEventManager(sdk.NewEventManager()) s := &DBImpl{ - ctx: cacheCtx, - committedCtx: ctx, - k: k, - validRevisions: []revision{}, - snapshottedEventManagers: []*sdk.EventManager{}, - coinbaseAddress: GetCoinbaseAddress(ctx.TxIndex()), - simulation: simulation, - tempState: NewTemporaryState(), - journal: []journalEntry{}, - coinbaseEvmAddress: feeCollector, + ctx: ctx, + committedCtx: ctx, + k: k, + snapshottedCtxs: []sdk.Context{}, + validRevisions: []revision{}, + coinbaseAddress: GetCoinbaseAddress(ctx.TxIndex()), + simulation: simulation, + tempState: NewTemporaryState(), + journal: []journalEntry{}, + coinbaseEvmAddress: feeCollector, } s.Snapshot() return s @@ -100,23 +96,20 @@ func (s *DBImpl) AddPreimage(_ common.Hash, _ []byte) {} func (s *DBImpl) Cleanup() { s.tempState = nil s.logger = nil - s.snapshottedEventManagers = nil + s.snapshottedCtxs = nil s.validRevisions = nil } func (s *DBImpl) CleanupForTracer() { - // Reset back to the committed (pre-stateDB) state by discarding the CMS layer. + // Reset back to the committed (pre-stateDB) state by discarding all CMS layers. s.ctx = s.committedCtx feeCollector, _ := s.k.GetFeeCollectorAddress(s.Ctx()) s.coinbaseEvmAddress = feeCollector s.tempState = NewTemporaryState() s.journal = []journalEntry{} s.validRevisions = []revision{} - s.snapshottedEventManagers = []*sdk.EventManager{} + s.snapshottedCtxs = []sdk.Context{} s.nextRevisionId = 0 - // Re-create the CMS layer for the tracer. - s.committedCtx = s.ctx - s.ctx = s.ctx.WithMultiStore(s.ctx.MultiStore().CacheMultiStore()).WithEventManager(sdk.NewEventManager()) s.Snapshot() } @@ -129,6 +122,9 @@ func (s *DBImpl) ResetForTracer() { s.coinbaseEvmAddress = feeCollector s.tempState = NewTemporaryState() s.journal = []journalEntry{} + s.validRevisions = []revision{} + s.nextRevisionId = 0 + s.snapshottedCtxs = []sdk.Context{} s.Snapshot() } @@ -145,20 +141,39 @@ func (s *DBImpl) Finalize() (surplus sdk.Int, err error) { s.handleResidualFundsInDestructedAccounts(s.tempState) s.clearAccountStateIfDestructed(s.tempState) - // Write the single CMS layer to the underlying store. - s.ctx.MultiStore().(sdk.CacheMultiStore).Write() - s.ctx.GigaMultiStore().WriteGiga() + s.flushCtxs() - // Emit all surviving events (from snapshots + current) to the committed ctx's EventManager. - for _, em := range s.snapshottedEventManagers { - s.committedCtx.EventManager().EmitEvents(em.Events()) + // Emit all surviving events in snapshot order. + for i := 1; i < len(s.snapshottedCtxs); i++ { + s.flushEvents(s.snapshottedCtxs[i]) } - s.committedCtx.EventManager().EmitEvents(s.ctx.EventManager().Events()) + s.flushEvents(s.ctx) surplus = s.tempState.surplus return } +func (s *DBImpl) flushCtxs() { + if len(s.snapshottedCtxs) == 0 { + return + } + s.flushCtx(s.ctx) + for i := len(s.snapshottedCtxs) - 1; i > 0; i-- { + s.flushCtx(s.snapshottedCtxs[i]) + } +} + +func (s *DBImpl) flushCtx(ctx sdk.Context) { + ctx.MultiStore().(sdk.CacheMultiStore).Write() + if gms, ok := ctx.MultiStore().(sdk.GigaMultiStore); ok { + gms.WriteGiga() + } +} + +func (s *DBImpl) flushEvents(ctx sdk.Context) { + s.committedCtx.EventManager().EmitEvents(ctx.EventManager().Events()) +} + // Backward-compatibility functions func (s *DBImpl) Error() error { return s.Err() @@ -172,25 +187,26 @@ func (s *DBImpl) Copy() vm.StateDB { newCtx := s.ctx.WithMultiStore(s.ctx.MultiStore().CacheMultiStore()).WithEventManager(sdk.NewEventManager()) journal := make([]journalEntry, len(s.journal)) copy(journal, s.journal) - snapshottedEMs := make([]*sdk.EventManager, len(s.snapshottedEventManagers)) - copy(snapshottedEMs, s.snapshottedEventManagers) + snapshots := make([]sdk.Context, len(s.snapshottedCtxs)+1) + copy(snapshots, s.snapshottedCtxs) + snapshots[len(s.snapshottedCtxs)] = s.ctx validRevisions := make([]revision, len(s.validRevisions)) copy(validRevisions, s.validRevisions) return &DBImpl{ - ctx: newCtx, - committedCtx: s.committedCtx, - validRevisions: validRevisions, - nextRevisionId: s.nextRevisionId, - snapshottedEventManagers: snapshottedEMs, - tempState: s.tempState.DeepCopy(), - journal: journal, - k: s.k, - coinbaseAddress: s.coinbaseAddress, - coinbaseEvmAddress: s.coinbaseEvmAddress, - simulation: s.simulation, - err: s.err, - precompileErr: s.precompileErr, - logger: s.logger, + ctx: newCtx, + committedCtx: s.committedCtx, + snapshottedCtxs: snapshots, + validRevisions: validRevisions, + nextRevisionId: s.nextRevisionId, + tempState: s.tempState.DeepCopy(), + journal: journal, + k: s.k, + coinbaseAddress: s.coinbaseAddress, + coinbaseEvmAddress: s.coinbaseEvmAddress, + simulation: s.simulation, + err: s.err, + precompileErr: s.precompileErr, + logger: s.logger, } } diff --git a/giga/deps/xevm/state/statedb_internal_test.go b/giga/deps/xevm/state/statedb_internal_test.go index df2783b4f9..dbd7e43f91 100644 --- a/giga/deps/xevm/state/statedb_internal_test.go +++ b/giga/deps/xevm/state/statedb_internal_test.go @@ -10,34 +10,12 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/holiman/uint256" testkeeper "github.com/sei-protocol/sei-chain/giga/deps/testutil/keeper" - evmkeeper "github.com/sei-protocol/sei-chain/giga/deps/xevm/keeper" "github.com/sei-protocol/sei-chain/giga/deps/xevm/state" evmtypes "github.com/sei-protocol/sei-chain/giga/deps/xevm/types" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/stretchr/testify/require" ) -type recipientCheckerRegistrar interface { - RegisterRecipientChecker(func(sdk.Context, sdk.AccAddress) bool) -} - -func newMappedStateDB(t *testing.T) (*state.DBImpl, *evmkeeper.Keeper, sdk.AccAddress, common.Address) { - t.Helper() - k, ctx := testkeeper.MockEVMKeeper(t) - ctx = ctx.WithBlockTime(time.Now()) - seiAddr, evmAddr := testkeeper.MockAddressPair() - db := state.NewDBImpl(ctx, k, false) - k.SetAddressMapping(db.Ctx(), seiAddr, evmAddr) - return db, k, seiAddr, evmAddr -} - -func fundUsei(t *testing.T, k *evmkeeper.Keeper, ctx sdk.Context, seiAddr sdk.AccAddress, amount int64) { - t.Helper() - coins := sdk.NewCoins(sdk.NewCoin(k.GetBaseDenom(ctx), sdk.NewInt(amount))) - require.NoError(t, k.BankKeeper().MintCoins(ctx, evmtypes.ModuleName, coins)) - require.NoError(t, k.BankKeeper().SendCoinsFromModuleToAccount(ctx, evmtypes.ModuleName, seiAddr, coins)) -} - func TestSetNonceCallsV2LoggerWithPreviousNonce(t *testing.T) { k, ctx := testkeeper.MockEVMKeeper(t) ctx = ctx.WithBlockTime(time.Now()) @@ -135,58 +113,3 @@ func TestCreateAccountSkipsMalformedRawStorageKey(t *testing.T) { require.NotPanics(t, func() { db.CreateAccount(evmAddr) }) } - -func TestSnapshotRevertBalancePanicsOnBankErrors(t *testing.T) { - t.Run("sub unlocked coins", func(t *testing.T) { - db, k, seiAddr, evmAddr := newMappedStateDB(t) - coins := sdk.NewCoins(sdk.NewCoin(k.GetBaseDenom(db.Ctx()), sdk.NewInt(1))) - - rev := db.Snapshot() - db.AddBalance(evmAddr, uint256.NewInt(1_000_000_000_000), tracing.BalanceChangeUnspecified) - require.NoError(t, db.Err()) - require.NoError(t, k.BankKeeper().SubUnlockedCoins(db.Ctx(), seiAddr, coins, true)) - - require.Panics(t, func() { db.RevertToSnapshot(rev) }) - }) - - t.Run("sub wei", func(t *testing.T) { - db, k, seiAddr, evmAddr := newMappedStateDB(t) - - rev := db.Snapshot() - db.AddBalance(evmAddr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) - require.NoError(t, db.Err()) - require.NoError(t, k.BankKeeper().SubWei(db.Ctx(), seiAddr, sdk.NewInt(1))) - - require.Panics(t, func() { db.RevertToSnapshot(rev) }) - }) - - t.Run("add coins", func(t *testing.T) { - db, k, seiAddr, evmAddr := newMappedStateDB(t) - fundUsei(t, k, db.Ctx(), seiAddr, 1) - - rev := db.Snapshot() - db.SubBalance(evmAddr, uint256.NewInt(1_000_000_000_000), tracing.BalanceChangeUnspecified) - require.NoError(t, db.Err()) - registrar := k.BankKeeper().(recipientCheckerRegistrar) - registrar.RegisterRecipientChecker(func(sdk.Context, sdk.AccAddress) bool { return false }) - - require.Panics(t, func() { db.RevertToSnapshot(rev) }) - }) - - t.Run("add wei", func(t *testing.T) { - db, k, seiAddr, evmAddr := newMappedStateDB(t) - require.NoError(t, k.BankKeeper().AddWei(db.Ctx(), seiAddr, sdk.NewInt(1))) - - rev := db.Snapshot() - db.SubBalance(evmAddr, uint256.NewInt(1), tracing.BalanceChangeUnspecified) - require.NoError(t, db.Err()) - registrar := k.BankKeeper().(recipientCheckerRegistrar) - calls := 0 - registrar.RegisterRecipientChecker(func(sdk.Context, sdk.AccAddress) bool { - calls++ - return calls == 1 - }) - - require.Panics(t, func() { db.RevertToSnapshot(rev) }) - }) -}