diff --git a/core/state/journal.go b/core/state/journal.go index d2d8e0805118..fb79c0212515 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -17,11 +17,19 @@ package state import ( + "fmt" + "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 { @@ -38,6 +46,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. @@ -47,6 +58,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 b06a637cc41c..295b1eadbe5b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -19,7 +19,6 @@ package state import ( "fmt" - "sort" "time" "github.com/ethereum/go-ethereum/common" @@ -42,11 +41,6 @@ const ( storageDeleteLimit = 512 * 1024 * 1024 ) -type revision struct { - id int - journalIndex int -} - // StateDB structs within the ethereum protocol are used to store anything // within the merkle trie. StateDBs take care of caching and storing // nested states. It's the general query interface to retrieve: @@ -113,9 +107,7 @@ type StateDB struct { // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. - journal *journal - validRevisions []revision - nextRevisionId int + journal *journal // Measurements gathered during execution for debugging purposes AccountReads time.Duration @@ -774,26 +766,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. @@ -924,11 +902,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