Skip to content

Commit

Permalink
Merge pull request #2145 from CortexFoundation/dev
Browse files Browse the repository at this point in the history
core/state: semantic journalling
  • Loading branch information
ucwong authored Sep 3, 2024
2 parents d8440ab + 605a798 commit 23fb332
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 178 deletions.
198 changes: 133 additions & 65 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,19 @@ package state
import (
"math/big"

"fmt"
"github.com/CortexFoundation/CortexTheseus/common"
"github.com/CortexFoundation/CortexTheseus/core/types"
"maps"
"slices"
"sort"
)

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 @@ -42,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 create a new initialized journal.
Expand All @@ -51,6 +63,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]
clear(j.dirties)
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 Expand Up @@ -95,11 +141,90 @@ 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) {
j.append(codeChange{account: &address})
}

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 @@ -114,26 +239,14 @@ type (
}

selfDestructChange struct {
account *common.Address
prev bool // whether account had already self-destructed
prevbalance *big.Int
prevupload *big.Int
prevnum *big.Int
account *common.Address
}

// Changes to individual accounts.
balanceChange struct {
account *common.Address
prev *big.Int
}
uploadChange struct {
account *common.Address
prev *big.Int
}
numChange struct {
account *common.Address
prev *big.Int
}
nonceChange struct {
account *common.Address
prev uint64
Expand All @@ -145,8 +258,7 @@ type (
origvalue common.Hash
}
codeChange struct {
account *common.Address
prevcode, prevhash []byte
account *common.Address
}

// Changes to other state values.
Expand All @@ -156,9 +268,6 @@ type (
addLogChange struct {
txhash common.Hash
}
addPreimageChange struct {
hash common.Hash
}
touchChange struct {
account *common.Address
}
Expand Down Expand Up @@ -210,10 +319,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.setUpload(ch.prevupload)
obj.setNum(ch.prevnum)
obj.selfDestructed = false
}
}

Expand All @@ -223,11 +329,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),
prevupload: new(big.Int).Set(ch.prevupload),
prevnum: new(big.Int).Set(ch.prevnum),
account: ch.account,
}
}

Expand All @@ -254,22 +356,6 @@ func (ch balanceChange) dirtied() *common.Address {
return ch.account
}

func (ch uploadChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setUpload(ch.prev)
}

func (ch uploadChange) dirtied() *common.Address {
return ch.account
}

func (ch numChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNum(ch.prev)
}

func (ch numChange) dirtied() *common.Address {
return ch.account
}

func (ch balanceChange) copy() journalEntry {
return balanceChange{
account: ch.account,
Expand All @@ -293,19 +379,15 @@ func (ch nonceChange) copy() journalEntry {
}

func (ch codeChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
s.getStateObject(*ch.account).setCode(types.EmptyCodeHash, nil)
}

func (ch codeChange) dirtied() *common.Address {
return ch.account
}

func (ch codeChange) copy() journalEntry {
return codeChange{
account: ch.account,
prevhash: common.CopyBytes(ch.prevhash),
prevcode: common.CopyBytes(ch.prevcode),
}
return codeChange{account: ch.account}
}

func (ch storageChange) revert(s *StateDB) {
Expand Down Expand Up @@ -374,20 +456,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
77 changes: 77 additions & 0 deletions core/state/journal_mdl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2023 The CortexTheseus Authors
// This file is part of the CortexFoundation library.
//
// The CortexFoundation library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The CortexFoundation library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the CortexFoundation library. If not, see <http://www.gnu.org/licenses/>.

package state

import (
"github.com/CortexFoundation/CortexTheseus/common"
"math/big"
)

type (
uploadChange struct {
account *common.Address
prev *big.Int
}
numChange struct {
account *common.Address
prev *big.Int
}
)

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

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

func (ch uploadChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setUpload(ch.prev)
}

func (ch uploadChange) dirtied() *common.Address {
return ch.account
}

func (ch numChange) revert(s *StateDB) {
s.getStateObject(*ch.account).setNum(ch.prev)
}

func (ch numChange) dirtied() *common.Address {
return ch.account
}

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

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

0 comments on commit 23fb332

Please sign in to comment.