core/state: move revision-handling into journal

This commit is contained in:
Martin Holst Swende 2024-01-26 16:43:32 +01:00
parent 137fdb7fe2
commit 36e875a361
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 49 additions and 32 deletions

View file

@ -17,13 +17,20 @@
package state package state
import ( import (
"fmt"
"maps" "maps"
"sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
type revision struct {
id int
journalIndex int
}
// journalEntry is a modification entry in the state change journal that can be // journalEntry is a modification entry in the state change journal that can be
// reverted on demand. // reverted on demand.
type journalEntry interface { type journalEntry interface {
@ -43,6 +50,9 @@ type journalEntry interface {
type journal struct { type journal struct {
entries []journalEntry // Current changes tracked by the journal entries []journalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes dirties map[common.Address]int // Dirty accounts and the number of changes
validRevisions []revision
nextRevisionId int
} }
// newJournal creates a new initialized journal. // 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. // append inserts a new modification entry to the end of the change journal.
func (j *journal) append(entry journalEntry) { func (j *journal) append(entry journalEntry) {
j.entries = append(j.entries, entry) j.entries = append(j.entries, entry)

View file

@ -23,7 +23,6 @@ import (
"maps" "maps"
"math/big" "math/big"
"slices" "slices"
"sort"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -48,11 +47,6 @@ import (
// TriesInMemory represents the number of layers that are kept in RAM. // TriesInMemory represents the number of layers that are kept in RAM.
const TriesInMemory = 128 const TriesInMemory = 128
type revision struct {
id int
journalIndex int
}
type mutationType int type mutationType int
const ( const (
@ -144,8 +138,6 @@ type StateDB struct {
// Journal of state modifications. This is the backbone of // Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot. // Snapshot and RevertToSnapshot.
journal *journal journal *journal
validRevisions []revision
nextRevisionId int
// State witness if cross validation is needed // State witness if cross validation is needed
witness *stateless.Witness witness *stateless.Witness
@ -703,8 +695,6 @@ func (s *StateDB) Copy() *StateDB {
logSize: s.logSize, logSize: s.logSize,
preimages: maps.Clone(s.preimages), preimages: maps.Clone(s.preimages),
journal: s.journal.copy(), 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 // 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 // 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. // Snapshot returns an identifier for the current revision of the state.
func (s *StateDB) Snapshot() int { func (s *StateDB) Snapshot() int {
id := s.nextRevisionId return s.journal.Snapshot()
s.nextRevisionId++
s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()})
return id
} }
// RevertToSnapshot reverts all state changes made since the given revision. // RevertToSnapshot reverts all state changes made since the given revision.
func (s *StateDB) RevertToSnapshot(revid int) { func (s *StateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots. s.journal.RevertToSnapshot(revid, s)
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]
} }
// GetRefund returns the current value of the refund counter. // GetRefund returns the current value of the refund counter.
@ -983,12 +959,9 @@ func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
} }
func (s *StateDB) clearJournalAndRefund() { func (s *StateDB) clearJournalAndRefund() {
if len(s.journal.entries) > 0 { s.journal.Reset()
s.journal = newJournal()
s.refund = 0 s.refund = 0
} }
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries
}
// fastDeleteStorage is the function that efficiently deletes the storage trie // fastDeleteStorage is the function that efficiently deletes the storage trie
// of a specific account. It leverages the associated state snapshot for fast // of a specific account. It leverages the associated state snapshot for fast