mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-09 22:44:27 +00:00
parent
0b7b02b8e0
commit
3926b08074
3 changed files with 74 additions and 33 deletions
|
|
@ -90,7 +90,8 @@ type (
|
||||||
account *common.Address
|
account *common.Address
|
||||||
}
|
}
|
||||||
resetObjectChange struct {
|
resetObjectChange struct {
|
||||||
prev *stateObject
|
prev *stateObject
|
||||||
|
prevdestruct bool
|
||||||
}
|
}
|
||||||
selfDestructChange struct {
|
selfDestructChange struct {
|
||||||
account *common.Address
|
account *common.Address
|
||||||
|
|
@ -155,6 +156,9 @@ func (ch createObjectChange) dirtied() *common.Address {
|
||||||
|
|
||||||
func (ch resetObjectChange) revert(s *StateDB) {
|
func (ch resetObjectChange) revert(s *StateDB) {
|
||||||
s.setStateObject(ch.prev)
|
s.setStateObject(ch.prev)
|
||||||
|
if !ch.prevdestruct {
|
||||||
|
delete(s.stateObjectsDestruct, ch.prev.address)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch resetObjectChange) dirtied() *common.Address {
|
func (ch resetObjectChange) dirtied() *common.Address {
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,15 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
|
||||||
if value, cached := s.originStorage[key]; cached {
|
if value, cached := s.originStorage[key]; cached {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
// If the object was destructed in *this* block (and potentially resurrected),
|
||||||
|
// the storage has been cleared out, and we should *not* consult the previous
|
||||||
|
// database about any storage values. The only possible alternatives are:
|
||||||
|
// 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.stateObjectsDestruct[s.address]; destructed {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
// Track the amount of time wasted on reading the storage trie
|
// Track the amount of time wasted on reading the storage trie
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
// Otherwise load the value from the database
|
// Otherwise load the value from the database
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,10 @@ type StateDB struct {
|
||||||
trie Trie
|
trie Trie
|
||||||
|
|
||||||
// This map holds 'live' objects, which will get modified while processing a state transition.
|
// This map holds 'live' objects, which will get modified while processing a state transition.
|
||||||
stateObjects map[common.Address]*stateObject
|
stateObjects map[common.Address]*stateObject
|
||||||
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
stateObjectsPending map[common.Address]struct{} // State objects finalized but not yet written to the trie
|
||||||
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
stateObjectsDirty map[common.Address]struct{} // State objects modified in the current execution
|
||||||
|
stateObjectsDestruct map[common.Address]struct{} // State objects destructed in the block
|
||||||
|
|
||||||
// DB error.
|
// DB error.
|
||||||
// State objects are used by the consensus core and VM which are
|
// State objects are used by the consensus core and VM which are
|
||||||
|
|
@ -115,16 +116,17 @@ func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &StateDB{
|
return &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
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{}),
|
||||||
logs: make(map[common.Hash][]*types.Log),
|
stateObjectsDestruct: make(map[common.Address]struct{}),
|
||||||
preimages: make(map[common.Hash][]byte),
|
logs: make(map[common.Hash][]*types.Log),
|
||||||
journal: newJournal(),
|
preimages: make(map[common.Hash][]byte),
|
||||||
accessList: newAccessList(),
|
journal: newJournal(),
|
||||||
transientStorage: newTransientStorage(),
|
accessList: newAccessList(),
|
||||||
|
transientStorage: newTransientStorage(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -569,11 +571,19 @@ func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
|
||||||
// the given address, it is overwritten and returned as the second return value.
|
// the given address, it is overwritten and returned as the second return value.
|
||||||
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) {
|
||||||
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
|
prev = s.getDeletedStateObject(addr) // Note, prev might have been deleted, we need that!
|
||||||
|
|
||||||
|
var prevdestruct bool
|
||||||
|
if prev != nil {
|
||||||
|
_, prevdestruct = s.stateObjectsDestruct[prev.address]
|
||||||
|
if !prevdestruct {
|
||||||
|
s.stateObjectsDestruct[prev.address] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
newobj = newObject(s, addr, types.StateAccount{})
|
newobj = newObject(s, addr, types.StateAccount{})
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
s.journal.append(createObjectChange{account: &addr})
|
s.journal.append(createObjectChange{account: &addr})
|
||||||
} else {
|
} else {
|
||||||
s.journal.append(resetObjectChange{prev: prev})
|
s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct})
|
||||||
}
|
}
|
||||||
|
|
||||||
newobj.created = true
|
newobj.created = true
|
||||||
|
|
@ -640,16 +650,17 @@ func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.
|
||||||
func (s *StateDB) Copy() *StateDB {
|
func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
trie: s.db.CopyTrie(s.trie),
|
trie: s.db.CopyTrie(s.trie),
|
||||||
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)),
|
||||||
refund: s.refund,
|
stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)),
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
refund: s.refund,
|
||||||
logSize: s.logSize,
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
preimages: maps.Clone(s.preimages),
|
logSize: s.logSize,
|
||||||
journal: newJournal(),
|
preimages: maps.Clone(s.preimages),
|
||||||
|
journal: newJournal(),
|
||||||
}
|
}
|
||||||
// Copy the dirty states, logs, and preimages
|
// Copy the dirty states, logs, and preimages
|
||||||
for addr := range s.journal.dirties {
|
for addr := range s.journal.dirties {
|
||||||
|
|
@ -683,7 +694,10 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
}
|
}
|
||||||
state.stateObjectsDirty[addr] = struct{}{}
|
state.stateObjectsDirty[addr] = struct{}{}
|
||||||
}
|
}
|
||||||
|
// Deep copy the destruction flag.
|
||||||
|
for addr := range s.stateObjectsDestruct {
|
||||||
|
state.stateObjectsDestruct[addr] = struct{}{}
|
||||||
|
}
|
||||||
// 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 {
|
||||||
cpy := make([]*types.Log, len(logs))
|
cpy := make([]*types.Log, len(logs))
|
||||||
|
|
@ -693,14 +707,16 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
}
|
}
|
||||||
state.logs[hash] = cpy
|
state.logs[hash] = cpy
|
||||||
}
|
}
|
||||||
|
for hash, preimage := range s.preimages {
|
||||||
// Do we need to copy the access list? In practice: No. At the start of a
|
state.preimages[hash] = preimage
|
||||||
// transaction, the access list is empty. In practice, we only ever copy state
|
}
|
||||||
// _between_ transactions/blocks, never in the middle of a transaction.
|
// Do we need to copy the access list and transient storage?
|
||||||
// However, it doesn't cost us much to copy an empty list, so we do it anyway
|
// In practice: No. At the start of a transaction, these two lists are empty.
|
||||||
// to not blow up if we ever decide copy it in the middle of a transaction
|
// In practice, we only ever copy state _between_ transactions/blocks, never
|
||||||
|
// in the middle of a transaction. However, it doesn't cost us much to copy
|
||||||
|
// empty lists, so we do it anyway to not blow up if we ever decide copy them
|
||||||
|
// in the middle of a transaction.
|
||||||
state.accessList = s.accessList.Copy()
|
state.accessList = s.accessList.Copy()
|
||||||
|
|
||||||
state.transientStorage = s.transientStorage.Copy()
|
state.transientStorage = s.transientStorage.Copy()
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
@ -747,6 +763,10 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
|
||||||
|
|
||||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
||||||
obj.deleted = true
|
obj.deleted = true
|
||||||
|
|
||||||
|
// We need to maintain account deletions explicitly (will remain
|
||||||
|
// set indefinitely).
|
||||||
|
s.stateObjectsDestruct[obj.address] = struct{}{}
|
||||||
} else {
|
} else {
|
||||||
obj.finalise()
|
obj.finalise()
|
||||||
}
|
}
|
||||||
|
|
@ -765,6 +785,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
// Finalise all the dirty storage states and write them into the tries
|
// Finalise all the dirty storage states and write them into the tries
|
||||||
s.Finalise(deleteEmptyObjects)
|
s.Finalise(deleteEmptyObjects)
|
||||||
|
|
||||||
|
// Although naively it makes sense to retrieve the account trie and then do
|
||||||
|
// the contract storage and account updates sequentially, that short circuits
|
||||||
|
// the account prefetcher. Instead, let's process all the storage updates
|
||||||
|
// first, giving the account prefetches just a few more milliseconds of time
|
||||||
|
// to pull useful data from disk.
|
||||||
for addr := range s.stateObjectsPending {
|
for addr := range s.stateObjectsPending {
|
||||||
obj := s.stateObjects[addr]
|
obj := s.stateObjects[addr]
|
||||||
if obj.deleted {
|
if obj.deleted {
|
||||||
|
|
@ -867,6 +892,9 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
|
||||||
s.AccountUpdated, s.AccountDeleted = 0, 0
|
s.AccountUpdated, s.AccountDeleted = 0, 0
|
||||||
s.StorageUpdated, s.StorageDeleted = 0, 0
|
s.StorageUpdated, s.StorageDeleted = 0, 0
|
||||||
|
|
||||||
|
if len(s.stateObjectsDestruct) > 0 {
|
||||||
|
s.stateObjectsDestruct = make(map[common.Address]struct{})
|
||||||
|
}
|
||||||
return root, err
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue