From b9a50aeadb72f93067369fa1906e0da8568207c2 Mon Sep 17 00:00:00 2001 From: CPerezz Date: Sun, 4 Jan 2026 00:36:38 +0100 Subject: [PATCH] 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. --- core/blockchain.go | 1 + core/blockchain_stats.go | 5 +++-- core/state/statedb.go | 16 ++++++++++++++-- core/state/statedb_hooked.go | 4 ++-- core/vm/evm.go | 8 ++++---- core/vm/interface.go | 3 ++- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index ec73d4885e..469844b503 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2192,6 +2192,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s stats.UniqueAccountsAccessed = statedb.UniqueAccountsAccessed() stats.UniqueStorageAccessed = statedb.UniqueStorageAccessed() stats.UniqueCodeExecuted = statedb.UniqueCodeExecuted() + stats.SystemCodeExecuted = statedb.SystemCodeExecuted() 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 diff --git a/core/blockchain_stats.go b/core/blockchain_stats.go index 81586d1926..33a716da01 100644 --- a/core/blockchain_stats.go +++ b/core/blockchain_stats.go @@ -52,6 +52,7 @@ type ExecuteStats struct { UniqueAccountsAccessed int // Number of unique accounts accessed UniqueStorageAccessed int // Number of unique storage slots accessed 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 Validation time.Duration // Time spent on the block validation @@ -132,7 +133,7 @@ State read: %v Unique state access: Accounts: %d Storage slots: %d - Contracts executed: %d + Contracts executed: %d (%d system) State hash: %v Account hash: %v @@ -158,7 +159,7 @@ DB write: %v // Unique state access s.UniqueAccountsAccessed, s.UniqueStorageAccessed, - s.UniqueCodeExecuted, + s.UniqueCodeExecuted, s.SystemCodeExecuted, // State hash common.PrettyDuration(s.AccountHashes+s.AccountUpdates+s.StorageUpdates+max(s.AccountCommits, s.StorageCommits)), diff --git a/core/state/statedb.go b/core/state/statedb.go index b95f864b6e..d3283d18a2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -163,7 +163,8 @@ type StateDB struct { 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) - 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. @@ -191,6 +192,7 @@ func NewWithReader(root common.Hash, db Database, reader Reader) (*StateDB, erro accessList: newAccessList(), transientStorage: newTransientStorage(), executedCodes: make(map[common.Hash]struct{}), + systemExecutedCodes: make(map[common.Hash]struct{}), } if db.TrieDB().IsVerkle() { 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. // 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{}) { return } @@ -394,6 +397,9 @@ func (s *StateDB) MarkCodeExecuted(codeHash common.Hash) { return // Skip tracking for state copies (e.g., prefetcher) } s.executedCodes[codeHash] = struct{}{} + if isSystem { + s.systemExecutedCodes[codeHash] = struct{}{} + } } // UniqueCodeExecuted returns the number of unique contract codes executed. @@ -401,6 +407,12 @@ func (s *StateDB) UniqueCodeExecuted() int { 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. func (s *StateDB) UniqueAccountsAccessed() int { return len(s.stateObjects) diff --git a/core/state/statedb_hooked.go b/core/state/statedb_hooked.go index 997ef91667..0e3b543322 100644 --- a/core/state/statedb_hooked.go +++ b/core/state/statedb_hooked.go @@ -298,6 +298,6 @@ func (s *hookedStateDB) Inner() *StateDB { } // MarkCodeExecuted records that a contract's code was executed. -func (s *hookedStateDB) MarkCodeExecuted(codeHash common.Hash) { - s.inner.MarkCodeExecuted(codeHash) +func (s *hookedStateDB) MarkCodeExecuted(codeHash common.Hash, isSystem bool) { + s.inner.MarkCodeExecuted(codeHash, isSystem) } diff --git a/core/vm/evm.go b/core/vm/evm.go index 43787c2d0f..0ffe4f7095 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -296,7 +296,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g codeHash := evm.resolveCodeHash(addr) contract.SetCallCode(codeHash, code) // Track unique contract execution for metrics - evm.StateDB.MarkCodeExecuted(codeHash) + evm.StateDB.MarkCodeExecuted(codeHash, contract.IsSystemCall) ret, err = evm.Run(contract, input, false) gas = contract.Gas } @@ -358,7 +358,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt codeHash := evm.resolveCodeHash(addr) contract.SetCallCode(codeHash, evm.resolveCode(addr)) // Track unique contract execution for metrics - evm.StateDB.MarkCodeExecuted(codeHash) + evm.StateDB.MarkCodeExecuted(codeHash, isSystemCall(caller)) ret, err = evm.Run(contract, input, false) gas = contract.Gas } @@ -405,7 +405,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, codeHash := evm.resolveCodeHash(addr) contract.SetCallCode(codeHash, evm.resolveCode(addr)) // Track unique contract execution for metrics - evm.StateDB.MarkCodeExecuted(codeHash) + evm.StateDB.MarkCodeExecuted(codeHash, isSystemCall(originCaller)) ret, err = evm.Run(contract, input, false) gas = contract.Gas } @@ -459,7 +459,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b codeHash := evm.resolveCodeHash(addr) contract.SetCallCode(codeHash, evm.resolveCode(addr)) // 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 // above we revert to the snapshot and consume any gas remaining. Additionally diff --git a/core/vm/interface.go b/core/vm/interface.go index fc8efb02aa..13da4a1ea9 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -104,5 +104,6 @@ type StateDB interface { // MarkCodeExecuted records that a contract's code was 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) }