diff --git a/giga/deps/xevm/state/state.go b/giga/deps/xevm/state/state.go index 0c938a3e5f..0adb29f494 100644 --- a/giga/deps/xevm/state/state.go +++ b/giga/deps/xevm/state/state.go @@ -119,6 +119,10 @@ func (s *DBImpl) RevertToSnapshot(rev int) { panic("invalid revision number") } + // Save references to contexts being discarded before updating state + discardedCtx := s.ctx + discardedSnapshots := s.snapshottedCtxs[rev+1:] + s.ctx = s.snapshottedCtxs[rev] s.snapshottedCtxs = s.snapshottedCtxs[:rev] @@ -137,6 +141,12 @@ func (s *DBImpl) RevertToSnapshot(rev int) { if watermarkIndex >= 0 { s.journal = s.journal[:watermarkIndex] } + + // Release resources from discarded contexts after all state updates + s.releaseCtxResources(discardedCtx) + for i := len(discardedSnapshots) - 1; i >= 0; i-- { + s.releaseCtxResources(discardedSnapshots[i]) + } } func (s *DBImpl) handleResidualFundsInDestructedAccounts(st *TemporaryState) { @@ -217,6 +227,15 @@ func (s *DBImpl) getTransientState(acc common.Address, key common.Hash) (common. return val, found } +// releaseCtxResources returns poolable resources (EventManager, CMS stores) from +// a snapshot context back to their respective sync.Pools. +func (s *DBImpl) releaseCtxResources(ctx sdk.Context) { + sdk.ReleaseEventManager(ctx.EventManager()) + if cms, ok := ctx.MultiStore().(interface{ Release() }); ok { + cms.Release() + } +} + func deleteIfExists(store storetypes.KVStore, key []byte) bool { if store.Has(key) { store.Delete(key) diff --git a/sei-cosmos/store/multiversion/mvkv.go b/sei-cosmos/store/multiversion/mvkv.go index 33f509540c..ea9fa674d7 100644 --- a/sei-cosmos/store/multiversion/mvkv.go +++ b/sei-cosmos/store/multiversion/mvkv.go @@ -110,13 +110,20 @@ func NewVersionIndexedStore(parent types.KVStore, multiVersionStore MultiVersion } // Reset reinitializes the store for reuse from a pool. -// We allocate fresh maps and slices instead of clearing/reusing because the old -// data may have been handed off to the multiversion store (via SetReadset/ -// SetWriteset/SetIterateset) and is still referenced for validation. +// SetReadset/SetWriteset/SetIterateset now clone data before storing, so we can +// safely clear and reuse the maps/slices here instead of allocating fresh ones. func (store *VersionIndexedStore) Reset(parent types.KVStore, mvs MultiVersionStore, index, incarnation int, abortCh chan scheduler.Abort) { - store.readset = make(ReadSet) - store.writeset = make(map[string][]byte) - store.iterateset = nil + if store.readset != nil { + clear(store.readset) + } else { + store.readset = make(ReadSet) + } + if store.writeset != nil { + clear(store.writeset) + } else { + store.writeset = make(map[string][]byte) + } + store.iterateset = store.iterateset[:0] store.parent = parent store.multiVersionStore = mvs store.transactionIndex = index diff --git a/sei-cosmos/store/multiversion/store.go b/sei-cosmos/store/multiversion/store.go index 0ef549593c..3c856407a9 100644 --- a/sei-cosmos/store/multiversion/store.go +++ b/sei-cosmos/store/multiversion/store.go @@ -249,9 +249,14 @@ func (s *Store) GetAllWritesetKeys() map[int][]string { } func (s *Store) SetReadset(index int, readset ReadSet) { + // Clone the readset so the caller (VIS) can reuse its map via clear(). + clone := make(ReadSet, len(readset)) + for k, v := range readset { + clone[k] = v + } sl := s.slot(index) sl.mu.Lock() - sl.readset = readset + sl.readset = clone sl.mu.Unlock() } @@ -264,9 +269,12 @@ func (s *Store) GetReadset(index int) ReadSet { } func (s *Store) SetIterateset(index int, iterateset Iterateset) { + // Clone the iterateset so the caller (VIS) can reuse its slice via [:0]. + clone := make(Iterateset, len(iterateset)) + copy(clone, iterateset) sl := s.slot(index) sl.mu.Lock() - sl.iterateset = iterateset + sl.iterateset = clone sl.mu.Unlock() } diff --git a/sei-cosmos/types/events.go b/sei-cosmos/types/events.go index cdd1586b8d..ace695da45 100644 --- a/sei-cosmos/types/events.go +++ b/sei-cosmos/types/events.go @@ -45,14 +45,28 @@ const ( AttributeKeyAccessTypeRead = "read" ) +var eventManagerPool = sync.Pool{ + New: func() any { + return &EventManager{ + events: make(Events, 0, 8), + } + }, +} + func NewEventManager() *EventManager { - em := EventManager{ - mtx: sync.RWMutex{}, + em := eventManagerPool.Get().(*EventManager) + em.events = em.events[:0] + return em +} + +// ReleaseEventManager returns an EventManager to the pool for reuse. +// The caller must not use the EventManager after calling this. +func ReleaseEventManager(em *EventManager) { + if em == nil { + return } - em.mtx.Lock() - defer em.mtx.Unlock() - em.events = EmptyEvents() - return &em + em.events = em.events[:0] + eventManagerPool.Put(em) } func (em *EventManager) Events() Events { return em.events } @@ -191,12 +205,10 @@ type ( // NewEvent creates a new Event object with a given type and slice of one or more // attributes. func NewEvent(ty string, attrs ...Attribute) Event { - e := Event{Type: ty} - - for _, attr := range attrs { - e.Attributes = append(e.Attributes, attr.ToKVPair()) + e := Event{Type: ty, Attributes: make([]abci.EventAttribute, len(attrs))} + for i, attr := range attrs { + e.Attributes[i] = attr.ToKVPair() } - return e } @@ -344,33 +356,23 @@ func StringifyEvents(events []abci.Event) StringEvents { return res.Flatten() } -// MarkEventsToIndex returns the set of ABCI events, where each event's attribute -// has it's index value marked based on the provided set of events to index. +// MarkEventsToIndex modifies the ABCI events in-place, setting each attribute's +// Index field based on the provided set of events to index. func MarkEventsToIndex(events []abci.Event, indexSet map[string]struct{}) []abci.Event { indexAll := len(indexSet) == 0 - updatedEvents := make([]abci.Event, len(events)) - for i, e := range events { - updatedEvent := abci.Event{ - Type: e.Type, - Attributes: make([]abci.EventAttribute, len(e.Attributes)), - } - - for j, attr := range e.Attributes { - _, index := indexSet[fmt.Sprintf("%s.%s", e.Type, attr.Key)] - updatedAttr := abci.EventAttribute{ - Key: attr.Key, - Value: attr.Value, - Index: index || indexAll, + for i := range events { + e := &events[i] + for j := range e.Attributes { + if indexAll { + e.Attributes[j].Index = true + } else { + _, e.Attributes[j].Index = indexSet[fmt.Sprintf("%s.%s", e.Type, e.Attributes[j].Key)] } - - updatedEvent.Attributes[j] = updatedAttr } - - updatedEvents[i] = updatedEvent } - return updatedEvents + return events } type EVMEventManager struct { diff --git a/sei-cosmos/types/events_test.go b/sei-cosmos/types/events_test.go index 156da246be..2b68b8b470 100644 --- a/sei-cosmos/types/events_test.go +++ b/sei-cosmos/types/events_test.go @@ -131,30 +131,31 @@ func (s *eventsTestSuite) TestStringifyEvents() { } func (s *eventsTestSuite) TestMarkEventsToIndex() { - events := []abci.Event{ - { - Type: "message", - Attributes: []abci.EventAttribute{ - {Key: []byte("sender"), Value: []byte("foo")}, - {Key: []byte("recipient"), Value: []byte("bar")}, + // Helper to create fresh events for each subtest since MarkEventsToIndex modifies in-place. + newEvents := func() []abci.Event { + return []abci.Event{ + { + Type: "message", + Attributes: []abci.EventAttribute{ + {Key: []byte("sender"), Value: []byte("foo")}, + {Key: []byte("recipient"), Value: []byte("bar")}, + }, }, - }, - { - Type: "staking", - Attributes: []abci.EventAttribute{ - {Key: []byte("deposit"), Value: []byte("5")}, - {Key: []byte("unbond"), Value: []byte("10")}, + { + Type: "staking", + Attributes: []abci.EventAttribute{ + {Key: []byte("deposit"), Value: []byte("5")}, + {Key: []byte("unbond"), Value: []byte("10")}, + }, }, - }, + } } testCases := map[string]struct { - events []abci.Event indexSet map[string]struct{} expected []abci.Event }{ "empty index set": { - events: events, expected: []abci.Event{ { Type: "message", @@ -174,7 +175,6 @@ func (s *eventsTestSuite) TestMarkEventsToIndex() { indexSet: map[string]struct{}{}, }, "index some events": { - events: events, expected: []abci.Event{ { Type: "message", @@ -197,7 +197,6 @@ func (s *eventsTestSuite) TestMarkEventsToIndex() { }, }, "index all events": { - events: events, expected: []abci.Event{ { Type: "message", @@ -226,7 +225,8 @@ func (s *eventsTestSuite) TestMarkEventsToIndex() { for name, tc := range testCases { tc := tc s.T().Run(name, func(_ *testing.T) { - legacyEvents := sdk.MarkEventsToIndex(tc.events, tc.indexSet) + events := newEvents() + legacyEvents := sdk.MarkEventsToIndex(events, tc.indexSet) s.Require().Equal(tc.expected, legacyEvents) }) }