core: track system contract executions separately

Extend the unique code execution tracking to distinguish between system
contract calls (EIP-4788 beacon root, EIP-2935 history storage, EIP-7002
withdrawal queue, EIP-7251 consolidation queue) and user contract calls.

The MarkCodeExecuted method now takes an isSystem parameter to track
system contracts separately. This is useful for understanding block
execution statistics, as system contracts are called automatically by
the protocol at the start of each block.

The slow block log now shows "Contracts executed: N (M system)" to
make it clear how many of the executed contracts were system calls.
This commit is contained in:
CPerezz 2026-01-04 00:36:38 +01:00
parent be2d43090c
commit b9a50aeadb
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
6 changed files with 26 additions and 11 deletions

View file

@ -2192,6 +2192,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
stats.UniqueAccountsAccessed = statedb.UniqueAccountsAccessed() stats.UniqueAccountsAccessed = statedb.UniqueAccountsAccessed()
stats.UniqueStorageAccessed = statedb.UniqueStorageAccessed() stats.UniqueStorageAccessed = statedb.UniqueStorageAccessed()
stats.UniqueCodeExecuted = statedb.UniqueCodeExecuted() stats.UniqueCodeExecuted = statedb.UniqueCodeExecuted()
stats.SystemCodeExecuted = statedb.SystemCodeExecuted()
stats.Execution = ptime - (statedb.AccountReads + statedb.StorageReads + statedb.CodeReads) // The time spent on EVM processing stats.Execution = ptime - (statedb.AccountReads + statedb.StorageReads + statedb.CodeReads) // The time spent on EVM processing
stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation

View file

