Skip to content

Commit

Permalink
core/state: move revision-handling into journal
Browse files Browse the repository at this point in the history
  • Loading branch information
holiman committed Jan 26, 2024
1 parent fc340e5 commit 2ccd040
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 30 deletions.
45 changes: 45 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand All @@ -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)
Expand Down
35 changes: 5 additions & 30 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package state

import (
"fmt"
"sort"
"time"

"github.com/ethereum/go-ethereum/common"
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 2ccd040

Please sign in to comment.