add codes origin

This commit is contained in:
weiihann 2025-12-31 13:39:10 +08:00
parent b9d45acf90
commit d11a86f275
4 changed files with 56 additions and 20 deletions

View file

@ -423,7 +423,7 @@ func (s *stateObject) commitStorage(op *accountUpdate) {
//
// Note, commit may run concurrently across all the state objects. Do not assume
// thread-safe access to the statedb.
func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
func (s *stateObject) commit(resolveCode bool) (*accountUpdate, *trienode.NodeSet, error) {
// commit the account metadata changes
op := &accountUpdate{
address: s.address,
@ -439,6 +439,20 @@ func (s *stateObject) commit() (*accountUpdate, *trienode.NodeSet, error) {
blob: s.code,
}
s.dirtyCode = false // reset the dirty flag
// retrieve the original code if requested and the code was not empty
if resolveCode && s.origin != nil && !bytes.Equal(s.origin.CodeHash, types.EmptyCodeHash.Bytes()) {
code, err := s.db.reader.Code(s.address, common.BytesToHash(s.origin.CodeHash))
if err != nil {
return nil, nil, err
}
op.codeOrigin = &contractCode{
hash: common.BytesToHash(s.origin.CodeHash),
blob: code,
exists: true, // the code exists before the update
}
}
}
// Commit storage changes and the associated storage trie
s.commitStorage(op)

View file

@ -1162,7 +1162,7 @@ func (s *StateDB) GetTrie() Trie {
// commit gathers the state mutations accumulated along with the associated
// trie changes, resetting all internal flags with the new state as the base.
func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNumber uint64) (*stateUpdate, error) {
func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNumber uint64, resolveCode bool) (*stateUpdate, error) {
// Short circuit in case any database failure occurred earlier.
if s.dbErr != nil {
return nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr)
@ -1272,7 +1272,7 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
// Run the storage updates concurrently to one another
workers.Go(func() error {
// Write any storage changes in the state object to its storage trie
update, set, err := obj.commit()
update, set, err := obj.commit(resolveCode)
if err != nil {
return err
}
@ -1319,13 +1319,13 @@ func (s *StateDB) commit(deleteEmptyObjects bool, noStorageWiping bool, blockNum
// commitAndFlush is a wrapper of commit which also commits the state mutations
// to the configured data stores.
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool, dedupCode bool) (*stateUpdate, error) {
ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block)
func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool, noStorageWiping bool, resolveCode bool) (*stateUpdate, error) {
ret, err := s.commit(deleteEmptyObjects, noStorageWiping, block, resolveCode)
if err != nil {
return nil, err
}
if dedupCode {
if resolveCode {
ret.markCodeExistence(s.reader)
}

View file

@ -52,11 +52,12 @@ type accountDelete struct {
// accountUpdate represents an operation for updating an Ethereum account.
type accountUpdate struct {
address common.Address // address is the unique account identifier
data []byte // data is the slim-RLP encoded account data.
origin []byte // origin is the original value of account data in slim-RLP encoding.
code *contractCode // code represents mutated contract code; nil means it's not modified.
storages map[common.Hash][]byte // storages stores mutated slots in prefix-zero-trimmed RLP format.
address common.Address // address is the unique account identifier
data []byte // data is the slim-RLP encoded account data.
origin []byte // origin is the original value of account data in slim-RLP encoding.
code *contractCode // code represents mutated contract code; nil means it's not modified.
codeOrigin *contractCode // codeOrigin is the original value of contract code; nil means it does not exist before the update.
storages map[common.Hash][]byte // storages stores mutated slots in prefix-zero-trimmed RLP format.
// storagesOriginByKey and storagesOriginByHash both store the original values
// of mutated slots in prefix-zero-trimmed RLP format. The difference is that
@ -88,8 +89,9 @@ type stateUpdate struct {
storagesOrigin map[common.Address]map[common.Hash][]byte
rawStorageKey bool
codes map[common.Address]*contractCode // codes contains the set of dirty codes
nodes *trienode.MergedNodeSet // Aggregated dirty nodes caused by state changes
codes map[common.Address]*contractCode // codes contains the set of dirty codes
codesOrigin map[common.Address]*contractCode // codesOrigin contains the original code data of the mutated accounts
nodes *trienode.MergedNodeSet // Aggregated dirty nodes caused by state changes
}
// empty returns a flag indicating the state transition is empty or not.
@ -110,6 +112,7 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
storages = make(map[common.Hash]map[common.Hash][]byte)
storagesOrigin = make(map[common.Address]map[common.Hash][]byte)
codes = make(map[common.Address]*contractCode)
codesOrigin = make(map[common.Address]*contractCode)
)
// Since some accounts might be destroyed and recreated within the same
// block, deletions must be aggregated first.
@ -133,6 +136,9 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
if op.code != nil {
codes[addr] = op.code
}
if op.codeOrigin != nil {
codesOrigin[addr] = op.codeOrigin
}
accounts[addrHash] = op.data
// Aggregate the account original value. If the account is already
@ -179,6 +185,7 @@ func newStateUpdate(rawStorageKey bool, originRoot common.Hash, root common.Hash
storagesOrigin: storagesOrigin,
rawStorageKey: rawStorageKey,
codes: codes,
codesOrigin: codesOrigin,
nodes: nodes,
}
}
@ -311,11 +318,21 @@ func (sc *stateUpdate) ToTracingUpdate() (*tracing.StateUpdate, error) {
}
for addr, code := range sc.codes {
update.CodeChanges[addr] = &tracing.CodeChange{
Hash: code.hash,
Code: code.blob,
Exists: code.exists,
change := &tracing.CodeChange{
New: &tracing.ContractCode{
Hash: code.hash,
Code: code.blob,
Exists: code.exists,
},
}
if origin, ok := sc.codesOrigin[addr]; ok {
change.Prev = &tracing.ContractCode{
Hash: origin.hash,
Code: origin.blob,
Exists: origin.exists,
}
}
update.CodeChanges[addr] = change
}
if sc.nodes != nil {

View file

@ -109,11 +109,16 @@ type StorageChange struct {
New common.Hash // new value (zero if slot was deleted)
}
// CodeChange represents a contract code deployment or change.
type CodeChange struct {
type ContractCode struct {
Hash common.Hash
Code []byte
Exists bool // true if the code already deployed before
Exists bool // true if the code was existent
}
// CodeChange represents a change in contract code of an account.
type CodeChange struct {
Prev *ContractCode // nil if no code existed before
New *ContractCode
}
type TrieNodeChange struct {