diff --git a/core/blockchain.go b/core/blockchain.go
index 8cfeabe8a2..6f1db96463 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -2107,24 +2107,11 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
}
// Upload the statistics of reader at the end
defer func() {
- pStat := prefetch.GetStats()
- accountCacheHitPrefetchMeter.Mark(pStat.AccountCacheHit)
- accountCacheMissPrefetchMeter.Mark(pStat.AccountCacheMiss)
- storageCacheHitPrefetchMeter.Mark(pStat.StorageCacheHit)
- storageCacheMissPrefetchMeter.Mark(pStat.StorageCacheMiss)
-
- rStat := process.GetStats()
- accountCacheHitMeter.Mark(rStat.AccountCacheHit)
- accountCacheMissMeter.Mark(rStat.AccountCacheMiss)
- storageCacheHitMeter.Mark(rStat.StorageCacheHit)
- storageCacheMissMeter.Mark(rStat.StorageCacheMiss)
-
if result != nil {
- result.stats.StatePrefetchCacheStats = pStat
- result.stats.StateReadCacheStats = rStat
+ result.stats.StatePrefetchCacheStats = prefetch.GetStats()
+ result.stats.StateReadCacheStats = process.GetStats()
}
}()
-
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
// Disable tracing for prefetcher executions.
vmCfg := bc.cfg.VmConfig
@@ -2239,16 +2226,11 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
stats.StorageLoaded = statedb.StorageLoaded
stats.StorageUpdated = int(statedb.StorageUpdated.Load())
stats.StorageDeleted = int(statedb.StorageDeleted.Load())
+
stats.CodeLoaded = statedb.CodeLoaded
stats.CodeLoadBytes = statedb.CodeLoadBytes
-
- // EIP-7702 delegation metrics
- stats.Eip7702DelegationsSet = statedb.Eip7702DelegationsSet
- stats.Eip7702DelegationsCleared = statedb.Eip7702DelegationsCleared
-
- // Code write metrics
stats.CodeUpdated = statedb.CodeUpdated
- stats.CodeBytesWrite = statedb.CodeBytesWrite
+ stats.CodeUpdateBytes = statedb.CodeUpdateBytes
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_7702_metrics_test.go b/core/blockchain_7702_metrics_test.go
deleted file mode 100644
index 186273ca54..0000000000
--- a/core/blockchain_7702_metrics_test.go
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2025 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/beacon"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
- "github.com/holiman/uint256"
-)
-
-// TestEIP7702DelegationMetricsSet verifies that Eip7702DelegationsSet is correctly
-// incremented when a SetCodeTx sets a delegation (non-zero address).
-func TestEIP7702DelegationMetricsSet(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- target = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- addr1: {Balance: funds},
- target: {Code: []byte{0x60, 0x00}, Balance: big.NewInt(0)}, // Simple PUSH1 0
- },
- }
-
- // Sign authorization to set delegation to target address
- // Note: Nonce is 1 because key1 is also the tx sender, and sender's nonce
- // is incremented (0->1) before authorization validation runs.
- auth, err := types.SignSetCode(key1, types.SetCodeAuthorization{
- ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
- Address: target, // Non-zero address = SET delegation
- Nonce: 1,
- })
- if err != nil {
- t.Fatalf("failed to sign authorization: %v", err)
- }
-
- // Generate block with SetCodeTx
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- txdata := &types.SetCodeTx{
- ChainID: uint256.MustFromBig(gspec.Config.ChainID),
- Nonce: 0,
- To: addr1,
- Gas: 100000,
- GasFeeCap: uint256.MustFromBig(newGwei(5)),
- GasTipCap: uint256.NewInt(2),
- AuthList: []types.SetCodeAuthorization{auth},
- }
- tx := types.MustSignNewTx(key1, signer, txdata)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- // Process block and get stats
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- // Verify SET delegation metric
- stats := result.Stats()
- if stats.Eip7702DelegationsSet != 1 {
- t.Errorf("Expected Eip7702DelegationsSet=1, got %d", stats.Eip7702DelegationsSet)
- }
- if stats.Eip7702DelegationsCleared != 0 {
- t.Errorf("Expected Eip7702DelegationsCleared=0, got %d", stats.Eip7702DelegationsCleared)
- }
-
- // Also verify CodeUpdated and CodeBytesWrite are incremented
- // Delegation code is 23 bytes (0xef0100 + 20-byte address)
- if stats.CodeUpdated != 1 {
- t.Errorf("Expected CodeUpdated=1, got %d", stats.CodeUpdated)
- }
- if stats.CodeBytesWrite != 23 {
- t.Errorf("Expected CodeBytesWrite=23 (delegation code size), got %d", stats.CodeBytesWrite)
- }
-}
-
-// TestEIP7702DelegationMetricsClear verifies that Eip7702DelegationsCleared is correctly
-// incremented when a SetCodeTx clears a delegation (zero address).
-func TestEIP7702DelegationMetricsClear(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- target = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Start with addr1 already having a delegation set
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- addr1: {
- Balance: funds,
- Code: types.AddressToDelegation(target), // Pre-existing delegation
- Nonce: 1, // Nonce 1 since delegation was "set"
- },
- target: {Code: []byte{0x60, 0x00}, Balance: big.NewInt(0)},
- },
- }
-
- // Sign authorization to CLEAR delegation (zero address)
- // Note: Auth nonce is 2 because addr1 starts with nonce 1, and tx sender's
- // nonce is incremented (1->2) before authorization validation runs.
- auth, err := types.SignSetCode(key1, types.SetCodeAuthorization{
- ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
- Address: common.Address{}, // Zero address = CLEAR delegation
- Nonce: 2, // Post-increment nonce (1->2)
- })
- if err != nil {
- t.Fatalf("failed to sign authorization: %v", err)
- }
-
- // Generate block with SetCodeTx that clears delegation
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- txdata := &types.SetCodeTx{
- ChainID: uint256.MustFromBig(gspec.Config.ChainID),
- Nonce: 1, // Account starts with nonce 1
- To: addr1,
- Gas: 100000,
- GasFeeCap: uint256.MustFromBig(newGwei(5)),
- GasTipCap: uint256.NewInt(2),
- AuthList: []types.SetCodeAuthorization{auth},
- }
- tx := types.MustSignNewTx(key1, signer, txdata)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- // Process block and get stats
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- // Verify CLEAR delegation metric
- stats := result.Stats()
- if stats.Eip7702DelegationsCleared != 1 {
- t.Errorf("Expected Eip7702DelegationsCleared=1, got %d", stats.Eip7702DelegationsCleared)
- }
- if stats.Eip7702DelegationsSet != 0 {
- t.Errorf("Expected Eip7702DelegationsSet=0, got %d", stats.Eip7702DelegationsSet)
- }
-}
-
-// TestEIP7702DelegationMetricsMultiple verifies metrics when multiple authorizations
-// are included in a single SetCodeTx.
-func TestEIP7702DelegationMetricsMultiple(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- addr2 = crypto.PubkeyToAddress(key2.PublicKey)
- addr3 = crypto.PubkeyToAddress(key3.PublicKey)
- targetA = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
- targetB = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // addr3 starts with a delegation that will be cleared
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- addr1: {Balance: funds},
- addr2: {Balance: funds},
- addr3: {Balance: funds, Code: types.AddressToDelegation(targetA), Nonce: 1},
- targetA: {Code: []byte{0x60, 0x00}, Balance: big.NewInt(0)},
- targetB: {Code: []byte{0x60, 0x01}, Balance: big.NewInt(0)},
- },
- }
-
- // Auth 1: addr1 sets delegation to targetA
- // Note: Nonce is 1 because key1 is also the tx sender (nonce 0->1)
- auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
- ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
- Address: targetA,
- Nonce: 1,
- })
-
- // Auth 2: addr2 sets delegation to targetB
- auth2, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
- ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
- Address: targetB,
- Nonce: 0,
- })
-
- // Auth 3: addr3 clears delegation (zero address)
- auth3, _ := types.SignSetCode(key3, types.SetCodeAuthorization{
- ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
- Address: common.Address{}, // Clear
- Nonce: 1,
- })
-
- // Generate block with all three authorizations
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- txdata := &types.SetCodeTx{
- ChainID: uint256.MustFromBig(gspec.Config.ChainID),
- Nonce: 0,
- To: addr1,
- Gas: 500000,
- GasFeeCap: uint256.MustFromBig(newGwei(5)),
- GasTipCap: uint256.NewInt(2),
- AuthList: []types.SetCodeAuthorization{auth1, auth2, auth3},
- }
- tx := types.MustSignNewTx(key1, signer, txdata)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- // Process block and get stats
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- // Verify metrics: 2 delegations set, 1 cleared
- stats := result.Stats()
- if stats.Eip7702DelegationsSet != 2 {
- t.Errorf("Expected Eip7702DelegationsSet=2, got %d", stats.Eip7702DelegationsSet)
- }
- if stats.Eip7702DelegationsCleared != 1 {
- t.Errorf("Expected Eip7702DelegationsCleared=1, got %d", stats.Eip7702DelegationsCleared)
- }
-
- // CodeUpdated should be 2 (two delegations set, clear doesn't add code)
- if stats.CodeUpdated != 2 {
- t.Errorf("Expected CodeUpdated=2, got %d", stats.CodeUpdated)
- }
-
- // CodeBytesWrite should be 46 (23 bytes per delegation * 2)
- if stats.CodeBytesWrite != 46 {
- t.Errorf("Expected CodeBytesWrite=46, got %d", stats.CodeBytesWrite)
- }
-}
diff --git a/core/blockchain_metrics_test.go b/core/blockchain_metrics_test.go
deleted file mode 100644
index 1e0784dbba..0000000000
--- a/core/blockchain_metrics_test.go
+++ /dev/null
@@ -1,576 +0,0 @@
-// Copyright 2025 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "math/big"
- "testing"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/beacon"
- "github.com/ethereum/go-ethereum/consensus/ethash"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/params"
- "github.com/holiman/uint256"
-)
-
-// TestAccountMetrics verifies that AccountLoaded and AccountUpdated are correctly
-// tracked when processing a simple ETH transfer transaction.
-func TestAccountMetrics(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- receiver = common.HexToAddress("0x1111111111111111111111111111111111111111")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- receiver: {Balance: big.NewInt(1)}, // Pre-existing account
- },
- }
-
- // Generate block with simple ETH transfer
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0, // nonce
- receiver, // to
- big.NewInt(1000), // value
- 21000, // gas
- uint256.MustFromBig(newGwei(5)).ToBig(), // gasPrice
- nil, // data
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- // Process block and get stats
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // Both sender and receiver should be loaded from the database
- if stats.AccountLoaded < 2 {
- t.Errorf("Expected AccountLoaded >= 2, got %d", stats.AccountLoaded)
- }
-
- // Both sender (nonce+balance) and receiver (balance) should be updated
- if stats.AccountUpdated < 2 {
- t.Errorf("Expected AccountUpdated >= 2, got %d", stats.AccountUpdated)
- }
-}
-
-// TestStorageLoadedMetric verifies that StorageLoaded is correctly incremented
-// when a contract reads from storage that was set in genesis.
-func TestStorageLoadedMetric(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- contract = common.HexToAddress("0x2222222222222222222222222222222222222222")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Contract that reads slot 0 via SLOAD and returns it
- // PUSH1 0x00 SLOAD POP STOP
- contractCode := []byte{
- byte(vm.PUSH1), 0x00,
- byte(vm.SLOAD),
- byte(vm.POP),
- byte(vm.STOP),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- contract: {
- Code: contractCode,
- Balance: big.NewInt(0),
- Storage: map[common.Hash]common.Hash{
- common.HexToHash("0x00"): common.HexToHash("0x42"), // Pre-populated storage
- },
- },
- },
- }
-
- // Generate block that calls the contract
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0,
- contract,
- big.NewInt(0),
- 50000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- nil,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // Storage slot 0 should be loaded from DB
- if stats.StorageLoaded < 1 {
- t.Errorf("Expected StorageLoaded >= 1, got %d", stats.StorageLoaded)
- }
-}
-
-// TestStorageUpdatedMetric verifies that StorageUpdated is correctly incremented
-// when a contract writes to storage.
-func TestStorageUpdatedMetric(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- contract = common.HexToAddress("0x3333333333333333333333333333333333333333")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Contract that writes 0x42 to slot 0x01
- // PUSH1 0x42 PUSH1 0x01 SSTORE STOP
- contractCode := []byte{
- byte(vm.PUSH1), 0x42, // value
- byte(vm.PUSH1), 0x01, // key
- byte(vm.SSTORE),
- byte(vm.STOP),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- contract: {Code: contractCode, Balance: big.NewInt(0)},
- },
- }
-
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0,
- contract,
- big.NewInt(0),
- 50000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- nil,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // Storage slot 1 should be updated
- if stats.StorageUpdated < 1 {
- t.Errorf("Expected StorageUpdated >= 1, got %d", stats.StorageUpdated)
- }
-}
-
-// TestStorageDeletedMetric verifies that StorageDeleted is correctly incremented
-// when a contract clears a storage slot (sets to zero).
-func TestStorageDeletedMetric(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- contract = common.HexToAddress("0x4444444444444444444444444444444444444444")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Contract that clears slot 0x01 (sets to zero)
- // PUSH1 0x00 PUSH1 0x01 SSTORE STOP
- contractCode := []byte{
- byte(vm.PUSH1), 0x00, // value (zero = delete)
- byte(vm.PUSH1), 0x01, // key
- byte(vm.SSTORE),
- byte(vm.STOP),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- contract: {
- Code: contractCode,
- Balance: big.NewInt(0),
- Storage: map[common.Hash]common.Hash{
- common.HexToHash("0x01"): common.HexToHash("0x42"), // Pre-existing non-zero value
- },
- },
- },
- }
-
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0,
- contract,
- big.NewInt(0),
- 50000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- nil,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // Storage slot 1 should be deleted (cleared)
- if stats.StorageDeleted < 1 {
- t.Errorf("Expected StorageDeleted >= 1, got %d", stats.StorageDeleted)
- }
-}
-
-// TestCodeLoadedMetric verifies that CodeLoaded is correctly incremented
-// when a contract's code is fetched to execute it.
-func TestCodeLoadedMetric(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- contract = common.HexToAddress("0x5555555555555555555555555555555555555555")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Simple contract: PUSH1 0x42 POP STOP
- contractCode := []byte{
- byte(vm.PUSH1), 0x42,
- byte(vm.POP),
- byte(vm.STOP),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- contract: {Code: contractCode, Balance: big.NewInt(0)},
- },
- }
-
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0,
- contract,
- big.NewInt(0),
- 50000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- nil,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // Contract code should be loaded to execute
- if stats.CodeLoaded < 1 {
- t.Errorf("Expected CodeLoaded >= 1, got %d", stats.CodeLoaded)
- }
-}
-
-// TestCodeUpdatedMetricCREATE verifies that CodeUpdated and CodeBytesWrite are correctly
-// incremented when a contract is deployed via a creation transaction.
-func TestCodeUpdatedMetricCREATE(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Runtime code: PUSH1 0x42 POP STOP (4 bytes)
- runtimeCode := []byte{
- byte(vm.PUSH1), 0x42,
- byte(vm.POP),
- byte(vm.STOP),
- }
- runtimeLen := len(runtimeCode)
-
- // Initcode that returns the runtime code
- // PUSH4 PUSH1 0x00 MSTORE PUSH1 PUSH1 0x1c RETURN
- initCode := []byte{
- byte(vm.PUSH4),
- runtimeCode[0], runtimeCode[1], runtimeCode[2], runtimeCode[3],
- byte(vm.PUSH1), 0x00,
- byte(vm.MSTORE),
- byte(vm.PUSH1), byte(runtimeLen), // size
- byte(vm.PUSH1), 0x1c, // offset (32 - 4 = 28 = 0x1c)
- byte(vm.RETURN),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- },
- }
-
- // Contract creation transaction (to = nil)
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewContractCreation(
- 0,
- big.NewInt(0),
- 100000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- initCode,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // One contract should be deployed
- if stats.CodeUpdated != 1 {
- t.Errorf("Expected CodeUpdated = 1, got %d", stats.CodeUpdated)
- }
-
- // Runtime code is 4 bytes
- if stats.CodeBytesWrite != runtimeLen {
- t.Errorf("Expected CodeBytesWrite = %d, got %d", runtimeLen, stats.CodeBytesWrite)
- }
-}
-
-// TestAccountDeletedMetric verifies that AccountDeleted is correctly incremented
-// when a contract self-destructs. Post-Cancun (EIP-6780), this only works if the
-// contract is created and destroyed in the same transaction.
-func TestAccountDeletedMetric(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- beneficiary = common.HexToAddress("0x6666666666666666666666666666666666666666")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Initcode that:
- // 1. Deploys runtime code that will never execute (we self-destruct in initcode)
- // 2. Immediately calls SELFDESTRUCT to beneficiary during contract creation
- // This is EIP-6780 compatible because we self-destruct in the same tx as creation.
- //
- // PUSH20 SELFDESTRUCT
- initCode := make([]byte, 0, 22)
- initCode = append(initCode, byte(vm.PUSH20))
- initCode = append(initCode, beneficiary.Bytes()...)
- initCode = append(initCode, byte(vm.SELFDESTRUCT))
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- beneficiary: {Balance: big.NewInt(1)}, // Pre-existing beneficiary
- },
- }
-
- // Contract creation that immediately self-destructs
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewContractCreation(
- 0,
- big.NewInt(1000), // Send some ETH to the contract
- 100000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- initCode,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // The newly created contract should be deleted (self-destructed)
- if stats.AccountDeleted < 1 {
- t.Errorf("Expected AccountDeleted >= 1, got %d", stats.AccountDeleted)
- }
-}
-
-// TestMultipleStorageOperationsMetrics verifies that storage metrics correctly
-// accumulate when multiple operations occur.
-func TestMultipleStorageOperationsMetrics(t *testing.T) {
- var (
- config = *params.MergedTestChainConfig
- signer = types.LatestSigner(&config)
- engine = beacon.New(ethash.NewFaker())
- key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- sender = crypto.PubkeyToAddress(key.PublicKey)
- contract = common.HexToAddress("0x7777777777777777777777777777777777777777")
- funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
- )
-
- // Contract that:
- // 1. Reads slots 0, 1, 2 (3 SLOADs)
- // 2. Writes to slots 3, 4 (2 SSTOREs with non-zero values)
- // 3. Clears slot 5 (1 SSTORE with zero, existing value was non-zero)
- contractCode := []byte{
- // SLOAD slot 0
- byte(vm.PUSH1), 0x00, byte(vm.SLOAD), byte(vm.POP),
- // SLOAD slot 1
- byte(vm.PUSH1), 0x01, byte(vm.SLOAD), byte(vm.POP),
- // SLOAD slot 2
- byte(vm.PUSH1), 0x02, byte(vm.SLOAD), byte(vm.POP),
- // SSTORE slot 3 = 0xFF
- byte(vm.PUSH1), 0xFF, byte(vm.PUSH1), 0x03, byte(vm.SSTORE),
- // SSTORE slot 4 = 0xEE
- byte(vm.PUSH1), 0xEE, byte(vm.PUSH1), 0x04, byte(vm.SSTORE),
- // SSTORE slot 5 = 0x00 (delete)
- byte(vm.PUSH1), 0x00, byte(vm.PUSH1), 0x05, byte(vm.SSTORE),
- byte(vm.STOP),
- }
-
- gspec := &Genesis{
- Config: &config,
- Alloc: types.GenesisAlloc{
- sender: {Balance: funds},
- contract: {
- Code: contractCode,
- Balance: big.NewInt(0),
- Storage: map[common.Hash]common.Hash{
- common.HexToHash("0x00"): common.HexToHash("0x01"),
- common.HexToHash("0x01"): common.HexToHash("0x02"),
- common.HexToHash("0x02"): common.HexToHash("0x03"),
- common.HexToHash("0x05"): common.HexToHash("0xAA"), // Will be cleared
- },
- },
- },
- }
-
- _, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
- tx, _ := types.SignTx(types.NewTransaction(
- 0,
- contract,
- big.NewInt(0),
- 100000,
- uint256.MustFromBig(newGwei(5)).ToBig(),
- nil,
- ), signer, key)
- b.AddTx(tx)
- })
-
- chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), gspec, engine, nil)
- if err != nil {
- t.Fatalf("failed to create chain: %v", err)
- }
- defer chain.Stop()
-
- result, err := chain.ProcessBlock(chain.CurrentBlock().Root, blocks[0], true, false)
- if err != nil {
- t.Fatalf("failed to process block: %v", err)
- }
-
- stats := result.Stats()
-
- // 3 slots read: 0, 1, 2
- if stats.StorageLoaded < 3 {
- t.Errorf("Expected StorageLoaded >= 3, got %d", stats.StorageLoaded)
- }
-
- // 2 slots written with non-zero values: 3, 4
- if stats.StorageUpdated < 2 {
- t.Errorf("Expected StorageUpdated >= 2, got %d", stats.StorageUpdated)
- }
-
- // 1 slot cleared: 5
- if stats.StorageDeleted < 1 {
- t.Errorf("Expected StorageDeleted >= 1, got %d", stats.StorageDeleted)
- }
-}
diff --git a/core/blockchain_stats.go b/core/blockchain_stats.go
index 5bd2e5bd1e..adc66266c4 100644
--- a/core/blockchain_stats.go
+++ b/core/blockchain_stats.go
@@ -38,20 +38,16 @@ type ExecuteStats struct {
StorageCommits time.Duration // Time spent on the storage trie commit
CodeReads time.Duration // Time spent on the contract code read
- AccountLoaded int // Number of accounts loaded
- AccountUpdated int // Number of accounts updated
- AccountDeleted int // Number of accounts deleted
- StorageLoaded int // Number of storage slots loaded
- StorageUpdated int // Number of storage slots updated
- StorageDeleted int // Number of storage slots deleted
- CodeLoaded int // Number of contract code loaded
- CodeLoadBytes int // Number of bytes read from contract code
- CodeUpdated int // Number of contract code written (CREATE/CREATE2 + EIP-7702)
- CodeBytesWrite int // Total bytes of code written
-
- // EIP-7702 delegation tracking
- Eip7702DelegationsSet int // Number of EIP-7702 delegations set
- Eip7702DelegationsCleared int // Number of EIP-7702 delegations cleared
+ AccountLoaded int // Number of accounts loaded
+ AccountUpdated int // Number of accounts updated
+ AccountDeleted int // Number of accounts deleted
+ StorageLoaded int // Number of storage slots loaded
+ StorageUpdated int // Number of storage slots updated
+ StorageDeleted int // Number of storage slots deleted
+ CodeLoaded int // Number of contract code loaded
+ CodeLoadBytes int // Number of bytes read from contract code
+ CodeUpdated int // Number of contract code written (CREATE/CREATE2 + EIP-7702)
+ CodeUpdateBytes int // Total bytes of code written
Execution time.Duration // Time spent on the EVM execution
Validation time.Duration // Time spent on the block validation
@@ -150,14 +146,12 @@ type slowBlockReads struct {
}
type slowBlockWrites struct {
- Accounts int `json:"accounts"`
- AccountsDeleted int `json:"accounts_deleted"`
- StorageSlots int `json:"storage_slots"`
- StorageSlotsDeleted int `json:"storage_slots_deleted"`
- Code int `json:"code"`
- CodeBytes int `json:"code_bytes"`
- Eip7702DelegationsSet int `json:"eip7702_delegations_set"`
- Eip7702DelegationsCleared int `json:"eip7702_delegations_cleared"`
+ Accounts int `json:"accounts"`
+ AccountsDeleted int `json:"accounts_deleted"`
+ StorageSlots int `json:"storage_slots"`
+ StorageSlotsDeleted int `json:"storage_slots_deleted"`
+ Code int `json:"code"`
+ CodeBytes int `json:"code_bytes"`
}
// slowBlockCache represents cache hit/miss statistics for cross-client analysis.
@@ -209,7 +203,6 @@ func (s *ExecuteStats) logSlow(block *types.Block, slowBlockThreshold time.Durat
if slowBlockThreshold > 0 && s.TotalTime < slowBlockThreshold {
return
}
-
logEntry := slowBlockLog{
Level: "warn",
Msg: "Slow block",
@@ -236,14 +229,12 @@ func (s *ExecuteStats) logSlow(block *types.Block, slowBlockThreshold time.Durat
CodeBytes: s.CodeLoadBytes,
},
StateWrites: slowBlockWrites{
- Accounts: s.AccountUpdated,
- AccountsDeleted: s.AccountDeleted,
- StorageSlots: s.StorageUpdated,
- StorageSlotsDeleted: s.StorageDeleted,
- Code: s.CodeUpdated,
- CodeBytes: s.CodeBytesWrite,
- Eip7702DelegationsSet: s.Eip7702DelegationsSet,
- Eip7702DelegationsCleared: s.Eip7702DelegationsCleared,
+ Accounts: s.AccountUpdated,
+ AccountsDeleted: s.AccountDeleted,
+ StorageSlots: s.StorageUpdated,
+ StorageSlotsDeleted: s.StorageDeleted,
+ Code: s.CodeUpdated,
+ CodeBytes: s.CodeUpdateBytes,
},
Cache: slowBlockCache{
Account: slowBlockCacheEntry{
@@ -265,7 +256,6 @@ func (s *ExecuteStats) logSlow(block *types.Block, slowBlockThreshold time.Durat
},
},
}
-
jsonBytes, err := json.Marshal(logEntry)
if err != nil {
log.Error("Failed to marshal slow block log", "error", err)
diff --git a/core/blockchain_stats_test.go b/core/blockchain_stats_test.go
deleted file mode 100644
index 269168fde1..0000000000
--- a/core/blockchain_stats_test.go
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2025 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package core
-
-import (
- "bytes"
- "encoding/json"
- "testing"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/log"
-)
-
-// TestLogSlowBlockJSON tests that logSlow outputs valid JSON in the expected format.
-func TestLogSlowBlockJSON(t *testing.T) {
- // Create a buffer to capture log output
- var buf bytes.Buffer
- handler := log.NewTerminalHandler(&buf, false)
- log.SetDefault(log.NewLogger(handler))
-
- // Create a test block
- header := &types.Header{
- Number: common.Big1,
- GasUsed: 21000000,
- GasLimit: 30000000,
- }
- block := types.NewBlockWithHeader(header)
-
- // Create test stats with cache data
- stats := &ExecuteStats{
- Execution: 500 * time.Millisecond,
- TotalTime: 1200 * time.Millisecond, // > 1s threshold
- MgasPerSecond: 17.5,
- AccountLoaded: 100,
- StorageLoaded: 500,
- CodeLoaded: 20,
- AccountUpdated: 50,
- StorageUpdated: 200,
- StateReadCacheStats: state.ReaderStats{
- AccountCacheHit: 4,
- AccountCacheMiss: 6,
- StorageCacheHit: 0,
- StorageCacheMiss: 11,
- CodeStats: state.ContractCodeReaderStats{
- CacheHit: 4,
- CacheMiss: 0,
- CacheHitBytes: 102400, // ~100KB served from cache
- CacheMissBytes: 0,
- },
- },
- }
-
- // Log the slow block (threshold of 1s, so 1.2s total time should trigger)
- stats.logSlow(block, 1*time.Second)
-
- // Get the log output
- output := buf.String()
- t.Logf("Log output:\n%s", output)
-
- // The output should contain the JSON
- if len(output) == 0 {
- t.Fatal("Expected log output, got empty string")
- }
-
- // Try to extract and parse the JSON from the log line
- // The log format is: WARN [...] {"level":"warn",...}
- // We need to find the JSON part
- jsonStart := bytes.Index(buf.Bytes(), []byte(`{"level"`))
- if jsonStart == -1 {
- t.Logf("Full output: %s", output)
- t.Fatal("Could not find JSON in log output")
- }
-
- jsonBytes := buf.Bytes()[jsonStart:]
- // Find the end of the JSON (newline or end of buffer)
- jsonEnd := bytes.IndexByte(jsonBytes, '\n')
- if jsonEnd != -1 {
- jsonBytes = jsonBytes[:jsonEnd]
- }
-
- // Parse the JSON
- var logEntry slowBlockLog
- if err := json.Unmarshal(jsonBytes, &logEntry); err != nil {
- t.Fatalf("Failed to parse JSON: %v\nJSON: %s", err, string(jsonBytes))
- }
-
- // Verify the fields
- if logEntry.Level != "warn" {
- t.Errorf("Expected level 'warn', got '%s'", logEntry.Level)
- }
- if logEntry.Msg != "Slow block" {
- t.Errorf("Expected msg 'Slow block', got '%s'", logEntry.Msg)
- }
- if logEntry.Block.Number != 1 {
- t.Errorf("Expected block number 1, got %d", logEntry.Block.Number)
- }
- if logEntry.Block.GasUsed != 21000000 {
- t.Errorf("Expected gas used 21000000, got %d", logEntry.Block.GasUsed)
- }
- if logEntry.Timing.ExecutionMs != 500.0 {
- t.Errorf("Expected execution_ms 500.0, got %v", logEntry.Timing.ExecutionMs)
- }
- if logEntry.Timing.TotalMs != 1200.0 {
- t.Errorf("Expected total_ms 1200.0, got %v", logEntry.Timing.TotalMs)
- }
- if logEntry.StateReads.Accounts != 100 {
- t.Errorf("Expected accounts 100, got %d", logEntry.StateReads.Accounts)
- }
- if logEntry.StateReads.StorageSlots != 500 {
- t.Errorf("Expected storage_slots 500, got %d", logEntry.StateReads.StorageSlots)
- }
- if logEntry.StateWrites.Accounts != 50 {
- t.Errorf("Expected accounts updated 50, got %d", logEntry.StateWrites.Accounts)
- }
-
- // Verify cache statistics
- if logEntry.Cache.Account.Hits != 4 {
- t.Errorf("Expected account cache hits 4, got %d", logEntry.Cache.Account.Hits)
- }
- if logEntry.Cache.Account.Misses != 6 {
- t.Errorf("Expected account cache misses 6, got %d", logEntry.Cache.Account.Misses)
- }
- // 4/(4+6) = 0.4 = 40%
- if logEntry.Cache.Account.HitRate != 40.0 {
- t.Errorf("Expected account cache hit_rate 40.0, got %f", logEntry.Cache.Account.HitRate)
- }
- if logEntry.Cache.Storage.Hits != 0 {
- t.Errorf("Expected storage cache hits 0, got %d", logEntry.Cache.Storage.Hits)
- }
- if logEntry.Cache.Storage.Misses != 11 {
- t.Errorf("Expected storage cache misses 11, got %d", logEntry.Cache.Storage.Misses)
- }
- // 0/(0+11) = 0%
- if logEntry.Cache.Storage.HitRate != 0.0 {
- t.Errorf("Expected storage cache hit_rate 0.0, got %f", logEntry.Cache.Storage.HitRate)
- }
- if logEntry.Cache.Code.Hits != 4 {
- t.Errorf("Expected code cache hits 4, got %d", logEntry.Cache.Code.Hits)
- }
- if logEntry.Cache.Code.Misses != 0 {
- t.Errorf("Expected code cache misses 0, got %d", logEntry.Cache.Code.Misses)
- }
- // 4/(4+0) = 100%
- if logEntry.Cache.Code.HitRate != 100.0 {
- t.Errorf("Expected code cache hit_rate 100.0, got %f", logEntry.Cache.Code.HitRate)
- }
- // Verify new byte-level cache statistics
- if logEntry.Cache.Code.HitBytes != 102400 {
- t.Errorf("Expected code cache hit_bytes 102400, got %d", logEntry.Cache.Code.HitBytes)
- }
- if logEntry.Cache.Code.MissBytes != 0 {
- t.Errorf("Expected code cache miss_bytes 0, got %d", logEntry.Cache.Code.MissBytes)
- }
-
- t.Logf("Parsed JSON:\n%+v", logEntry)
-}
-
-// TestLogSlowBlockEIP7702 tests that EIP-7702 delegation fields are properly serialized.
-func TestLogSlowBlockEIP7702(t *testing.T) {
- var buf bytes.Buffer
- handler := log.NewTerminalHandler(&buf, false)
- log.SetDefault(log.NewLogger(handler))
-
- header := &types.Header{
- Number: common.Big1,
- GasUsed: 21000000,
- GasLimit: 30000000,
- }
- block := types.NewBlockWithHeader(header)
-
- // Create test stats with EIP-7702 delegation data and deletion metrics
- stats := &ExecuteStats{
- Execution: 500 * time.Millisecond,
- TotalTime: 1200 * time.Millisecond,
- MgasPerSecond: 17.5,
- AccountLoaded: 100,
- StorageLoaded: 500,
- CodeLoaded: 20,
- CodeLoadBytes: 4096,
- AccountUpdated: 50,
- AccountDeleted: 2,
- StorageUpdated: 200,
- StorageDeleted: 7,
- CodeUpdated: 5,
- CodeBytesWrite: 2048,
- Eip7702DelegationsSet: 3,
- Eip7702DelegationsCleared: 1,
- StateReadCacheStats: state.ReaderStats{
- AccountCacheHit: 4,
- AccountCacheMiss: 6,
- },
- }
-
- stats.logSlow(block, 1*time.Second)
-
- // Find and parse the JSON
- jsonStart := bytes.Index(buf.Bytes(), []byte(`{"level"`))
- if jsonStart == -1 {
- t.Fatal("Could not find JSON in log output")
- }
- jsonBytes := buf.Bytes()[jsonStart:]
- if jsonEnd := bytes.IndexByte(jsonBytes, '\n'); jsonEnd != -1 {
- jsonBytes = jsonBytes[:jsonEnd]
- }
-
- var logEntry slowBlockLog
- if err := json.Unmarshal(jsonBytes, &logEntry); err != nil {
- t.Fatalf("Failed to parse JSON: %v", err)
- }
-
- // Verify EIP-7702 fields
- if logEntry.StateWrites.Eip7702DelegationsSet != 3 {
- t.Errorf("Expected eip7702_delegations_set 3, got %d", logEntry.StateWrites.Eip7702DelegationsSet)
- }
- if logEntry.StateWrites.Eip7702DelegationsCleared != 1 {
- t.Errorf("Expected eip7702_delegations_cleared 1, got %d", logEntry.StateWrites.Eip7702DelegationsCleared)
- }
-
- // Verify code bytes fields
- if logEntry.StateReads.CodeBytes != 4096 {
- t.Errorf("Expected code_bytes read 4096, got %d", logEntry.StateReads.CodeBytes)
- }
- if logEntry.StateWrites.CodeBytes != 2048 {
- t.Errorf("Expected code_bytes write 2048, got %d", logEntry.StateWrites.CodeBytes)
- }
- if logEntry.StateWrites.Code != 5 {
- t.Errorf("Expected code writes 5, got %d", logEntry.StateWrites.Code)
- }
-
- // Verify deletion metrics
- if logEntry.StateWrites.AccountsDeleted != 2 {
- t.Errorf("Expected accounts_deleted 2, got %d", logEntry.StateWrites.AccountsDeleted)
- }
- if logEntry.StateWrites.StorageSlotsDeleted != 7 {
- t.Errorf("Expected storage_slots_deleted 7, got %d", logEntry.StateWrites.StorageSlotsDeleted)
- }
-}
-
-// TestLogSlowBlockThreshold tests that logSlow respects the threshold.
-func TestLogSlowBlockThreshold(t *testing.T) {
- var buf bytes.Buffer
- handler := log.NewTerminalHandler(&buf, false)
- log.SetDefault(log.NewLogger(handler))
-
- header := &types.Header{Number: common.Big1}
- block := types.NewBlockWithHeader(header)
-
- stats := &ExecuteStats{
- TotalTime: 500 * time.Millisecond, // 0.5s, below 1s threshold
- }
-
- // Should NOT log (below threshold)
- stats.logSlow(block, 1*time.Second)
-
- if buf.Len() > 0 {
- t.Errorf("Expected no output for fast block, got: %s", buf.String())
- }
-
- // Reset buffer
- buf.Reset()
-
- // Test with zero threshold (logs all blocks)
- stats.logSlow(block, 0)
-
- if buf.Len() == 0 {
- t.Errorf("Expected output for zero threshold (logs all), got nothing")
- }
-
- // Reset buffer
- buf.Reset()
-
- // Test with negative threshold (disabled)
- stats.logSlow(block, -1)
-
- if buf.Len() > 0 {
- t.Errorf("Expected no output for negative threshold (disabled), got: %s", buf.String())
- }
-}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 56087f75ed..35cb8164ce 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -158,21 +158,15 @@ type StateDB struct {
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
- CodeLoaded int // Number of contract code loaded during the state transition
// CodeLoadBytes is the total number of bytes read from contract code.
// This value may be smaller than the actual number of bytes read, since
// some APIs (e.g. CodeSize) may load the entire code from either the
// cache or the database when the size is not available in the cache.
- CodeLoadBytes int
-
- // EIP-7702 delegation tracking for cross-client execution metrics
- Eip7702DelegationsSet int // Number of EIP-7702 delegations set
- Eip7702DelegationsCleared int // Number of EIP-7702 delegations cleared
-
- // Code write tracking for cross-client execution metrics
- CodeUpdated int // Number of contracts with code changes that persisted
- CodeBytesWrite int // Total bytes of persisted code written
+ CodeLoaded int // Number of contract code loaded during the state transition
+ CodeLoadBytes int // Total bytes of resolved code
+ CodeUpdated int // Number of contracts with code changes that persisted
+ CodeUpdateBytes int // Total bytes of persisted code written
}
// New creates a new state from a given trie.
@@ -481,14 +475,6 @@ func (s *StateDB) SetNonce(addr common.Address, nonce uint64, reason tracing.Non
func (s *StateDB) SetCode(addr common.Address, code []byte, reason tracing.CodeChangeReason) (prev []byte) {
stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
- // EIP-7702 delegation counters are safe here: authorizations are applied
- // at transaction level (applyAuthorization), outside revertable EVM frames.
- switch reason {
- case tracing.CodeChangeAuthorization:
- s.Eip7702DelegationsSet++
- case tracing.CodeChangeAuthorizationClear:
- s.Eip7702DelegationsCleared++
- }
return stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
return nil
@@ -960,11 +946,11 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
obj := s.stateObjects[addr]
s.updateStateObject(obj)
s.AccountUpdated += 1
+
// Count code writes post-Finalise so reverted CREATEs are excluded.
- // Only count non-empty code (clears like EIP-7702 revocations are not code writes).
if obj.dirtyCode && len(obj.code) > 0 {
s.CodeUpdated += 1
- s.CodeBytesWrite += len(obj.code)
+ s.CodeUpdateBytes += len(obj.code)
}
}
usedAddrs = append(usedAddrs, addr) // Copy needed for closure