diff --git a/core/state/journal.go b/core/state/journal.go index b042e0cf3f..5fdf59c88b 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -17,13 +17,20 @@ package state import ( + "fmt" "maps" + "sort" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/holiman/uint256" ) +type revision struct { + id int + journalIndex int +} + // journalEntry is a modification entry in the state change journal that can be // reverted on demand. type journalEntry interface { @@ -43,6 +50,9 @@ type journalEntry interface { type journal struct { entries []journalEntry // Current changes tracked by the journal dirties map[common.Address]int // Dirty accounts and the number of changes + + validRevisions []revision + nextRevisionId int } // newJournal creates a new initialized journal. @@ -52,6 +62,40 @@ func newJournal() *journal { } } +// Reset clears the journal, after this operation the journal can be used +// anew. It is semantically similar to calling 'newJournal', but the underlying +// slices can be reused +func (j *journal) Reset() { + j.entries = j.entries[:0] + j.validRevisions = j.validRevisions[:0] + j.dirties = make(map[common.Address]int) + j.nextRevisionId = 0 +} + +// Snapshot returns an identifier for the current revision of the state. +func (j *journal) Snapshot() int { + id := j.nextRevisionId + j.nextRevisionId++ + j.validRevisions = append(j.validRevisions, revision{id, j.length()}) + return id +} + +// RevertToSnapshot reverts all state changes made since the given revision. +func (j *journal) RevertToSnapshot(revid int, s *StateDB) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(j.validRevisions), func(i int) bool { + return j.validRevisions[i].id >= revid + }) + if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid { + panic(fmt.Errorf("revision id %v cannot be reverted", revid)) + } + snapshot := j.validRevisions[idx].journalIndex + + // Replay the journal to undo changes and remove invalidated snapshots + j.revert(s, snapshot) + j.validRevisions = j.validRevisions[:idx] +} + // append inserts a new modification entry to the end of the change journal. func (j *journal) append(entry journalEntry) { j.entries = append(j.entries, entry) diff --git a/core/state/statedb.go b/core/state/statedb.go index 0457628864..c553418770 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -23,7 +23,6 @@ import ( "maps" "math/big" "slices" - "sort" "sync" "sync/atomic" "time" @@ -48,11 +47,6 @@ import ( // TriesInMemory represents the number of layers that are kept in RAM. const TriesInMemory = 128 -type revision struct { - id int - journalIndex int -} - type mutationType int const ( @@ -143,9 +137,7 @@ type StateDB struct { // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. - journal *journal - validRevisions []revision - nextRevisionId int + journal *journal // State witness if cross validation is needed witness *stateless.Witness @@ -703,8 +695,6 @@ func (s *StateDB) Copy() *StateDB { logSize: s.logSize, preimages: maps.Clone(s.preimages), journal: s.journal.copy(), - validRevisions: slices.Clone(s.validRevisions), - nextRevisionId: s.nextRevisionId, // In order for the block producer to be able to use and make additions // to the snapshot tree, we need to copy that as well. Otherwise, any @@ -750,26 +740,12 @@ func (s *StateDB) Copy() *StateDB { // Snapshot returns an identifier for the current revision of the state. func (s *StateDB) Snapshot() int { - id := s.nextRevisionId - s.nextRevisionId++ - s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) - return id + return s.journal.Snapshot() } // RevertToSnapshot reverts all state changes made since the given revision. func (s *StateDB) RevertToSnapshot(revid int) { - // Find the snapshot in the stack of valid snapshots. - idx := sort.Search(len(s.validRevisions), func(i int) bool { - return s.validRevisions[i].id >= revid - }) - if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid { - panic(fmt.Errorf("revision id %v cannot be reverted", revid)) - } - snapshot := s.validRevisions[idx].journalIndex - - // Replay the journal to undo changes and remove invalidated snapshots - s.journal.revert(s, snapshot) - s.validRevisions = s.validRevisions[:idx] + s.journal.RevertToSnapshot(revid, s) } // GetRefund returns the current value of the refund counter. @@ -983,11 +959,8 @@ func (s *StateDB) SetTxContext(thash common.Hash, ti int) { } func (s *StateDB) clearJournalAndRefund() { - if len(s.journal.entries) > 0 { - s.journal = newJournal() - s.refund = 0 - } - s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries + s.journal.Reset() + s.refund = 0 } // fastDeleteStorage is the function that efficiently deletes the storage trie