core, trie: track state change set with account address (#27815)

This commit is contained in:
rjl493456442 2023-07-31 20:07:51 +08:00 committed by GitHub
parent 43a1a48ee2
commit 817553cc28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 57 deletions

View File

@ -173,10 +173,10 @@ func (ch resetObjectChange) revert(s *StateDB) {
s.storages[ch.prev.addrHash] = ch.prevStorage s.storages[ch.prev.addrHash] = ch.prevStorage
} }
if ch.prevAccountOriginExist { if ch.prevAccountOriginExist {
s.accountsOrigin[ch.prev.addrHash] = ch.prevAccountOrigin s.accountsOrigin[ch.prev.address] = ch.prevAccountOrigin
} }
if ch.prevStorageOrigin != nil { if ch.prevStorageOrigin != nil {
s.storagesOrigin[ch.prev.addrHash] = ch.prevStorageOrigin s.storagesOrigin[ch.prev.address] = ch.prevStorageOrigin
} }
} }

View File

@ -328,9 +328,9 @@ func (s *stateObject) updateTrie() (Trie, error) {
// Cache the original value of mutated storage slots // Cache the original value of mutated storage slots
if origin == nil { if origin == nil {
if origin = s.db.storagesOrigin[s.addrHash]; origin == nil { if origin = s.db.storagesOrigin[s.address]; origin == nil {
origin = make(map[common.Hash][]byte) origin = make(map[common.Hash][]byte)
s.db.storagesOrigin[s.addrHash] = origin s.db.storagesOrigin[s.address] = origin
} }
} }
// Track the original value of slot only if it's mutated first time // Track the original value of slot only if it's mutated first time

View File

@ -81,8 +81,8 @@ type StateDB struct {
// original value) that occurred in this **block**. // original value) that occurred in this **block**.
accounts map[common.Hash][]byte // The mutated accounts in 'slim RLP' encoding accounts map[common.Hash][]byte // The mutated accounts in 'slim RLP' encoding
storages map[common.Hash]map[common.Hash][]byte // The mutated slots in prefix-zero trimmed rlp format storages map[common.Hash]map[common.Hash][]byte // The mutated slots in prefix-zero trimmed rlp format
accountsOrigin map[common.Hash][]byte // The original value of mutated accounts in 'slim RLP' encoding accountsOrigin map[common.Address][]byte // The original value of mutated accounts in 'slim RLP' encoding
storagesOrigin map[common.Hash]map[common.Hash][]byte // The original value of mutated slots in prefix-zero trimmed rlp format storagesOrigin map[common.Address]map[common.Hash][]byte // The original value of mutated slots in prefix-zero trimmed rlp format
// This map holds 'live' objects, which will get modified while processing // This map holds 'live' objects, which will get modified while processing
// a state transition. // a state transition.
@ -157,8 +157,8 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
snaps: snaps, snaps: snaps,
accounts: make(map[common.Hash][]byte), accounts: make(map[common.Hash][]byte),
storages: make(map[common.Hash]map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte),
accountsOrigin: make(map[common.Hash][]byte), accountsOrigin: make(map[common.Address][]byte),
storagesOrigin: make(map[common.Hash]map[common.Hash][]byte), storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
stateObjects: make(map[common.Address]*stateObject), stateObjects: make(map[common.Address]*stateObject),
stateObjectsPending: make(map[common.Address]struct{}), stateObjectsPending: make(map[common.Address]struct{}),
stateObjectsDirty: make(map[common.Address]struct{}), stateObjectsDirty: make(map[common.Address]struct{}),
@ -555,11 +555,11 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
// Track the original value of mutated account, nil means it was not present. // Track the original value of mutated account, nil means it was not present.
// Skip if it has been tracked (because updateStateObject may be called // Skip if it has been tracked (because updateStateObject may be called
// multiple times in a block). // multiple times in a block).
if _, ok := s.accountsOrigin[obj.addrHash]; !ok { if _, ok := s.accountsOrigin[obj.address]; !ok {
if obj.origin == nil { if obj.origin == nil {
s.accountsOrigin[obj.addrHash] = nil s.accountsOrigin[obj.address] = nil
} else { } else {
s.accountsOrigin[obj.addrHash] = types.SlimAccountRLP(*obj.origin) s.accountsOrigin[obj.address] = types.SlimAccountRLP(*obj.origin)
} }
} }
} }
@ -676,7 +676,7 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
// There may be some cached account/storage data already since IntermediateRoot // There may be some cached account/storage data already since IntermediateRoot
// will be called for each transaction before byzantium fork which will always // will be called for each transaction before byzantium fork which will always
// cache the latest account/storage data. // cache the latest account/storage data.
prevAccount, ok := s.accountsOrigin[prev.addrHash] prevAccount, ok := s.accountsOrigin[prev.address]
s.journal.append(resetObjectChange{ s.journal.append(resetObjectChange{
account: &addr, account: &addr,
prev: prev, prev: prev,
@ -685,12 +685,12 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
prevStorage: s.storages[prev.addrHash], prevStorage: s.storages[prev.addrHash],
prevAccountOriginExist: ok, prevAccountOriginExist: ok,
prevAccountOrigin: prevAccount, prevAccountOrigin: prevAccount,
prevStorageOrigin: s.storagesOrigin[prev.addrHash], prevStorageOrigin: s.storagesOrigin[prev.address],
}) })
delete(s.accounts, prev.addrHash) delete(s.accounts, prev.addrHash)
delete(s.storages, prev.addrHash) delete(s.storages, prev.addrHash)
delete(s.accountsOrigin, prev.addrHash) delete(s.accountsOrigin, prev.address)
delete(s.storagesOrigin, prev.addrHash) delete(s.storagesOrigin, prev.address)
} }
newobj.created = true newobj.created = true
@ -766,8 +766,8 @@ func (s *StateDB) Copy() *StateDB {
originalRoot: s.originalRoot, originalRoot: s.originalRoot,
accounts: make(map[common.Hash][]byte), accounts: make(map[common.Hash][]byte),
storages: make(map[common.Hash]map[common.Hash][]byte), storages: make(map[common.Hash]map[common.Hash][]byte),
accountsOrigin: make(map[common.Hash][]byte), accountsOrigin: make(map[common.Address][]byte),
storagesOrigin: make(map[common.Hash]map[common.Hash][]byte), storagesOrigin: make(map[common.Address]map[common.Hash][]byte),
stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)), stateObjects: make(map[common.Address]*stateObject, len(s.journal.dirties)),
stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)),
stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)), stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)),
@ -824,10 +824,10 @@ func (s *StateDB) Copy() *StateDB {
} }
// Deep copy the state changes made in the scope of block // Deep copy the state changes made in the scope of block
// along with their original values. // along with their original values.
state.accounts = copyAccounts(s.accounts) state.accounts = copySet(s.accounts)
state.storages = copyStorages(s.storages) state.storages = copy2DSet(s.storages)
state.accountsOrigin = copyAccounts(state.accountsOrigin) state.accountsOrigin = copySet(state.accountsOrigin)
state.storagesOrigin = copyStorages(state.storagesOrigin) state.storagesOrigin = copy2DSet(state.storagesOrigin)
// Deep copy the logs occurred in the scope of block // Deep copy the logs occurred in the scope of block
for hash, logs := range s.logs { for hash, logs := range s.logs {
@ -919,8 +919,8 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
// resurrect an account; but the snapshotter needs both events. // resurrect an account; but the snapshotter needs both events.
delete(s.accounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect) delete(s.accounts, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect)
delete(s.storages, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect) delete(s.storages, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect)
delete(s.accountsOrigin, obj.addrHash) // Clear out any previously updated account data (may be recreated via a resurrect) delete(s.accountsOrigin, obj.address) // Clear out any previously updated account data (may be recreated via a resurrect)
delete(s.storagesOrigin, obj.addrHash) // Clear out any previously updated storage data (may be recreated via a resurrect) delete(s.storagesOrigin, obj.address) // Clear out any previously updated storage data (may be recreated via a resurrect)
} else { } else {
obj.finalise(true) // Prefetch slots in the background obj.finalise(true) // Prefetch slots in the background
} }
@ -1098,8 +1098,8 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
// //
// In case (d), **original** account along with its storages should be deleted, // In case (d), **original** account along with its storages should be deleted,
// with their values be tracked as original value. // with their values be tracked as original value.
func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.Hash]struct{}, error) { func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.Address]struct{}, error) {
incomplete := make(map[common.Hash]struct{}) incomplete := make(map[common.Address]struct{})
for addr, prev := range s.stateObjectsDestruct { for addr, prev := range s.stateObjectsDestruct {
// The original account was non-existing, and it's marked as destructed // The original account was non-existing, and it's marked as destructed
// in the scope of block. It can be case (a) or (b). // in the scope of block. It can be case (a) or (b).
@ -1109,12 +1109,12 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.H
addrHash := crypto.Keccak256Hash(addr[:]) addrHash := crypto.Keccak256Hash(addr[:])
if prev == nil { if prev == nil {
if _, ok := s.accounts[addrHash]; ok { if _, ok := s.accounts[addrHash]; ok {
s.accountsOrigin[addrHash] = nil // case (b) s.accountsOrigin[addr] = nil // case (b)
} }
continue continue
} }
// It can overwrite the data in s.accountsOrigin set by 'updateStateObject'. // It can overwrite the data in s.accountsOrigin set by 'updateStateObject'.
s.accountsOrigin[addrHash] = types.SlimAccountRLP(*prev) // case (c) or (d) s.accountsOrigin[addr] = types.SlimAccountRLP(*prev) // case (c) or (d)
// Short circuit if the storage was empty. // Short circuit if the storage was empty.
if prev.Root == types.EmptyRootHash { if prev.Root == types.EmptyRootHash {
@ -1130,17 +1130,17 @@ func (s *StateDB) handleDestruction(nodes *trienode.MergedNodeSet) (map[common.H
// created. In this case, wipe the entire storage state diff because // created. In this case, wipe the entire storage state diff because
// of aborted deletion. // of aborted deletion.
if aborted { if aborted {
incomplete[addrHash] = struct{}{} incomplete[addr] = struct{}{}
delete(s.storagesOrigin, addrHash) delete(s.storagesOrigin, addr)
continue continue
} }
if s.storagesOrigin[addrHash] == nil { if s.storagesOrigin[addr] == nil {
s.storagesOrigin[addrHash] = slots s.storagesOrigin[addr] = slots
} else { } else {
// It can overwrite the data in s.storagesOrigin[addrHash] set by // It can overwrite the data in s.storagesOrigin[addrHash] set by
// 'object.updateTrie'. // 'object.updateTrie'.
for key, val := range slots { for key, val := range slots {
s.storagesOrigin[addrHash][key] = val s.storagesOrigin[addr][key] = val
} }
} }
if err := nodes.Merge(set); err != nil { if err := nodes.Merge(set); err != nil {
@ -1290,8 +1290,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
// Clear all internal flags at the end of commit operation. // Clear all internal flags at the end of commit operation.
s.accounts = make(map[common.Hash][]byte) s.accounts = make(map[common.Hash][]byte)
s.storages = make(map[common.Hash]map[common.Hash][]byte) s.storages = make(map[common.Hash]map[common.Hash][]byte)
s.accountsOrigin = make(map[common.Hash][]byte) s.accountsOrigin = make(map[common.Address][]byte)
s.storagesOrigin = make(map[common.Hash]map[common.Hash][]byte) s.storagesOrigin = make(map[common.Address]map[common.Hash][]byte)
s.stateObjectsDirty = make(map[common.Address]struct{}) s.stateObjectsDirty = make(map[common.Address]struct{})
s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount) s.stateObjectsDestruct = make(map[common.Address]*types.StateAccount)
return root, nil return root, nil
@ -1387,18 +1387,18 @@ func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount)
return ret return ret
} }
// copyAccounts returns a deep-copied account set of the provided one. // copySet returns a deep-copied set.
func copyAccounts(set map[common.Hash][]byte) map[common.Hash][]byte { func copySet[k comparable](set map[k][]byte) map[k][]byte {
copied := make(map[common.Hash][]byte, len(set)) copied := make(map[k][]byte, len(set))
for key, val := range set { for key, val := range set {
copied[key] = common.CopyBytes(val) copied[key] = common.CopyBytes(val)
} }
return copied return copied
} }
// copyStorages returns a deep-copied storage set of the provided one. // copy2DSet returns a two-dimensional deep-copied set.
func copyStorages(set map[common.Hash]map[common.Hash][]byte) map[common.Hash]map[common.Hash][]byte { func copy2DSet[k comparable](set map[k]map[common.Hash][]byte) map[k]map[common.Hash][]byte {
copied := make(map[common.Hash]map[common.Hash][]byte, len(set)) copied := make(map[k]map[common.Hash][]byte, len(set))
for addr, subset := range set { for addr, subset := range set {
copied[addr] = make(map[common.Hash][]byte, len(subset)) copied[addr] = make(map[common.Hash][]byte, len(subset))
for key, val := range subset { for key, val := range subset {

View File

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/trie/triestate"
@ -171,11 +172,11 @@ func (test *stateTest) String() string {
func (test *stateTest) run() bool { func (test *stateTest) run() bool {
var ( var (
roots []common.Hash roots []common.Hash
accountList []map[common.Hash][]byte accountList []map[common.Address][]byte
storageList []map[common.Hash]map[common.Hash][]byte storageList []map[common.Address]map[common.Hash][]byte
onCommit = func(states *triestate.Set) { onCommit = func(states *triestate.Set) {
accountList = append(accountList, copyAccounts(states.Accounts)) accountList = append(accountList, copySet(states.Accounts))
storageList = append(storageList, copyStorages(states.Storages)) storageList = append(storageList, copy2DSet(states.Storages))
} }
disk = rawdb.NewMemoryDatabase() disk = rawdb.NewMemoryDatabase()
tdb = trie.NewDatabaseWithConfig(disk, &trie.Config{OnCommit: onCommit}) tdb = trie.NewDatabaseWithConfig(disk, &trie.Config{OnCommit: onCommit})
@ -235,8 +236,9 @@ func (test *stateTest) run() bool {
// - the account was indeed not present in trie // - the account was indeed not present in trie
// - the account is present in new trie, nil->nil is regarded as invalid // - the account is present in new trie, nil->nil is regarded as invalid
// - the slots transition is correct // - the slots transition is correct
func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addrHash common.Hash, slots map[common.Hash][]byte) error { func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error {
// Verify account change // Verify account change
addrHash := crypto.Keccak256Hash(addr.Bytes())
oBlob, err := otr.Get(addrHash.Bytes()) oBlob, err := otr.Get(addrHash.Bytes())
if err != nil { if err != nil {
return err return err
@ -285,8 +287,9 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database
// - the account was indeed present in trie // - the account was indeed present in trie
// - the account in old trie matches the provided value // - the account in old trie matches the provided value
// - the slots transition is correct // - the slots transition is correct
func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addrHash common.Hash, origin []byte, slots map[common.Hash][]byte) error { func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error {
// Verify account change // Verify account change
addrHash := crypto.Keccak256Hash(addr.Bytes())
oBlob, err := otr.Get(addrHash.Bytes()) oBlob, err := otr.Get(addrHash.Bytes())
if err != nil { if err != nil {
return err return err
@ -338,7 +341,7 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database,
return nil return nil
} }
func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Hash][]byte, storagesOrigin map[common.Hash]map[common.Hash][]byte) error { func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
otr, err := trie.New(trie.StateTrieID(root), db) otr, err := trie.New(trie.StateTrieID(root), db)
if err != nil { if err != nil {
return err return err
@ -347,12 +350,12 @@ func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Datab
if err != nil { if err != nil {
return err return err
} }
for addrHash, account := range accountsOrigin { for addr, account := range accountsOrigin {
var err error var err error
if len(account) == 0 { if len(account) == 0 {
err = test.verifyAccountCreation(next, db, otr, ntr, addrHash, storagesOrigin[addrHash]) err = test.verifyAccountCreation(next, db, otr, ntr, addr, storagesOrigin[addr])
} else { } else {
err = test.verifyAccountUpdate(next, db, otr, ntr, addrHash, accountsOrigin[addrHash], storagesOrigin[addrHash]) err = test.verifyAccountUpdate(next, db, otr, ntr, addr, accountsOrigin[addr], storagesOrigin[addr])
} }
if err != nil { if err != nil {
return err return err

View File

@ -22,7 +22,7 @@ import "github.com/ethereum/go-ethereum/common"
// The value refers to the original content of state before the transition // The value refers to the original content of state before the transition
// is made. Nil means that the state was not present previously. // is made. Nil means that the state was not present previously.
type Set struct { type Set struct {
Accounts map[common.Hash][]byte // Mutated account set, nil means the account was not present Accounts map[common.Address][]byte // Mutated account set, nil means the account was not present
Storages map[common.Hash]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present Storages map[common.Address]map[common.Hash][]byte // Mutated storage set, nil means the slot was not present
Incomplete map[common.Hash]struct{} // Indicator whether the storage slot is incomplete due to large deletion Incomplete map[common.Address]struct{} // Indicator whether the storage slot is incomplete due to large deletion
} }