Skip to content

Commit a6e70cb

Browse files
committed
add transition + benchmarks
1 parent f427f56 commit a6e70cb

File tree

10 files changed

+1169
-1
lines changed

10 files changed

+1169
-1
lines changed

core/bench_eip8032_test.go

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
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

Comments
 (0)