Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 122 additions & 27 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@
package state

import (
"fmt"
"maps"
"math/big"
"slices"
"sort"

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/crypto"
)

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 @@ -43,6 +51,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 @@ -52,6 +63,41 @@ 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() {
clear(j.entries)
j.entries = j.entries[:0]
j.validRevisions = j.validRevisions[:0]
clear(j.dirties)
j.nextRevisionId = 0
}
Comment thread
gzliudan marked this conversation as resolved.

// 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 Expand Up @@ -96,22 +142,93 @@ func (j *journal) copy() *journal {
entries = append(entries, j.entries[i].copy())
}
return &journal{
entries: entries,
dirties: maps.Clone(j.dirties),
entries: entries,
dirties: maps.Clone(j.dirties),
validRevisions: slices.Clone(j.validRevisions),
nextRevisionId: j.nextRevisionId,
}
}

func (j *journal) logChange(txHash common.Hash) {
j.append(addLogChange{txhash: txHash})
}

func (j *journal) createObject(addr common.Address) {
j.append(createObjectChange{account: addr})
}

func (j *journal) createContract(addr common.Address) {
j.append(createContractChange{account: addr})
}

func (j *journal) destruct(addr common.Address) {
j.append(selfDestructChange{account: addr})
}

func (j *journal) storageChange(addr common.Address, key, prev, origin common.Hash) {
j.append(storageChange{
account: addr,
key: key,
prevvalue: prev,
origvalue: origin,
})
}

func (j *journal) transientStateChange(addr common.Address, key, prev common.Hash) {
j.append(transientStorageChange{
account: addr,
key: key,
prevalue: prev,
})
}

func (j *journal) refundChange(previous uint64) {
j.append(refundChange{prev: previous})
}

func (j *journal) balanceChange(addr common.Address, previous *big.Int) {
j.append(balanceChange{
account: addr,
prev: new(big.Int).Set(previous),
})
}

func (j *journal) setCode(address common.Address, prevCode []byte) {
j.append(codeChange{
account: address,
prevCode: prevCode,
})
}

func (j *journal) nonceChange(address common.Address, prev uint64) {
j.append(nonceChange{
account: address,
prev: prev,
})
}

func (j *journal) touchChange(address common.Address) {
j.append(touchChange{
account: address,
})
if address == ripemd {
// Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals.
j.dirty(address)
}
}

func (j *journal) accessListAddAccount(addr common.Address) {
j.append(accessListAddAccountChange{addr})
}

func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) {
j.append(accessListAddSlotChange{
address: addr,
slot: slot,
})
}

type (
// Changes to the account trie.
createObjectChange struct {
Expand All @@ -124,9 +241,7 @@ type (
account common.Address
}
selfDestructChange struct {
account common.Address
prev bool // whether account had already self-destructed
prevbalance *big.Int
account common.Address
}

// Changes to individual accounts.
Expand Down Expand Up @@ -156,9 +271,6 @@ type (
addLogChange struct {
txhash common.Hash
}
addPreimageChange struct {
hash common.Hash
}
touchChange struct {
account common.Address
}
Expand Down Expand Up @@ -210,8 +322,7 @@ func (ch createContractChange) copy() journalEntry {
func (ch selfDestructChange) revert(s *StateDB) {
obj := s.getStateObject(ch.account)
if obj != nil {
obj.selfDestructed = ch.prev
obj.setBalance(ch.prevbalance)
obj.selfDestructed = false
}
}

Expand All @@ -221,9 +332,7 @@ func (ch selfDestructChange) dirtied() *common.Address {

func (ch selfDestructChange) copy() journalEntry {
return selfDestructChange{
account: ch.account,
prev: ch.prev,
prevbalance: new(big.Int).Set(ch.prevbalance),
account: ch.account,
}
}

Expand Down Expand Up @@ -354,20 +463,6 @@ func (ch addLogChange) copy() journalEntry {
}
}

func (ch addPreimageChange) revert(s *StateDB) {
delete(s.preimages, ch.hash)
}

func (ch addPreimageChange) dirtied() *common.Address {
return nil
}

func (ch addPreimageChange) copy() journalEntry {
return addPreimageChange{
hash: ch.hash,
}
}

func (ch accessListAddAccountChange) revert(s *StateDB) {
/*
One important invariant here, is that whenever a (addr, slot) is added, if the
Expand Down
26 changes: 4 additions & 22 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,7 @@ func (s *stateObject) markSelfdestructed() {
}

func (s *stateObject) touch() {
s.db.journal.append(touchChange{
account: s.address,
})
if s.address == ripemd {
// Explicitly put it in the dirty-cache, which is otherwise generated from
// flattened journals.
s.db.journal.dirty(s.address)
}
s.db.journal.touchChange(s.address)
}

// getTrie returns the associated storage trie. The trie will be opened
Expand Down Expand Up @@ -194,12 +187,7 @@ func (s *stateObject) SetState(key, value common.Hash) common.Hash {
return prev
}
// New value is different, update and journal the change
s.db.journal.append(storageChange{
account: s.address,
key: key,
prevvalue: prev,
origvalue: origin,
})
s.db.journal.storageChange(s.address, key, prev, origin)
s.setState(key, value, origin)
return prev
}
Expand Down Expand Up @@ -379,10 +367,7 @@ func (s *stateObject) AddBalance(amount *big.Int) *big.Int {
// SetBalance sets the balance for the object, and returns the previous balance.
func (s *stateObject) SetBalance(amount *big.Int) *big.Int {
prev := new(big.Int).Set(s.data.Balance)
s.db.journal.append(balanceChange{
account: s.address,
prev: new(big.Int).Set(s.data.Balance),
})
s.db.journal.balanceChange(s.address, s.data.Balance)
s.setBalance(amount)
return prev
}
Expand Down Expand Up @@ -468,10 +453,7 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
}

func (s *stateObject) SetNonce(nonce uint64) {
s.db.journal.append(nonceChange{
account: s.address,
prev: s.data.Nonce,
})
s.db.journal.nonceChange(s.address, s.data.Nonce)
s.setNonce(nonce)
}

Expand Down
Loading