@ -52,6 +52,7 @@ type ExecuteStats struct {
UniqueAccountsAccessed int // Number of unique accounts accessed UniqueAccountsAccessed int // Number of unique accounts accessed
UniqueStorageAccessed int // Number of unique storage slots accessed UniqueStorageAccessed int // Number of unique storage slots accessed
UniqueCodeExecuted int // Number of unique contracts executed UniqueCodeExecuted int // Number of unique contracts executed
SystemCodeExecuted int // Number of unique system contracts executed (subset of UniqueCodeExecuted)
Execution time.Duration // Time spent on the EVM execution Execution time.Duration // Time spent on the EVM execution
Validation time.Duration // Time spent on the block validation Validation time.Duration // Time spent on the block validation
@ -132,7 +133,7 @@ State read: %v
Unique state access: Unique state access:
Accounts: %d Accounts: %d
Storage slots: %d Storage slots: %d
Contracts executed: %d Contracts executed: %d (%d system)
State hash: %v State hash: %v
Account hash: %v Account hash: %v
@ -158,7 +159,7 @@ DB write: %v
// Unique state access // Unique state access
s.UniqueAccountsAccessed, s.UniqueAccountsAccessed,
s.UniqueStorageAccessed, s.UniqueStorageAccessed,
s.UniqueCodeExecuted, s.UniqueCodeExecuted, s.SystemCodeExecuted,
// State hash // State hash
common.PrettyDuration(s.AccountHashes+s.AccountUpdates+s.StorageUpdates+max(s.AccountCommits, s.StorageCommits)), common.PrettyDuration(s.AccountHashes+s.AccountUpdates+s.StorageUpdates+max(s.AccountCommits, s.StorageCommits)),

View file

@ -163,7 +163,8 @@ type StateDB struct {
CodeBytesRead int64 // Total bytes of contract code read during the state transition CodeBytesRead int64 // Total bytes of contract code read during the state transition
// Unique contracts executed tracking (set of code hashes executed during the state transition) // Unique contracts executed tracking (set of code hashes executed during the state transition)
executedCodes map[common.Hash]struct{} executedCodes map[common.Hash]struct{}
systemExecutedCodes map[common.Hash]struct{} // Subset of executedCodes that are system calls
} }
// New creates a new state from a given trie. // New creates a new state from a given trie.
@ -191,6 +192,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro
accessList: newAccessList(), accessList: newAccessList(),
transientStorage: newTransientStorage(), transientStorage: newTransientStorage(),
executedCodes: make(map[common.Hash]struct{}), executedCodes: make(map[common.Hash]struct{}),
systemExecutedCodes: make(map[common.Hash]struct{}),
} }
if db.TrieDB().IsVerkle() { if db.TrieDB().IsVerkle() {
sdb.accessEvents = NewAccessEvents(db.PointCache()) sdb.accessEvents = NewAccessEvents(db.PointCache())
@ -386,7 +388,8 @@ func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
// MarkCodeExecuted records that a contract's code was executed. // MarkCodeExecuted records that a contract's code was executed.
// This is used for metrics tracking to count unique contracts executed. // This is used for metrics tracking to count unique contracts executed.
func (s *StateDB) MarkCodeExecuted(codeHash common.Hash) { // The isSystem parameter indicates if this is a system call (e.g., EIP-4788 beacon root).
func (s *StateDB) MarkCodeExecuted(codeHash common.Hash, isSystem bool) {
if codeHash == types.EmptyCodeHash || codeHash == (common.Hash{}) { if codeHash == types.EmptyCodeHash || codeHash == (common.Hash{}) {
return return
} }
@ -394,6 +397,9 @@ func (s *StateDB) MarkCodeExecuted(codeHash common.Hash) {
return // Skip tracking for state copies (e.g., prefetcher) return // Skip tracking for state copies (e.g., prefetcher)
} }
s.executedCodes[codeHash] = struct{}{} s.executedCodes[codeHash] = struct{}{}
if isSystem {
s.systemExecutedCodes[codeHash] = struct{}{}
}
} }
// UniqueCodeExecuted returns the number of unique contract codes executed. // UniqueCodeExecuted returns the number of unique contract codes executed.
@ -401,6 +407,12 @@ func (s *StateDB) UniqueCodeExecuted() int {
return len(s.executedCodes) return len(s.executedCodes)
} }
// SystemCodeExecuted returns the number of unique system contract codes executed
// (e.g., EIP-4788 beacon root, EIP-2935 history storage, etc.).
func (s *StateDB) SystemCodeExecuted() int {
return len(s.systemExecutedCodes)
}
// UniqueAccountsAccessed returns the number of unique accounts accessed during the state transition. // UniqueAccountsAccessed returns the number of unique accounts accessed during the state transition.
func (s *StateDB) UniqueAccountsAccessed() int { func (s *StateDB) UniqueAccountsAccessed() int {
return len(s.stateObjects) return len(s.stateObjects)

View file

@ -298,6 +298,6 @@ func (s *hookedStateDB) Inner() *StateDB {
} }
// MarkCodeExecuted records that a contract's code was executed. // MarkCodeExecuted records that a contract's code was executed.
func (s *hookedStateDB) MarkCodeExecuted(codeHash common.Hash) { func (s *hookedStateDB) MarkCodeExecuted(codeHash common.Hash, isSystem bool) {
s.inner.MarkCodeExecuted(codeHash) s.inner.MarkCodeExecuted(codeHash, isSystem)
} }

View file

@ -296,7 +296,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
codeHash := evm.resolveCodeHash(addr) codeHash := evm.resolveCodeHash(addr)
contract.SetCallCode(codeHash, code) contract.SetCallCode(codeHash, code)
// Track unique contract execution for metrics // Track unique contract execution for metrics
evm.StateDB.MarkCodeExecuted(codeHash) evm.StateDB.MarkCodeExecuted(codeHash, contract.IsSystemCall)
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -358,7 +358,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
codeHash := evm.resolveCodeHash(addr) codeHash := evm.resolveCodeHash(addr)
contract.SetCallCode(codeHash, evm.resolveCode(addr)) contract.SetCallCode(codeHash, evm.resolveCode(addr))
// Track unique contract execution for metrics // Track unique contract execution for metrics
evm.StateDB.MarkCodeExecuted(codeHash) evm.StateDB.MarkCodeExecuted(codeHash, isSystemCall(caller))
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -405,7 +405,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
codeHash := evm.resolveCodeHash(addr) codeHash := evm.resolveCodeHash(addr)
contract.SetCallCode(codeHash, evm.resolveCode(addr)) contract.SetCallCode(codeHash, evm.resolveCode(addr))
// Track unique contract execution for metrics // Track unique contract execution for metrics
evm.StateDB.MarkCodeExecuted(codeHash) evm.StateDB.MarkCodeExecuted(codeHash, isSystemCall(originCaller))
ret, err = evm.Run(contract, input, false) ret, err = evm.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -459,7 +459,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
codeHash := evm.resolveCodeHash(addr) codeHash := evm.resolveCodeHash(addr)
contract.SetCallCode(codeHash, evm.resolveCode(addr)) contract.SetCallCode(codeHash, evm.resolveCode(addr))
// Track unique contract execution for metrics // Track unique contract execution for metrics
evm.StateDB.MarkCodeExecuted(codeHash) evm.StateDB.MarkCodeExecuted(codeHash, isSystemCall(caller))
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally

View file

@ -104,5 +104,6 @@ type StateDB interface {
// MarkCodeExecuted records that a contract's code was executed. // MarkCodeExecuted records that a contract's code was executed.
// Used for metrics tracking to count unique contracts executed. // Used for metrics tracking to count unique contracts executed.
MarkCodeExecuted(codeHash common.Hash) // The isSystem parameter indicates if this is a system call (e.g., EIP-4788).
MarkCodeExecuted(codeHash common.Hash, isSystem bool)
} }