core/state: separate journal-implementation behind interface, implement createaccount

This commit is contained in:
Martin Holst Swende 2024-04-19 09:08:05 +02:00
parent bc1ec69008
commit 2c9afab254
No known key found for this signature in database
GPG Key ID: 683B438C05A5DDF0
5 changed files with 186 additions and 84 deletions

View File

@ -32,33 +32,36 @@ type revision struct {
journalIndex 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 linear journal that can be
// reverted on demand. // reverted on demand.
type journalEntry interface { type journalEntry interface {
// revert undoes the changes introduced by this journal entry. // revert undoes the changes introduced by this entry.
revert(*StateDB) revert(*StateDB)
// dirtied returns the Ethereum address modified by this journal entry. // dirtied returns the Ethereum address modified by this entry.
dirtied() *common.Address dirtied() *common.Address
// copy returns a deep-copied journal entry. // copy returns a deep-copied entry.
copy() journalEntry copy() journalEntry
} }
// journal contains the list of state modifications applied since the last state // linearJournal contains the list of state modifications applied since the last state
// commit. These are tracked to be able to be reverted in the case of an execution // commit. These are tracked to be able to be reverted in the case of an execution
// exception or request for reversal. // exception or request for reversal.
type journal struct { type linearJournal struct {
entries []journalEntry // Current changes tracked by the journal entries []journalEntry // Current changes tracked by the linearJournal
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 validRevisions []revision
nextRevisionId int nextRevisionId int
} }
// newJournal creates a new initialized journal. // compile-time interface check
func newJournal() *journal { var _ journal = (*linearJournal)(nil)
return &journal{
// newLinearJournal creates a new initialized linearJournal.
func newLinearJournal() *linearJournal {
return &linearJournal{
dirties: make(map[common.Address]int), dirties: make(map[common.Address]int),
} }
} }
@ -66,15 +69,24 @@ func newJournal() *journal {
// reset clears the journal, after this operation the journal can be used anew. // reset clears the journal, after this operation the journal can be used anew.
// It is semantically similar to calling 'newJournal', but the underlying slices // It is semantically similar to calling 'newJournal', but the underlying slices
// can be reused. // can be reused.
func (j *journal) reset() { func (j *linearJournal) reset() {
j.entries = j.entries[:0] j.entries = j.entries[:0]
j.validRevisions = j.validRevisions[:0] j.validRevisions = j.validRevisions[:0]
clear(j.dirties) clear(j.dirties)
j.nextRevisionId = 0 j.nextRevisionId = 0
} }
func (j linearJournal) dirtyAccounts() []common.Address {
dirty := make([]common.Address, 0, len(j.dirties))
// flatten into list
for addr := range j.dirties {
dirty = append(dirty, addr)
}
return dirty
}
// snapshot returns an identifier for the current revision of the state. // snapshot returns an identifier for the current revision of the state.
func (j *journal) snapshot() int { func (j *linearJournal) snapshot() int {
id := j.nextRevisionId id := j.nextRevisionId
j.nextRevisionId++ j.nextRevisionId++
j.validRevisions = append(j.validRevisions, revision{id, j.length()}) j.validRevisions = append(j.validRevisions, revision{id, j.length()})
@ -82,23 +94,23 @@ func (j *journal) snapshot() int {
} }
// revertToSnapshot reverts all state changes made since the given revision. // revertToSnapshot reverts all state changes made since the given revision.
func (j *journal) revertToSnapshot(revid int, s *StateDB) { func (j *linearJournal) revertToSnapshot(revid int, s *StateDB) {
// Find the snapshot in the stack of valid snapshots. // Find the snapshot in the stack of valid snapshots.
idx := sort.Search(len(j.validRevisions), func(i int) bool { idx := sort.Search(len(j.validRevisions), func(i int) bool {
return j.validRevisions[i].id >= revid return j.validRevisions[i].id >= revid
}) })
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid { if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid)) panic(fmt.Errorf("revision id %v cannot be reverted (valid revisions: %d)", revid, len(j.validRevisions)))
} }
snapshot := j.validRevisions[idx].journalIndex snapshot := j.validRevisions[idx].journalIndex
// Replay the journal to undo changes and remove invalidated snapshots // Replay the linearJournal to undo changes and remove invalidated snapshots
j.revert(s, snapshot) j.revert(s, snapshot)
j.validRevisions = j.validRevisions[:idx] 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 linearJournal.
func (j *journal) append(entry journalEntry) { func (j *linearJournal) append(entry journalEntry) {
j.entries = append(j.entries, entry) j.entries = append(j.entries, entry)
if addr := entry.dirtied(); addr != nil { if addr := entry.dirtied(); addr != nil {
j.dirties[*addr]++ j.dirties[*addr]++
@ -107,7 +119,7 @@ func (j *journal) append(entry journalEntry) {
// revert undoes a batch of journalled modifications along with any reverted // revert undoes a batch of journalled modifications along with any reverted
// dirty handling too. // dirty handling too.
func (j *journal) revert(statedb *StateDB, snapshot int) { func (j *linearJournal) revert(statedb *StateDB, snapshot int) {
for i := len(j.entries) - 1; i >= snapshot; i-- { for i := len(j.entries) - 1; i >= snapshot; i-- {
// Undo the changes made by the operation // Undo the changes made by the operation
j.entries[i].revert(statedb) j.entries[i].revert(statedb)
@ -125,22 +137,22 @@ func (j *journal) revert(statedb *StateDB, snapshot int) {
// dirty explicitly sets an address to dirty, even if the change entries would // dirty explicitly sets an address to dirty, even if the change entries would
// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD // otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD
// precompile consensus exception. // precompile consensus exception.
func (j *journal) dirty(addr common.Address) { func (j *linearJournal) dirty(addr common.Address) {
j.dirties[addr]++ j.dirties[addr]++
} }
// length returns the current number of entries in the journal. // length returns the current number of entries in the linearJournal.
func (j *journal) length() int { func (j *linearJournal) length() int {
return len(j.entries) return len(j.entries)
} }
// copy returns a deep-copied journal. // copy returns a deep-copied journal.
func (j *journal) copy() *journal { func (j *linearJournal) copy() journal {
entries := make([]journalEntry, 0, j.length()) entries := make([]journalEntry, 0, j.length())
for i := 0; i < j.length(); i++ { for i := 0; i < j.length(); i++ {
entries = append(entries, j.entries[i].copy()) entries = append(entries, j.entries[i].copy())
} }
return &journal{ return &linearJournal{
entries: entries, entries: entries,
dirties: maps.Clone(j.dirties), dirties: maps.Clone(j.dirties),
validRevisions: slices.Clone(j.validRevisions), validRevisions: slices.Clone(j.validRevisions),
@ -148,23 +160,23 @@ func (j *journal) copy() *journal {
} }
} }
func (j *journal) logChange(txHash common.Hash) { func (j *linearJournal) logChange(txHash common.Hash) {
j.append(addLogChange{txhash: txHash}) j.append(addLogChange{txhash: txHash})
} }
func (j *journal) createObject(addr common.Address) { func (j *linearJournal) createObject(addr common.Address) {
j.append(createObjectChange{account: addr}) j.append(createObjectChange{account: addr})
} }
func (j *journal) createContract(addr common.Address) { func (j *linearJournal) createContract(addr common.Address) {
j.append(createContractChange{account: addr}) j.append(createContractChange{account: addr})
} }
func (j *journal) destruct(addr common.Address) { func (j *linearJournal) destruct(addr common.Address) {
j.append(selfDestructChange{account: addr}) j.append(selfDestructChange{account: addr})
} }
func (j *journal) storageChange(addr common.Address, key, prev, origin common.Hash) { func (j *linearJournal) storageChange(addr common.Address, key, prev, origin common.Hash) {
j.append(storageChange{ j.append(storageChange{
account: addr, account: addr,
key: key, key: key,
@ -173,7 +185,7 @@ func (j *journal) storageChange(addr common.Address, key, prev, origin common.Ha
}) })
} }
func (j *journal) transientStateChange(addr common.Address, key, prev common.Hash) { func (j *linearJournal) transientStateChange(addr common.Address, key, prev common.Hash) {
j.append(transientStorageChange{ j.append(transientStorageChange{
account: addr, account: addr,
key: key, key: key,
@ -181,32 +193,32 @@ func (j *journal) transientStateChange(addr common.Address, key, prev common.Has
}) })
} }
func (j *journal) refundChange(previous uint64) { func (j *linearJournal) refundChange(previous uint64) {
j.append(refundChange{prev: previous}) j.append(refundChange{prev: previous})
} }
func (j *journal) balanceChange(addr common.Address, previous *uint256.Int) { func (j *linearJournal) balanceChange(addr common.Address, previous *uint256.Int) {
j.append(balanceChange{ j.append(balanceChange{
account: addr, account: addr,
prev: previous.Clone(), prev: previous.Clone(),
}) })
} }
func (j *journal) setCode(address common.Address, prevCode []byte) { func (j *linearJournal) setCode(address common.Address, prevCode []byte) {
j.append(codeChange{ j.append(codeChange{
account: address, account: address,
prevCode: prevCode, prevCode: prevCode,
}) })
} }
func (j *journal) nonceChange(address common.Address, prev uint64) { func (j *linearJournal) nonceChange(address common.Address, prev uint64) {
j.append(nonceChange{ j.append(nonceChange{
account: address, account: address,
prev: prev, prev: prev,
}) })
} }
func (j *journal) touchChange(address common.Address) { func (j *linearJournal) touchChange(address common.Address) {
j.append(touchChange{ j.append(touchChange{
account: address, account: address,
}) })
@ -217,11 +229,11 @@ func (j *journal) touchChange(address common.Address) {
} }
} }
func (j *journal) accessListAddAccount(addr common.Address) { func (j *linearJournal) accessListAddAccount(addr common.Address) {
j.append(accessListAddAccountChange{addr}) j.append(accessListAddAccountChange{addr})
} }
func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) { func (j *linearJournal) accessListAddSlot(addr common.Address, slot common.Hash) {
j.append(accessListAddSlotChange{ j.append(accessListAddSlotChange{
address: addr, address: addr,
slot: slot, slot: slot,
@ -234,7 +246,7 @@ type (
account common.Address account common.Address
} }
// createContractChange represents an account becoming a contract-account. // createContractChange represents an account becoming a contract-account.
// This event happens prior to executing initcode. The journal-event simply // This event happens prior to executing initcode. The linearJournal-event simply
// manages the created-flag, in order to allow same-tx destruction. // manages the created-flag, in order to allow same-tx destruction.
createContractChange struct { createContractChange struct {
account common.Address account common.Address
@ -464,7 +476,7 @@ func (ch addLogChange) copy() journalEntry {
func (ch accessListAddAccountChange) revert(s *StateDB) { func (ch accessListAddAccountChange) revert(s *StateDB) {
/* /*
One important invariant here, is that whenever a (addr, slot) is added, if the One important invariant here, is that whenever a (addr, slot) is added, if the
addr is not already present, the add causes two journal entries: addr is not already present, the add causes two linearJournal entries:
- one for the address, - one for the address,
- one for the (address,slot) - one for the (address,slot)
Therefore, when unrolling the change, we can always blindly delete the Therefore, when unrolling the change, we can always blindly delete the

69
core/state/journal_api.go Normal file
View File

@ -0,0 +1,69 @@
package state
import (
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
type journal interface {
// snapshot returns an identifier for the current revision of the state.
snapshot() int
// revertToSnapshot reverts all state changes made since the given revision.
revertToSnapshot(revid int, s *StateDB)
// reset clears the journal so it can be reused.
reset()
// dirtyAccounts returns a list of all accounts modified in this journal
dirtyAccounts() []common.Address
// accessListAddAccount journals the adding of addr to the access list
accessListAddAccount(addr common.Address)
// accessListAddSlot journals the adding of addr/slot to the access list
accessListAddSlot(addr common.Address, slot common.Hash)
// logChange journals the adding of a log related to the txHash
logChange(txHash common.Hash)
// createObject journals the event of a new account created in the trie.
createObject(addr common.Address)
// createContract journals the creation of a new contract at addr.
// OBS: This method must not be applied twice, it assumes that the pre-state
// (i.e the rollback-state) is non-created.
createContract(addr common.Address)
// destruct journals the destruction of an account in the trie.
// OBS: This method must not be applied twice -- it always assumes that the
// pre-state (i.e the rollback-state) is non-destructed.
destruct(addr common.Address)
// storageChange journals a change in the storage data related to addr.
// It records the key and previous value of the slot.
storageChange(addr common.Address, key, prev, origin common.Hash)
// transientStateChange journals a change in the t-storage data related to addr.
// It records the key and previous value of the slot.
transientStateChange(addr common.Address, key, prev common.Hash)
// refundChange journals that the refund has been changed, recording the previous value.
refundChange(previous uint64)
// balanceChange journals tha the balance of addr has been changed, recording the previous value
balanceChange(addr common.Address, previous *uint256.Int)
// JournalSetCode journals that the code of addr has been set.
setCode(addr common.Address, prev []byte)
// nonceChange journals that the nonce of addr was changed, recording the previous value.
nonceChange(addr common.Address, prev uint64)
// touchChange journals that the account at addr was touched during execution.
touchChange(addr common.Address)
// copy returns a deep-copied journal.
copy() journal
}

View File

@ -133,7 +133,7 @@ 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
// State witness if cross validation is needed // State witness if cross validation is needed
witness *stateless.Witness witness *stateless.Witness
@ -177,7 +177,7 @@ func New(root common.Hash, db Database) (*StateDB, error) {
mutations: make(map[common.Address]*mutation), mutations: make(map[common.Address]*mutation),
logs: make(map[common.Hash][]*types.Log), logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte), preimages: make(map[common.Hash][]byte),
journal: newJournal(), journal: newLinearJournal(),
accessList: newAccessList(), accessList: newAccessList(),
transientStorage: newTransientStorage(), transientStorage: newTransientStorage(),
} }
@ -727,8 +727,9 @@ func (s *StateDB) GetRefund() uint64 {
// the journal as well as the refunds. Finalise, however, will not push any updates // the journal as well as the refunds. Finalise, however, will not push any updates
// into the tries just yet. Only IntermediateRoot or Commit will do that. // into the tries just yet. Only IntermediateRoot or Commit will do that.
func (s *StateDB) Finalise(deleteEmptyObjects bool) { func (s *StateDB) Finalise(deleteEmptyObjects bool) {
addressesToPrefetch := make([]common.Address, 0, len(s.journal.dirties)) dirties := s.journal.dirtyAccounts()
for addr := range s.journal.dirties { addressesToPrefetch := make([]common.Address, 0, len(dirties))
for _, addr := range dirties {
obj, exist := s.stateObjects[addr] obj, exist := s.stateObjects[addr]
if !exist { if !exist {
// ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 // ripeMD is 'touched' at block 1714175, in tx 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2

View File

@ -259,7 +259,7 @@ func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) {
if s.hooks.OnBalanceChange == nil { if s.hooks.OnBalanceChange == nil {
return return
} }
for addr := range s.inner.journal.dirties { for _, addr := range s.inner.journal.dirtyAccounts() {
obj := s.inner.stateObjects[addr] obj := s.inner.stateObjects[addr]
if obj != nil && obj.selfDestructed { if obj != nil && obj.selfDestructed {
// If ether was sent to account post-selfdestruct it is burnt. // If ether was sent to account post-selfdestruct it is burnt.

View File

@ -228,7 +228,7 @@ func TestCopy(t *testing.T) {
} }
// TestCopyWithDirtyJournal tests if Copy can correct create a equal copied // TestCopyWithDirtyJournal tests if Copy can correct create a equal copied
// stateDB with dirty journal present. // stateDB with dirty linearJournal present.
func TestCopyWithDirtyJournal(t *testing.T) { func TestCopyWithDirtyJournal(t *testing.T) {
db := NewDatabaseForTesting() db := NewDatabaseForTesting()
orig, _ := New(types.EmptyRootHash, db) orig, _ := New(types.EmptyRootHash, db)
@ -410,8 +410,8 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
// We also set some code here, to prevent the // We also set some code here, to prevent the
// CreateContract action from being performed twice in a row, // CreateContract action from being performed twice in a row,
// which would cause a difference in state when unrolling // which would cause a difference in state when unrolling
// the journal. (CreateContact assumes created was false prior to // the linearJournal. (CreateContact assumes created was false prior to
// invocation, and the journal rollback sets it to false). // invocation, and the linearJournal rollback sets it to false).
s.SetCode(addr, []byte{1}) s.SetCode(addr, []byte{1})
} }
}, },
@ -673,22 +673,23 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{})) state.GetLogs(common.Hash{}, 0, common.Hash{}), checkstate.GetLogs(common.Hash{}, 0, common.Hash{}))
} }
if !maps.Equal(state.journal.dirties, checkstate.journal.dirties) { { // Check the dirty-accounts
getKeys := func(dirty map[common.Address]int) string { have := state.journal.dirtyAccounts()
var keys []common.Address want := checkstate.journal.dirtyAccounts()
out := new(strings.Builder) slices.SortFunc(have, common.Address.Cmp)
for key := range dirty { slices.SortFunc(want, common.Address.Cmp)
keys = append(keys, key) if !slices.Equal(have, want) {
getKeys := func(keys []common.Address) string {
out := new(strings.Builder)
for i, key := range keys {
fmt.Fprintf(out, " %d. %v\n", i, key)
}
return out.String()
} }
slices.SortFunc(keys, common.Address.Cmp) haveK := getKeys(state.journal.dirtyAccounts())
for i, key := range keys { wantK := getKeys(checkstate.journal.dirtyAccounts())
fmt.Fprintf(out, " %d. %v\n", i, key) return fmt.Errorf("dirty-journal set mismatch.\nhave:\n%v\nwant:\n%v\n", haveK, wantK)
}
return out.String()
} }
have := getKeys(state.journal.dirties)
want := getKeys(checkstate.journal.dirties)
return fmt.Errorf("dirty-journal set mismatch.\nhave:\n%v\nwant:\n%v\n", have, want)
} }
return nil return nil
} }
@ -702,11 +703,11 @@ func TestTouchDelete(t *testing.T) {
snapshot := s.state.Snapshot() snapshot := s.state.Snapshot()
s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified) s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified)
if len(s.state.journal.dirties) != 1 { if len(s.state.journal.dirtyAccounts()) != 1 {
t.Fatal("expected one dirty state object") t.Fatal("expected one dirty state object")
} }
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
if len(s.state.journal.dirties) != 0 { if len(s.state.journal.dirtyAccounts()) != 0 {
t.Fatal("expected no dirty state object") t.Fatal("expected no dirty state object")
} }
} }
@ -1091,32 +1092,51 @@ func TestStateDBAccessList(t *testing.T) {
} }
} }
var ids []int
push := func(id int) {
ids = append(ids, id)
}
pop := func() int {
id := ids[len(ids)-1]
ids = ids[:len(ids)-1]
return id
}
push(state.journal.snapshot()) // journal id 0
state.AddAddressToAccessList(addr("aa")) // 1 state.AddAddressToAccessList(addr("aa")) // 1
state.AddSlotToAccessList(addr("bb"), slot("01")) // 2,3 push(state.journal.snapshot()) // journal id 1
state.AddAddressToAccessList(addr("bb")) // 2
push(state.journal.snapshot()) // journal id 2
state.AddSlotToAccessList(addr("bb"), slot("01")) // 3
push(state.journal.snapshot()) // journal id 3
state.AddSlotToAccessList(addr("bb"), slot("02")) // 4 state.AddSlotToAccessList(addr("bb"), slot("02")) // 4
push(state.journal.snapshot()) // journal id 4
verifyAddrs("aa", "bb") verifyAddrs("aa", "bb")
verifySlots("bb", "01", "02") verifySlots("bb", "01", "02")
// Make a copy // Make a copy
stateCopy1 := state.Copy() stateCopy1 := state.Copy()
if exp, got := 4, state.journal.length(); exp != got { if exp, got := 4, state.journal.(*linearJournal).length(); exp != got {
t.Fatalf("journal length mismatch: have %d, want %d", got, exp) t.Fatalf("linearJournal length mismatch: have %d, want %d", got, exp)
} }
// same again, should cause no journal entries // same again, should cause no linearJournal entries
state.AddSlotToAccessList(addr("bb"), slot("01")) state.AddSlotToAccessList(addr("bb"), slot("01"))
state.AddSlotToAccessList(addr("bb"), slot("02")) state.AddSlotToAccessList(addr("bb"), slot("02"))
state.AddAddressToAccessList(addr("aa")) state.AddAddressToAccessList(addr("aa"))
if exp, got := 4, state.journal.length(); exp != got { if exp, got := 4, state.journal.(*linearJournal).length(); exp != got {
t.Fatalf("journal length mismatch: have %d, want %d", got, exp) t.Fatalf("linearJournal length mismatch: have %d, want %d", got, exp)
} }
// some new ones // some new ones
state.AddSlotToAccessList(addr("bb"), slot("03")) // 5 state.AddSlotToAccessList(addr("bb"), slot("03")) // 5
push(state.journal.snapshot()) // journal id 5
state.AddSlotToAccessList(addr("aa"), slot("01")) // 6 state.AddSlotToAccessList(addr("aa"), slot("01")) // 6
state.AddSlotToAccessList(addr("cc"), slot("01")) // 7,8 push(state.journal.snapshot()) // journal id 6
state.AddAddressToAccessList(addr("cc")) state.AddAddressToAccessList(addr("cc")) // 7
if exp, got := 8, state.journal.length(); exp != got { push(state.journal.snapshot()) // journal id 7
t.Fatalf("journal length mismatch: have %d, want %d", got, exp) state.AddSlotToAccessList(addr("cc"), slot("01")) // 8
if exp, got := 8, state.journal.(*linearJournal).length(); exp != got {
t.Fatalf("linearJournal length mismatch: have %d, want %d", got, exp)
} }
verifyAddrs("aa", "bb", "cc") verifyAddrs("aa", "bb", "cc")
@ -1125,7 +1145,7 @@ func TestStateDBAccessList(t *testing.T) {
verifySlots("cc", "01") verifySlots("cc", "01")
// now start rolling back changes // now start rolling back changes
state.journal.revert(state, 7) state.journal.revertToSnapshot(pop(), state) // revert to 6
if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok { if _, ok := state.SlotInAccessList(addr("cc"), slot("01")); ok {
t.Fatalf("slot present, expected missing") t.Fatalf("slot present, expected missing")
} }
@ -1133,7 +1153,7 @@ func TestStateDBAccessList(t *testing.T) {
verifySlots("aa", "01") verifySlots("aa", "01")
verifySlots("bb", "01", "02", "03") verifySlots("bb", "01", "02", "03")
state.journal.revert(state, 6) state.journal.revertToSnapshot(pop(), state) // revert to 5
if state.AddressInAccessList(addr("cc")) { if state.AddressInAccessList(addr("cc")) {
t.Fatalf("addr present, expected missing") t.Fatalf("addr present, expected missing")
} }
@ -1141,40 +1161,40 @@ func TestStateDBAccessList(t *testing.T) {
verifySlots("aa", "01") verifySlots("aa", "01")
verifySlots("bb", "01", "02", "03") verifySlots("bb", "01", "02", "03")
state.journal.revert(state, 5) state.journal.revertToSnapshot(pop(), state) // revert to 4
if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok { if _, ok := state.SlotInAccessList(addr("aa"), slot("01")); ok {
t.Fatalf("slot present, expected missing") t.Fatalf("slot present, expected missing")
} }
verifyAddrs("aa", "bb") verifyAddrs("aa", "bb")
verifySlots("bb", "01", "02", "03") verifySlots("bb", "01", "02", "03")
state.journal.revert(state, 4) state.journal.revertToSnapshot(pop(), state) // revert to 3
if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok { if _, ok := state.SlotInAccessList(addr("bb"), slot("03")); ok {
t.Fatalf("slot present, expected missing") t.Fatalf("slot present, expected missing")
} }
verifyAddrs("aa", "bb") verifyAddrs("aa", "bb")
verifySlots("bb", "01", "02") verifySlots("bb", "01", "02")
state.journal.revert(state, 3) state.journal.revertToSnapshot(pop(), state) // revert to 2
if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok { if _, ok := state.SlotInAccessList(addr("bb"), slot("02")); ok {
t.Fatalf("slot present, expected missing") t.Fatalf("slot present, expected missing")
} }
verifyAddrs("aa", "bb") verifyAddrs("aa", "bb")
verifySlots("bb", "01") verifySlots("bb", "01")
state.journal.revert(state, 2) state.journal.revertToSnapshot(pop(), state) // revert to 1
if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok { if _, ok := state.SlotInAccessList(addr("bb"), slot("01")); ok {
t.Fatalf("slot present, expected missing") t.Fatalf("slot present, expected missing")
} }
verifyAddrs("aa", "bb") verifyAddrs("aa", "bb")
state.journal.revert(state, 1) state.journal.revertToSnapshot(pop(), state) // revert to 0
if state.AddressInAccessList(addr("bb")) { if state.AddressInAccessList(addr("bb")) {
t.Fatalf("addr present, expected missing") t.Fatalf("addr present, expected missing")
} }
verifyAddrs("aa") verifyAddrs("aa")
state.journal.revert(state, 0) state.journal.revertToSnapshot(0, state)
if state.AddressInAccessList(addr("aa")) { if state.AddressInAccessList(addr("aa")) {
t.Fatalf("addr present, expected missing") t.Fatalf("addr present, expected missing")
} }
@ -1245,10 +1265,10 @@ func TestStateDBTransientStorage(t *testing.T) {
key := common.Hash{0x01} key := common.Hash{0x01}
value := common.Hash{0x02} value := common.Hash{0x02}
addr := common.Address{} addr := common.Address{}
revision := state.journal.snapshot()
state.SetTransientState(addr, key, value) state.SetTransientState(addr, key, value)
if exp, got := 1, state.journal.length(); exp != got { if exp, got := 1, state.journal.(*linearJournal).length(); exp != got {
t.Fatalf("journal length mismatch: have %d, want %d", got, exp) t.Fatalf("linearJournal length mismatch: have %d, want %d", got, exp)
} }
// the retrieved value should equal what was set // the retrieved value should equal what was set
if got := state.GetTransientState(addr, key); got != value { if got := state.GetTransientState(addr, key); got != value {
@ -1257,7 +1277,7 @@ func TestStateDBTransientStorage(t *testing.T) {
// revert the transient state being set and then check that the // revert the transient state being set and then check that the
// value is now the empty hash // value is now the empty hash
state.journal.revert(state, 0) state.journal.revertToSnapshot(revision, state)
if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got { if got, exp := state.GetTransientState(addr, key), (common.Hash{}); exp != got {
t.Fatalf("transient storage mismatch: have %x, want %x", got, exp) t.Fatalf("transient storage mismatch: have %x, want %x", got, exp)
} }