From cc6de619f0d81fee9a314a3bdf159ea30afd41bd Mon Sep 17 00:00:00 2001 From: Gary Rong Date: Wed, 28 Jan 2026 15:27:41 +0800 Subject: [PATCH] core: remove 7702 operations and some unit tests --- core/blockchain.go | 26 +- core/blockchain_7702_metrics_test.go | 284 ------------- core/blockchain_metrics_test.go | 576 --------------------------- core/blockchain_stats.go | 54 +-- core/blockchain_stats_test.go | 295 -------------- core/state/statedb.go | 26 +- 6 files changed, 32 insertions(+), 1229 deletions(-) delete mode 100644 core/blockchain_7702_metrics_test.go delete mode 100644 core/blockchain_metrics_test.go delete mode 100644 core/blockchain_stats_test.go 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