diff --git a/core/blockchain_stats_test.go b/core/blockchain_stats_test.go deleted file mode 100644 index e00f59c805..0000000000 --- a/core/blockchain_stats_test.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2026 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 ( - "encoding/json" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" -) - -// TestReadDurationsAdd locks down the read-duration merge primitive used to -// aggregate per-tx, pre-tx and post-tx reads in the BAL parallel path. -func TestReadDurationsAdd(t *testing.T) { - a := state.ReadDurations{Account: 1 * time.Millisecond, Storage: 2 * time.Millisecond, Code: 3 * time.Millisecond} - b := state.ReadDurations{Account: 10 * time.Millisecond, Storage: 20 * time.Millisecond, Code: 30 * time.Millisecond} - a.Add(b) - want := state.ReadDurations{Account: 11 * time.Millisecond, Storage: 22 * time.Millisecond, Code: 33 * time.Millisecond} - if a != want { - t.Fatalf("Add mismatch: got %+v, want %+v", a, want) - } -} - -// TestStateCountsAdd locks down the count merge primitive used to aggregate -// per-tx, pre-tx and post-tx counters in the BAL parallel path. -func TestStateCountsAdd(t *testing.T) { - a := state.StateCounts{ - AccountLoaded: 1, - AccountUpdated: 2, - AccountDeleted: 3, - StorageLoaded: 4, - StorageUpdated: 5, - StorageDeleted: 6, - CodeLoaded: 7, - CodeLoadBytes: 8, - CodeUpdated: 9, - CodeUpdateBytes: 10, - } - b := state.StateCounts{ - AccountLoaded: 100, - AccountUpdated: 200, - AccountDeleted: 300, - StorageLoaded: 400, - StorageUpdated: 500, - StorageDeleted: 600, - CodeLoaded: 700, - CodeLoadBytes: 800, - CodeUpdated: 900, - CodeUpdateBytes: 1000, - } - a.Add(b) - want := state.StateCounts{ - AccountLoaded: 101, - AccountUpdated: 202, - AccountDeleted: 303, - StorageLoaded: 404, - StorageUpdated: 505, - StorageDeleted: 606, - CodeLoaded: 707, - CodeLoadBytes: 808, - CodeUpdated: 909, - CodeUpdateBytes: 1010, - } - if a != want { - t.Fatalf("Add mismatch: got %+v, want %+v", a, want) - } -} - -// fixtureBlock builds a minimal *types.Block usable as the slow-block log -// subject. Only the header fields read by buildSlowBlockLog matter -// (Number, GasUsed, plus Transactions count via Body). -func fixtureBlock(number uint64, gasUsed uint64) *types.Block { - header := &types.Header{ - Number: new(big.Int).SetUint64(number), - GasUsed: gasUsed, - } - return types.NewBlockWithHeader(header) -} - -// TestBuildSlowBlockLog_NonBALShape ensures non-BAL output doesn't include -// the optional `bal` block (omitempty contract). -func TestBuildSlowBlockLog_NonBALShape(t *testing.T) { - stats := &ExecuteStats{ - AccountReads: 3 * time.Millisecond, - StorageReads: 4 * time.Millisecond, - AccountHashes: 5 * time.Millisecond, - Execution: 7 * time.Millisecond, - TotalTime: 20 * time.Millisecond, - MgasPerSecond: 12.5, - StateCounts: state.StateCounts{ - AccountLoaded: 9, - StorageLoaded: 21, - StorageUpdated: 42, - }, - // balTransitionStats deliberately nil — non-BAL block. - } - block := fixtureBlock(1234, 21000) - logEntry := buildSlowBlockLog(stats, block) - - if logEntry.BAL != nil { - t.Fatalf("non-BAL log unexpectedly has bal extension: %+v", logEntry.BAL) - } - jsonBytes, err := json.Marshal(logEntry) - if err != nil { - t.Fatalf("marshal failed: %v", err) - } - var decoded map[string]any - if err := json.Unmarshal(jsonBytes, &decoded); err != nil { - t.Fatalf("unmarshal failed: %v", err) - } - wantKeys := map[string]bool{ - "level": true, "msg": true, "block": true, "timing": true, - "throughput": true, "state_reads": true, "state_writes": true, "cache": true, - } - for k := range decoded { - if !wantKeys[k] { - t.Errorf("unexpected top-level key %q in non-BAL output (full JSON: %s)", k, string(jsonBytes)) - } - delete(wantKeys, k) - } - if len(wantKeys) != 0 { - t.Errorf("missing top-level keys: %v", wantKeys) - } - // Spot-check a count field that exercises the int64→int conversion in - // state_writes.storage_slots (StorageUpdated is int64 on StateCounts). - writes := decoded["state_writes"].(map[string]any) - if got := writes["storage_slots"].(float64); got != 42 { - t.Errorf("storage_slots: got %v, want 42", got) - } -} - -// TestBuildSlowBlockLog_BALShape ensures BAL output includes the bal extension -// with all expected sub-keys. -func TestBuildSlowBlockLog_BALShape(t *testing.T) { - balMetrics := &state.BALStateTransitionMetrics{ - StatePrefetch: 1 * time.Millisecond, - AccountUpdate: 2 * time.Millisecond, - StateUpdate: 3 * time.Millisecond, - StateHash: 4 * time.Millisecond, - AccountCommits: 5 * time.Millisecond, - StorageCommits: 6 * time.Millisecond, - TrieDBCommits: 7 * time.Millisecond, - SnapshotCommits: 8 * time.Millisecond, - } - stats := &ExecuteStats{ - AccountReads: 11 * time.Millisecond, - StorageReads: 22 * time.Millisecond, - CodeReads: 3 * time.Millisecond, - Execution: 15 * time.Millisecond, - TotalTime: 20 * time.Millisecond, - MgasPerSecond: 30.0, - ExecWall: 15 * time.Millisecond, - PostProcess: 2 * time.Millisecond, - Prefetch: 1 * time.Millisecond, - balTransitionStats: balMetrics, - StateCounts: state.StateCounts{ - AccountUpdated: 3, - AccountDeleted: 1, - CodeUpdated: 2, - CodeUpdateBytes: 1024, - }, - StateReadCacheStats: state.ReaderStats{ - StateStats: state.StateReaderStats{ - AccountCacheHit: 10, - AccountCacheMiss: 5, - StorageCacheHit: 20, - StorageCacheMiss: 8, - }, - }, - } - block := fixtureBlock(7, 100000) - logEntry := buildSlowBlockLog(stats, block) - - if logEntry.BAL == nil { - t.Fatal("BAL log missing the bal extension") - } - jsonBytes, err := json.Marshal(logEntry) - if err != nil { - t.Fatalf("marshal failed: %v", err) - } - var decoded map[string]any - if err := json.Unmarshal(jsonBytes, &decoded); err != nil { - t.Fatalf("unmarshal failed: %v", err) - } - bal, ok := decoded["bal"].(map[string]any) - if !ok { - t.Fatalf("bal key not present in JSON or wrong type; full JSON: %s", string(jsonBytes)) - } - wantSubkeys := []string{ - "exec_wall_ms", "post_process_ms", "prefetch_ms", - "state_prefetch_ms", "account_update_ms", "state_update_ms", "state_hash_ms", - "account_commit_ms", "storage_commit_ms", "triedb_commit_ms", "snapshot_commit_ms", - } - for _, k := range wantSubkeys { - if _, present := bal[k]; !present { - t.Errorf("bal extension missing key %q", k) - } - } - // Spot-check a value: exec_wall_ms should be 15.0 (15ms). - if got := bal["exec_wall_ms"].(float64); got != 15.0 { - t.Errorf("exec_wall_ms: got %v, want 15.0", got) - } - - // state_read_ms = AccountReads + StorageReads + CodeReads = 11 + 22 + 3 = 36 ms - timing := decoded["timing"].(map[string]any) - if got := timing["state_read_ms"].(float64); got != 36 { - t.Errorf("timing.state_read_ms: got %v, want 36", got) - } - - writes := decoded["state_writes"].(map[string]any) - if got := writes["accounts"].(float64); got != 3 { - t.Errorf("state_writes.accounts: got %v, want 3", got) - } - if got := writes["accounts_deleted"].(float64); got != 1 { - t.Errorf("state_writes.accounts_deleted: got %v, want 1", got) - } - if got := writes["code"].(float64); got != 2 { - t.Errorf("state_writes.code: got %v, want 2", got) - } - if got := writes["code_bytes"].(float64); got != 1024 { - t.Errorf("state_writes.code_bytes: got %v, want 1024", got) - } - - cache := decoded["cache"].(map[string]any) - acct := cache["account"].(map[string]any) - if got := acct["hits"].(float64); got != 10 { - t.Errorf("cache.account.hits: got %v, want 10", got) - } - if got := acct["misses"].(float64); got != 5 { - t.Errorf("cache.account.misses: got %v, want 5", got) - } - storage := cache["storage"].(map[string]any) - if got := storage["hits"].(float64); got != 20 { - t.Errorf("cache.storage.hits: got %v, want 20", got) - } - if got := storage["misses"].(float64); got != 8 { - t.Errorf("cache.storage.misses: got %v, want 8", got) - } -} - -// TestBuildSlowBlockLog_EmptyBlock ensures the helper handles a zero-tx, -// zero-counts block without panic and produces marshalable JSON. -func TestBuildSlowBlockLog_EmptyBlock(t *testing.T) { - stats := &ExecuteStats{} - block := fixtureBlock(0, 0) - logEntry := buildSlowBlockLog(stats, block) - if _, err := json.Marshal(logEntry); err != nil { - t.Fatalf("empty block marshal failed: %v", err) - } - if logEntry.BAL != nil { - t.Errorf("empty block should not have bal extension") - } -}