77from collections .abc import Callable
88from random import Random
99
10- from eth2spec .test .helpers .blob import (
11- get_sample_blob_tx ,
12- )
10+ from eth2spec .test .helpers .blob import get_sample_blob_tx
1311from eth2spec .test .helpers .execution_payload import (
1412 build_randomized_execution_payload ,
1513 compute_el_block_hash_for_block ,
1614)
17- from eth2spec .test .helpers .inactivity_scores import (
18- randomize_inactivity_scores ,
15+ from eth2spec .test .helpers .forks import (
16+ is_post_altair ,
17+ is_post_bellatrix ,
18+ is_post_capella ,
19+ is_post_deneb ,
1920)
21+ from eth2spec .test .helpers .inactivity_scores import randomize_inactivity_scores
2022from eth2spec .test .helpers .multi_operations import (
2123 build_random_block_from_state_for_next_slot ,
2224 get_random_bls_to_execution_changes ,
3436 next_slot ,
3537 state_transition_and_sign_block ,
3638)
39+ from eth2spec .utils import bls
3740
3841# primitives:
3942# state
@@ -60,41 +63,179 @@ def _randomize_deposit_state(spec, state, stats):
6063 }
6164
6265
66+ def _randomize_phase0_fields (spec , state ):
67+ """Set Phase0-specific fields to realistic non-default values."""
68+
69+ rng = Random (8020 ) # same seed as other randomization functions
70+ current_epoch = spec .get_current_epoch (state )
71+
72+ # Randomize ETH1 data votes (simulate realistic ETH1 voting)
73+ if len (state .eth1_data_votes ) == 0 :
74+ num_votes = rng .randint (1 , min (10 , spec .EPOCHS_PER_ETH1_VOTING_PERIOD ))
75+ for i in range (num_votes ):
76+ eth1_data = spec .Eth1Data (
77+ deposit_root = rng .randbytes (32 ),
78+ deposit_count = rng .randint (1 , 1000 ),
79+ block_hash = rng .randbytes (32 ),
80+ )
81+ state .eth1_data_votes .append (eth1_data )
82+
83+ # Randomize historical roots
84+ if current_epoch > 0 and len (state .historical_roots ) == 0 :
85+ num_historical = rng .randint (0 , min (3 , current_epoch ))
86+ for i in range (num_historical ):
87+ state .historical_roots .append (rng .randbytes (32 ))
88+
89+ # Randomize RANDAO mixes
90+ for i in range (min (len (state .randao_mixes ), spec .EPOCHS_PER_HISTORICAL_VECTOR )):
91+ if state .randao_mixes [i ] == b"\x00 " * 32 : # Only modify empty ones
92+ state .randao_mixes [i ] = rng .randbytes (32 )
93+
94+ # Add some slashing penalties
95+ current_epoch_index = current_epoch % spec .EPOCHS_PER_SLASHINGS_VECTOR
96+ if state .slashings [current_epoch_index ] == 0 :
97+ penalty = spec .EFFECTIVE_BALANCE_INCREMENT * rng .randint (0 , 10 )
98+ state .slashings [current_epoch_index ] = penalty
99+
100+
101+ def _randomize_altair_fields (spec , state ):
102+ """Set Altair-specific fields to realistic non-default values."""
103+ if not is_post_altair (spec ):
104+ return
105+
106+ rng = Random (4242 ) # consistent seed with inactivity scores
107+
108+ # Simulate sync committee rotation to catch transition bugs
109+ if hasattr (state , "current_sync_committee" ) and hasattr (state , "next_sync_committee" ):
110+ current_epoch = spec .get_current_epoch (state )
111+ active_validators = spec .get_active_validator_indices (state , current_epoch )
112+
113+ if len (active_validators ) >= spec .SYNC_COMMITTEE_SIZE :
114+ shuffled_validators = list (active_validators )
115+ rng .shuffle (shuffled_validators )
116+ next_committee_indices = shuffled_validators [: spec .SYNC_COMMITTEE_SIZE ]
117+ next_pubkeys = [state .validators [i ].pubkey for i in next_committee_indices ]
118+ state .next_sync_committee .pubkeys = next_pubkeys
119+
120+ if next_pubkeys :
121+ state .next_sync_committee .aggregate_pubkey = bls .AggregatePKs (next_pubkeys )
122+
123+
124+ def _randomize_bellatrix_fields (spec , state ):
125+ """Set Bellatrix-specific fields to realistic non-default values."""
126+ if not is_post_bellatrix (spec ):
127+ return
128+
129+ rng = Random (3456 ) # consistent seed with block randomization
130+
131+ if hasattr (state , "latest_execution_payload_header" ):
132+ empty_header = spec .ExecutionPayloadHeader ()
133+ if state .latest_execution_payload_header == empty_header :
134+ state .latest_execution_payload_header = spec .ExecutionPayloadHeader (
135+ parent_hash = rng .randbytes (32 ),
136+ fee_recipient = rng .randbytes (20 ),
137+ state_root = rng .randbytes (32 ),
138+ receipts_root = rng .randbytes (32 ),
139+ logs_bloom = rng .randbytes (spec .BYTES_PER_LOGS_BLOOM ),
140+ prev_randao = rng .randbytes (32 ),
141+ block_number = rng .randint (1 , 1000000 ),
142+ gas_limit = rng .randint (8000000 , 30000000 ),
143+ gas_used = rng .randint (100000 , 15000000 ),
144+ timestamp = rng .randint (1609459200 , 2000000000 ),
145+ extra_data = rng .randbytes (rng .randint (0 , 32 )),
146+ base_fee_per_gas = rng .randint (1 , 100000000000 ),
147+ block_hash = rng .randbytes (32 ),
148+ transactions_root = rng .randbytes (32 ),
149+ )
150+
151+
152+ def _randomize_capella_fields (spec , state ):
153+ """Set Capella-specific fields to realistic non-default values."""
154+ if not is_post_capella (spec ):
155+ return
156+
157+ rng = Random (7890 )
158+
159+ # Randomize withdrawal credentials to simulate realistic validator states
160+ if hasattr (state , "validators" ):
161+ num_validators = len (state .validators )
162+
163+ # Set some validators to have ETH1 withdrawal credentials (0x01 prefix)
164+ # to simulate realistic pre-Capella state where some validators haven't
165+ # updated their credentials yet
166+ for i in range (min (num_validators , 20 )):
167+ validator = state .validators [i ]
168+
169+ # ~30% chance to set ETH1 withdrawal credentials
170+ if rng .random () < 0.3 :
171+ eth1_address = rng .randbytes (20 )
172+ validator .withdrawal_credentials = b"\x01 " + b"\x00 " * 11 + eth1_address
173+
174+
175+ def _randomize_deneb_fields (spec , state ):
176+ """Set Deneb-specific fields to realistic non-default values."""
177+ if not is_post_deneb (spec ):
178+ return
179+
180+ rng = Random (9999 )
181+
182+ if hasattr (state , "historical_summaries" ) and len (state .historical_summaries ) == 0 :
183+ current_epoch = spec .get_current_epoch (state )
184+ num_summaries = rng .randint (0 , min (3 , current_epoch // 100 ))
185+
186+ for i in range (num_summaries ):
187+ historical_summary = spec .HistoricalSummary (
188+ block_summary_root = rng .randbytes (32 ),
189+ state_summary_root = rng .randbytes (32 ),
190+ )
191+ state .historical_summaries .append (historical_summary )
192+
193+
194+ def randomize_state_phase0 (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
195+ scenario_state = randomize_state (
196+ spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
197+ )
198+
199+ _randomize_phase0_fields (spec , state )
200+ return scenario_state
201+
202+
63203def randomize_state (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
64204 randomize_state_helper (spec , state , exit_fraction = exit_fraction , slash_fraction = slash_fraction )
65205 scenario_state = _randomize_deposit_state (spec , state , stats )
66206 return scenario_state
67207
68208
69209def randomize_state_altair (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
70- scenario_state = randomize_state (
210+ scenario_state = randomize_state_phase0 (
71211 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
72212 )
73213 randomize_inactivity_scores (spec , state )
214+ _randomize_altair_fields (spec , state )
74215 return scenario_state
75216
76217
77218def randomize_state_bellatrix (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
78219 scenario_state = randomize_state_altair (
79220 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
80221 )
81- # TODO: randomize execution payload, merge status, etc.
222+ _randomize_bellatrix_fields ( spec , state )
82223 return scenario_state
83224
84225
85226def randomize_state_capella (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
86227 scenario_state = randomize_state_bellatrix (
87228 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
88229 )
89- # TODO: randomize withdrawals
230+ _randomize_capella_fields ( spec , state )
90231 return scenario_state
91232
92233
93234def randomize_state_deneb (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
94235 scenario_state = randomize_state_capella (
95236 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
96237 )
97- # TODO: randomize execution payload
238+ _randomize_deneb_fields ( spec , state )
98239 return scenario_state
99240
100241
0 commit comments