core/state: address comments

This commit is contained in:
Gary Rong 2024-04-19 19:51:31 +08:00
parent 5327012be6
commit 6229ce66b9
4 changed files with 50 additions and 27 deletions

View file

@ -182,7 +182,7 @@ func (ch createObjectChange) copy() journalEntry {
}
func (ch createContractChange) revert(s *StateDB) {
s.stateObjects[ch.account].created = false
s.getStateObject(ch.account).newContract = false
}
func (ch createContractChange) dirtied() *common.Address {

View file

@ -66,8 +66,12 @@ type stateObject struct {
// account is still accessible in the scope of same transaction.
selfDestructed bool
// Flag whether the object was created in the current transaction
created bool
// This is an EIP-6780 flag indicating whether the object is eligible for
// self-destruct according to EIP-6780. The flag could be set either when
// the contract is just created within the current transaction, or when the
// object was previously existent and is being deployed as a contract within
// the current transaction.
newContract bool
}
// empty returns whether the account is considered empty.
@ -242,6 +246,10 @@ func (s *stateObject) finalise(prefetch bool) {
if len(s.dirtyStorage) > 0 {
s.dirtyStorage = make(Storage)
}
// Revoke the flag at the end of the transaction. It finalizes the status
// of the newly-created object as it's no longer eligible for self-destruct
// by EIP-6780. For non-newly-created objects, it's a no-op.
s.newContract = false
}
// updateTrie is responsible for persisting cached storage changes into the
@ -446,7 +454,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
obj.dirtyStorage = s.dirtyStorage.Copy()
obj.dirtyCode = s.dirtyCode
obj.selfDestructed = s.selfDestructed
obj.created = s.created
obj.newContract = s.newContract
return obj
}

View file

@ -56,6 +56,10 @@ type mutation struct {
applied bool
}
func (m *mutation) copy() *mutation {
return &mutation{typ: m.typ, applied: m.applied}
}
func (m *mutation) isDelete() bool {
return m.typ == deletion
}
@ -497,7 +501,7 @@ func (s *StateDB) Selfdestruct6780(addr common.Address) {
if stateObject == nil {
return
}
if stateObject.created {
if stateObject.newContract {
s.SelfDestruct(addr)
}
}
@ -663,14 +667,14 @@ func (s *StateDB) CreateAccount(addr common.Address) {
}
// CreateContract is used whenever a contract is created. This may be preceded
// by CreateAccount, but that is not required if it already existed
// in the state due to funds sent beforehand.
// This operation sets the 'created'-flag, which is required in order to
// by CreateAccount, but that is not required if it already existed in the
// state due to funds sent beforehand.
// This operation sets the 'newContract'-flag, which is required in order to
// correctly handle EIP-6780 'delete-in-same-transaction' logic.
func (s *StateDB) CreateContract(addr common.Address) {
obj := s.getStateObject(addr)
if !obj.created {
obj.created = true
if !obj.newContract {
obj.newContract = true
s.journal.append(createContractChange{account: addr})
}
}
@ -714,10 +718,9 @@ func (s *StateDB) Copy() *StateDB {
state.stateObjects[addr] = obj.deepCopy(state)
}
// Deep copy the object state markers.
for addr, m := range s.mutations {
state.mutations[addr] = &mutation{m.typ, m.applied}
for addr, op := range s.mutations {
state.mutations[addr] = op.copy()
}
// Deep copy the logs occurred in the scope of block
for hash, logs := range s.logs {
cpy := make([]*types.Log, len(logs))
@ -812,7 +815,7 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) {
delete(s.accountsOrigin, obj.address) // Clear out any previously updated account 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 {
obj.created = false
obj.newContract = false
obj.finalise(true) // Prefetch slots in the background
s.markUpdate(addr)
}
@ -849,6 +852,20 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
s.prefetcher = nil
}()
}
// 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, op := range s.mutations {
if op.applied {
continue
}
if op.isDelete() {
continue
}
s.stateObjects[addr].updateRoot()
}
// Now we're about to start to write changes to the trie. The trie is so far
// _untouched_. We can check with the prefetcher, if it can give us a trie
// which has the same root, but also has some content loaded into it.
@ -871,18 +888,16 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
usedAddrs [][]byte
deletedAddrs []common.Address
)
for addr, mutation := range s.mutations {
if mutation.applied {
for addr, op := range s.mutations {
if op.applied {
continue
}
mutation.applied = true
op.applied = true
if mutation.isDelete() {
if op.isDelete() {
deletedAddrs = append(deletedAddrs, addr)
} else {
obj := s.stateObjects[addr]
obj.updateRoot()
s.updateStateObject(obj)
s.updateStateObject(s.stateObjects[addr])
s.AccountUpdated += 1
}
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
@ -1137,8 +1152,8 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er
return common.Hash{}, err
}
// Handle all state updates afterwards
for addr, mutation := range s.mutations {
if mutation.isDelete() {
for addr, op := range s.mutations {
if op.isDelete() {
continue
}
obj := s.stateObjects[addr]

View file

@ -284,14 +284,14 @@ func TestCopyObjectState(t *testing.T) {
}
orig.Finalise(true)
cpy := orig.Copy()
for _, obj := range cpy.mutations {
if have, want := obj.applied, false; have != want {
for _, op := range cpy.mutations {
if have, want := op.applied, false; have != want {
t.Fatalf("Error in test itself, the 'done' flag should not be set before Commit, have %v want %v", have, want)
}
}
orig.Commit(0, true)
for _, obj := range cpy.mutations {
if have, want := obj.applied, false; have != want {
for _, op := range cpy.mutations {
if have, want := op.applied, false; have != want {
t.Fatalf("Error: original state affected copy, have %v want %v", have, want)
}
}