core: remove 7702 operations and some unit tests

This commit is contained in:
Gary Rong 2026-01-28 15:27:41 +08:00
parent 6f99536fcd
commit cc6de619f0
6 changed files with 32 additions and 1229 deletions

View file

@ -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

View file

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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 <runtime> PUSH1 0x00 MSTORE PUSH1 <len> 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 <beneficiary> 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)
}
}

View file

@ -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)

View file

@ -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 <http://www.gnu.org/licenses/>.
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())
}
}

View file

@ -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