Skip to content
This repository was archived by the owner on Jun 6, 2025. It is now read-only.
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
2 changes: 1 addition & 1 deletion core/state/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
account.SecureKey = it.Key
}
addr := common.BytesToAddress(addrBytes)
obj := newObject(s, addr, data)
obj := newObject(s, addr, &data)
if !conf.SkipCode {
account.Code = obj.Code(s.db)
}
Expand Down
23 changes: 21 additions & 2 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,18 @@ type (
account *common.Address
}
resetObjectChange struct {
account *common.Address
prev *stateObject
prevdestruct bool

// tracking previous states of accounts and storages in snapshot, before each transaction
prevAccount []byte
prevStorage map[common.Hash][]byte

// tracking previous states of accounts and storages in trie, before each commit
prevAccountOriginExist bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could u split space here between Account & AccountOrigin and put some small documentation here for explaining the diferrent tracking here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

prevAccountOrigin []byte
prevStorageOrigin map[common.Hash][]byte
}
selfDestructChange struct {
account *common.Address
Expand Down Expand Up @@ -157,12 +167,21 @@ func (ch createObjectChange) dirtied() *common.Address {
func (ch resetObjectChange) revert(s *StateDB) {
s.setStateObject(ch.prev)
if !ch.prevdestruct && s.snap != nil {
delete(s.snapDestructs, ch.prev.addrHash)
delete(s.stateObjectsDestruct, ch.prev.address)
}
if ch.prevAccountOriginExist {
s.accountsOrigin[ch.prev.addrHash] = ch.prevAccountOrigin
}
if ch.prevAccount != nil {
s.accounts[ch.prev.addrHash] = ch.prevAccount
}
if ch.prevStorage != nil {
s.storages[ch.prev.addrHash] = ch.prevStorage
}
}

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

func (ch selfDestructChange) revert(s *StateDB) {
Expand Down
7 changes: 7 additions & 0 deletions core/state/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ var (
storageTriesUpdatedMeter = metrics.NewRegisteredMeter("state/update/storagenodes", nil)
accountTrieDeletedMeter = metrics.NewRegisteredMeter("state/delete/accountnodes", nil)
storageTriesDeletedMeter = metrics.NewRegisteredMeter("state/delete/storagenodes", nil)

slotDeletionMaxCount = metrics.NewRegisteredGauge("state/delete/storage/max/slot", nil)
slotDeletionMaxSize = metrics.NewRegisteredGauge("state/delete/storage/max/size", nil)
slotDeletionTimer = metrics.NewRegisteredResettingTimer("state/delete/storage/timer", nil)
slotDeletionCount = metrics.NewRegisteredMeter("state/delete/storage/slot", nil)
slotDeletionSize = metrics.NewRegisteredMeter("state/delete/storage/size", nil)
slotDeletionSkip = metrics.NewRegisteredGauge("state/delete/storage/skip", nil)
)
2 changes: 1 addition & 1 deletion core/state/snapshot/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ func (dl *diskLayer) generateRange(trieID *trie.ID, prefix []byte, kind string,
}
root, nodes, _ := snapTrie.Commit(false)
if nodes != nil {
snapTrieDb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
snapTrieDb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes), nil)
}
snapTrieDb.Commit(root, false)
}
Expand Down
2 changes: 1 addition & 1 deletion core/state/snapshot/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func (t *testHelper) Commit() common.Hash {
if nodes != nil {
t.nodes.Merge(nodes)
}
t.triedb.Update(root, types.EmptyRootHash, t.nodes)
t.triedb.Update(root, types.EmptyRootHash, t.nodes, nil)
t.triedb.Commit(root, false)
return root
}
Expand Down
106 changes: 70 additions & 36 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,14 @@ func (s Storage) Copy() Storage {
//
// The usage pattern is as follows:
// First you need to obtain a state object.
// Account values can be accessed and modified through the object.
// Finally, call CommitTrie to write the modified storage trie into a database.
// Account values as well as storages can be accessed and modified through the object.
// Finally, call commit to return the changes of storage trie and update account data.
type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account
data types.StateAccount
address common.Address // address of the account
addrHash common.Hash // hash of ethereum address of the account
data types.StateAccount // Account data with all mutations applied in the scope of block
db *StateDB
origin *types.StateAccount // Account original data without any change applied, nil means it was not existent before

// DB error.
// State objects are used by the consensus core and VM which are
Expand All @@ -81,17 +82,22 @@ type stateObject struct {
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded

originStorage Storage // Storage cache of original entries to dedup rewrites, reset for every transaction
originStorage Storage // Storage cache of original entries to dedup rewrites
pendingStorage Storage // Storage entries that need to be flushed to disk, at the end of an entire block
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution
dirtyStorage Storage // Storage entries that have been modified in the current transaction execution, reset for every transaction
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.

// Cache flags.
// When an object is marked self-destructed it will be delete from the trie
// during the "update" phase of the state transition.
dirtyCode bool // true if the code was updated
dirtyCode bool // true if the code was updated

// Flag whether the account was marked as selfDestructed. The selfDestructed account
// is still accessible in the scope of same transaction.
selfDestructed bool
deleted bool

// Flag whether the account was marked as deleted. The selfDestructed account
// or the account is considered as empty will be marked as deleted at
// the end of transaction and no longer accessible anymore.
deleted bool

// Flag whether the object was created in the current transaction
created bool
Expand All @@ -103,21 +109,19 @@ func (s *stateObject) empty() bool {
}

// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
if data.Root == (common.Hash{}) {
data.Root = emptyRoot
func newObject(db *StateDB, address common.Address, acct *types.StateAccount) *stateObject {
// origin is supposed to not be changed directly but only be reassigned with the current state when committing
// so it's safe to use pointer here.
origin := acct
if acct == nil {
acct = types.NewEmptyStateAccount()
}
return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
origin: origin,
data: *acct,
originStorage: make(Storage),
pendingStorage: make(Storage),
dirtyStorage: make(Storage),
Expand Down Expand Up @@ -227,7 +231,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
// 1) resurrect happened, and new slot values were set -- those should
// have been handles via pendingStorage above.
// 2) we don't have new values, and can deliver empty response back
if _, destructed := s.db.snapDestructs[s.addrHash]; destructed {
if _, destructed := s.db.stateObjectsDestruct[s.address]; destructed {
return common.Hash{}
}
enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
Expand Down Expand Up @@ -334,7 +338,10 @@ func (s *stateObject) updateTrie(db Database) Trie {
defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now())
}
// The snapshot storage map for the object
var storage map[common.Hash][]byte
var (
storage map[common.Hash][]byte
origin map[common.Hash][]byte
)
// Insert all the pending updates into the trie
tr := s.getTrie(db)
hasher := s.db.hasher
Expand All @@ -345,6 +352,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
if value == s.originStorage[key] {
continue
}
prev := s.originStorage[key]
s.originStorage[key] = value

var v []byte
Expand All @@ -357,17 +365,35 @@ func (s *stateObject) updateTrie(db Database) Trie {
s.setError(tr.TryUpdate(key[:], v))
s.db.StorageUpdated += 1
}
// If state snapshotting is active, cache the data til commit
if s.db.snap != nil {
if storage == nil {
// Retrieve the old storage map, if available, create a new one otherwise
if storage = s.db.snapStorage[s.addrHash]; storage == nil {
storage = make(map[common.Hash][]byte)
s.db.snapStorage[s.addrHash] = storage
}
// Cache the mutated storage slots until commit
if storage == nil {
// Retrieve the old storage map, if available, create a new one otherwise
if storage = s.db.storages[s.addrHash]; storage == nil {
storage = make(map[common.Hash][]byte)
s.db.storages[s.addrHash] = storage
}
storage[crypto.HashData(hasher, key[:])] = v // v will be nil if it's deleted
}
khash := crypto.HashData(hasher, key[:])
storage[khash] = v // v will be nil if it's deleted
// Cache the original value of mutated storage slots
if origin == nil {
if origin = s.db.storagesOrigin[s.addrHash]; origin == nil {
origin = make(map[common.Hash][]byte)
s.db.storagesOrigin[s.addrHash] = origin
}
}
// Track the original value of slot only if it's mutated first time
if _, ok := origin[khash]; !ok {
if prev == (common.Hash{}) {
origin[khash] = nil // nil if it was not present previously
} else {
// Encoding []byte cannot fail, ok to ignore the error.
b, _ := rlp.EncodeToBytes(common.TrimLeftZeroes(prev[:]))
origin[khash] = b
}
}

// Cache the items for preloading
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
}
if s.db.prefetcher != nil {
Expand All @@ -392,11 +418,11 @@ func (s *stateObject) updateRoot(db Database) {
s.data.Root = s.trie.Hash()
}

// commitTrie submits the storage changes into the storage trie and re-computes
// the root. Besides, all trie changes will be collected in a nodeset and returned.
func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
// commit returns the changes made in storage trie and updates the account data.
func (s *stateObject) commit(db Database) (*trienode.NodeSet, error) {
// If nothing changed, don't bother with hashing anything
if s.updateTrie(db) == nil {
s.origin = s.data.Copy() // Update original account data after commit
return nil, nil
}
if s.dbErr != nil {
Expand All @@ -410,6 +436,8 @@ func (s *stateObject) commitTrie(db Database) (*trienode.NodeSet, error) {
if err == nil {
s.data.Root = root
}
// Update original account data after commit
s.origin = s.data.Copy()
return nodes, err
}

Expand Down Expand Up @@ -449,7 +477,13 @@ func (s *stateObject) setBalance(amount *big.Int) {
}

func (s *stateObject) deepCopy(db *StateDB) *stateObject {
stateObject := newObject(db, s.address, s.data)
stateObject := &stateObject{
db: db,
address: s.address,
addrHash: s.addrHash,
origin: s.origin,
data: s.data,
}
if s.trie != nil {
stateObject.trie = db.db.CopyTrie(s.trie)
}
Expand Down
14 changes: 7 additions & 7 deletions core/state/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ import (
"github.com/ethereum/go-ethereum/trie"
)

type stateTest struct {
type stateEnv struct {
db ethdb.Database
state *StateDB
}

func newStateTest() *stateTest {
func newStateEnv() *stateEnv {
db := rawdb.NewMemoryDatabase()
sdb, _ := New(common.Hash{}, NewDatabase(db), nil)
return &stateTest{db: db, state: sdb}
return &stateEnv{db: db, state: sdb}
}

func TestDump(t *testing.T) {
db := rawdb.NewMemoryDatabase()
sdb, _ := New(common.Hash{}, NewDatabaseWithConfig(db, &trie.Config{Preimages: true}), nil)
s := &stateTest{db: db, state: sdb}
s := &stateEnv{db: db, state: sdb}

// generate a few entries
obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestDump(t *testing.T) {
}

func TestNull(t *testing.T) {
s := newStateTest()
s := newStateEnv()
address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
Expand All @@ -114,7 +114,7 @@ func TestSnapshot(t *testing.T) {
var storageaddr common.Hash
data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43})
s := newStateTest()
s := newStateEnv()

// snapshot the genesis state
genesis := s.state.Snapshot()
Expand Down Expand Up @@ -145,7 +145,7 @@ func TestSnapshot(t *testing.T) {
}

func TestSnapshotEmpty(t *testing.T) {
s := newStateTest()
s := newStateEnv()
s.state.RevertToSnapshot(s.state.Snapshot())
}

Expand Down
Loading