1+ // Copyright 2024 The go-ethereum Authors
2+ // This file is part of the go-ethereum library.
3+ //
4+ // The go-ethereum library is free software: you can redistribute it and/or modify
5+ // it under the terms of the GNU Lesser General Public License as published by
6+ // the Free Software Foundation, either version 3 of the License, or
7+ // (at your option) any later version.
8+ //
9+ // The go-ethereum library is distributed in the hope that it will be useful,
10+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ // GNU Lesser General Public License for more details.
13+ //
14+ // You should have received a copy of the GNU Lesser General Public License
15+ // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+ package core
18+
19+ import (
20+ "math/big"
21+ "testing"
22+
23+ "github.com/ethereum/go-ethereum/common"
24+ "github.com/ethereum/go-ethereum/consensus/beacon"
25+ "github.com/ethereum/go-ethereum/consensus/ethash"
26+ "github.com/ethereum/go-ethereum/core/rawdb"
27+ "github.com/ethereum/go-ethereum/core/state"
28+ "github.com/ethereum/go-ethereum/core/tracing"
29+ "github.com/ethereum/go-ethereum/core/types"
30+ "github.com/ethereum/go-ethereum/crypto"
31+ "github.com/ethereum/go-ethereum/params"
32+ "github.com/ethereum/go-ethereum/triedb"
33+ "github.com/holiman/uint256"
34+ )
35+
36+ // setupTestStateWithStorage creates a test state with specified number of accounts and storage slots
37+ func setupTestStateWithStorage (accounts int , slotsPerAccount int ) (* state.StateDB , []common.Address ) {
38+ db := rawdb .NewMemoryDatabase ()
39+ trieConfig := triedb .HashDefaults
40+ tdb := triedb .NewDatabase (db , trieConfig )
41+ defer tdb .Close ()
42+
43+ statedb , _ := state .New (types .EmptyRootHash , state .NewDatabase (tdb , nil ))
44+
45+ addresses := make ([]common.Address , accounts )
46+
47+ // Create accounts with storage
48+ for i := 0 ; i < accounts ; i ++ {
49+ addr := common .BigToAddress (big .NewInt (int64 (i + 1 )))
50+ addresses [i ] = addr
51+
52+ statedb .CreateAccount (addr )
53+ statedb .AddBalance (addr , uint256 .NewInt (1000000000000000000 ), tracing .BalanceIncreaseGenesisBalance ) // 1 ETH
54+
55+ // Add storage slots to each account
56+ for j := 0 ; j < slotsPerAccount ; j ++ {
57+ key := common .BigToHash (big .NewInt (int64 (j )))
58+ value := common .BigToHash (big .NewInt (int64 (j + 1 )))
59+ statedb .SetState (addr , key , value )
60+ }
61+ }
62+
63+ statedb .Finalise (true )
64+ return statedb , addresses
65+ }
66+
67+ // createTestGenesis creates a genesis block with EIP-8032 enabled
68+ func createTestGenesis () * Genesis {
69+ return & Genesis {
70+ Config : func () * params.ChainConfig {
71+ config := * params .MergedTestChainConfig
72+ config .EIP8032Time = new (uint64 ) // Activate immediately
73+ * config .EIP8032Time = 0
74+ return & config
75+ }(),
76+ Alloc : types.GenesisAlloc {
77+ crypto .PubkeyToAddress (testKey .PublicKey ): types.Account {
78+ Balance : big .NewInt (1000000000000000000 ),
79+ },
80+ },
81+ }
82+ }
83+
84+ var testKey , _ = crypto .HexToECDSA ("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" )
85+
86+ func BenchmarkEIP8032Transition_Stride100 (b * testing.B ) {
87+ benchmarkEIP8032Transition (b , 100 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
88+ }
89+
90+ func BenchmarkEIP8032Transition_Stride500 (b * testing.B ) {
91+ benchmarkEIP8032Transition (b , 500 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
92+ }
93+
94+ func BenchmarkEIP8032Transition_Stride1000 (b * testing.B ) {
95+ benchmarkEIP8032Transition (b , 1000 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
96+ }
97+
98+ func BenchmarkEIP8032Transition_Stride2500 (b * testing.B ) {
99+ benchmarkEIP8032Transition (b , 2500 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
100+ }
101+
102+ func BenchmarkEIP8032Transition_Stride5000 (b * testing.B ) {
103+ benchmarkEIP8032Transition (b , 5000 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
104+ }
105+
106+ func BenchmarkEIP8032Transition_Stride10000 (b * testing.B ) {
107+ benchmarkEIP8032Transition (b , 10000 , 100 , 10 ) // 100 accounts × 10 slots = 1,000 slots
108+ }
109+
110+ // Large state benchmarks
111+ func BenchmarkEIP8032Transition_Large_Stride100 (b * testing.B ) {
112+ benchmarkEIP8032Transition (b , 100 , 1000 , 100 ) // 1,000 accounts × 100 slots = 100,000 slots
113+ }
114+
115+ func BenchmarkEIP8032Transition_Large_Stride1000 (b * testing.B ) {
116+ benchmarkEIP8032Transition (b , 1000 , 1000 , 100 ) // 1,000 accounts × 100 slots = 100,000 slots
117+ }
118+
119+ func BenchmarkEIP8032Transition_Large_Stride5000 (b * testing.B ) {
120+ benchmarkEIP8032Transition (b , 5000 , 1000 , 100 ) // 1,000 accounts × 100 slots = 100,000 slots
121+ }
122+
123+ func BenchmarkEIP8032Transition_Large_Stride10000 (b * testing.B ) {
124+ benchmarkEIP8032Transition (b , 10000 , 1000 , 100 ) // 1,000 accounts × 100 slots = 100,000 slots
125+ }
126+
127+ func benchmarkEIP8032Transition (b * testing.B , strideSize uint64 , accounts int , slotsPerAccount int ) {
128+ // Setup
129+ genesis := createTestGenesis ()
130+ engine := beacon .New (ethash .NewFaker ())
131+
132+ // Generate chain with test state
133+ db , blocks , _ := GenerateChainWithGenesis (genesis , engine , 2 , func (i int , gen * BlockGen ) {
134+ if i == 0 {
135+ // First block: Create accounts with storage
136+ statedb := gen .statedb
137+
138+ for j := 0 ; j < accounts ; j ++ {
139+ addr := common .BigToAddress (big .NewInt (int64 (j + 1000 ))) // Avoid conflicts
140+ statedb .CreateAccount (addr )
141+
142+ // Add storage slots
143+ for k := 0 ; k < slotsPerAccount ; k ++ {
144+ key := common .BigToHash (big .NewInt (int64 (k )))
145+ value := common .BigToHash (big .NewInt (int64 (k + 1 )))
146+ statedb .SetState (addr , key , value )
147+ }
148+ }
149+ }
150+ if i == 1 {
151+ // Second block: Trigger EIP-8032 transition
152+ gen .OffsetTime (1 ) // Activate EIP-8032
153+ }
154+ })
155+
156+ // Create blockchain
157+ blockchain , err := NewBlockChain (db , genesis , engine , nil )
158+ if err != nil {
159+ b .Fatalf ("failed to create blockchain: %v" , err )
160+ }
161+ defer blockchain .Stop ()
162+
163+ // Insert first block to set up state
164+ if _ , err := blockchain .InsertChain (blocks [:1 ]); err != nil {
165+ b .Fatalf ("failed to insert setup block: %v" , err )
166+ }
167+
168+ // Get state after setup
169+ state , err := blockchain .State ()
170+ if err != nil {
171+ b .Fatalf ("failed to get state: %v" , err )
172+ }
173+
174+ // Reset timer and start benchmark
175+ b .ResetTimer ()
176+ b .ReportAllocs ()
177+
178+ // Run the benchmark
179+ for i := 0 ; i < b .N ; i ++ {
180+ // Create a copy of state for each iteration
181+ stateCopy := state .Copy ()
182+
183+ // Clear any existing storage counts to simulate pre-EIP8032 state
184+ for j := 0 ; j < accounts ; j ++ {
185+ addr := common .BigToAddress (big .NewInt (int64 (j + 1000 )))
186+ stateCopy .SetStorageCount (addr , 0 )
187+ }
188+
189+ // Run transition with the specified stride size
190+ totalProcessed := uint64 (0 )
191+ iterations := 0
192+
193+ for {
194+ processed , hasMore := stateCopy .ProcessEIP8032Transition (strideSize )
195+ totalProcessed += processed
196+ iterations ++
197+
198+ if ! hasMore {
199+ break
200+ }
201+
202+ // Limit iterations to prevent infinite loops in case of issues
203+ if iterations > 1000 {
204+ b .Fatalf ("too many iterations: %d" , iterations )
205+ }
206+ }
207+
208+ // Verify some slots were processed (basic sanity check)
209+ if totalProcessed == 0 && accounts > 0 && slotsPerAccount > 0 {
210+ b .Logf ("Warning: no slots processed in transition" )
211+ }
212+ }
213+
214+ // Calculate some metrics
215+ totalSlots := uint64 (accounts * slotsPerAccount )
216+ expectedIterations := (totalSlots + strideSize - 1 ) / strideSize // Ceiling division
217+
218+ b .Logf ("Stride: %d, Accounts: %d, Slots/Account: %d, Total Slots: %d, Expected Iterations: %d" ,
219+ strideSize , accounts , slotsPerAccount , totalSlots , expectedIterations )
220+ }
221+
222+ // BenchmarkEIP8032TransitionIntegration tests the full block processing with EIP-8032 transition
223+ func BenchmarkEIP8032TransitionIntegration (b * testing.B ) {
224+ genesis := createTestGenesis ()
225+ engine := beacon .New (ethash .NewFaker ())
226+
227+ // Test with medium-sized state
228+ accounts := 500
229+ slotsPerAccount := 20
230+
231+ b .ResetTimer ()
232+ b .ReportAllocs ()
233+
234+ for i := 0 ; i < b .N ; i ++ {
235+ b .StopTimer ()
236+
237+ // Generate chain with transition
238+ db , blocks , _ := GenerateChainWithGenesis (genesis , engine , 2 , func (i int , gen * BlockGen ) {
239+ if i == 0 {
240+ // Setup state
241+ statedb := gen .statedb
242+ for j := 0 ; j < accounts ; j ++ {
243+ addr := common .BigToAddress (big .NewInt (int64 (j + 2000 )))
244+ statedb .CreateAccount (addr )
245+
246+ for k := 0 ; k < slotsPerAccount ; k ++ {
247+ key := common .BigToHash (big .NewInt (int64 (k )))
248+ value := common .BigToHash (big .NewInt (int64 (k + 1 )))
249+ statedb .SetState (addr , key , value )
250+ }
251+ }
252+ }
253+ if i == 1 {
254+ gen .OffsetTime (1 ) // Trigger EIP-8032
255+ }
256+ })
257+
258+ blockchain , err := NewBlockChain (db , genesis , engine , nil )
259+ if err != nil {
260+ b .Fatalf ("failed to create blockchain: %v" , err )
261+ }
262+
263+ b .StartTimer ()
264+
265+ // Benchmark the block insertion (includes transition processing)
266+ if _ , err := blockchain .InsertChain (blocks ); err != nil {
267+ b .Fatalf ("failed to insert chain: %v" , err )
268+ }
269+
270+ b .StopTimer ()
271+ blockchain .Stop ()
272+ }
273+ }
0 commit comments