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 ,
@@ -60,6 +62,148 @@ def _randomize_deposit_state(spec, state, stats):
6062 }
6163
6264
65+ def _randomize_phase0_fields (spec , state ):
66+ """Set Phase0-specific fields to realistic non-default values."""
67+ if is_post_altair (spec ):
68+ return
69+
70+ rng = Random (8020 ) # same seed as other randomization functions
71+ current_epoch = spec .get_current_epoch (state )
72+
73+ # Randomize ETH1 data votes (simulate realistic ETH1 voting)
74+ if len (state .eth1_data_votes ) == 0 :
75+ num_votes = rng .randint (1 , min (10 , spec .EPOCHS_PER_ETH1_VOTING_PERIOD ))
76+ for i in range (num_votes ):
77+ eth1_data = spec .Eth1Data (
78+ deposit_root = rng .randbytes (32 ),
79+ deposit_count = rng .randint (1 , 1000 ),
80+ block_hash = rng .randbytes (32 ),
81+ )
82+ state .eth1_data_votes .append (eth1_data )
83+
84+ # Randomize historical roots
85+ if current_epoch > 0 and len (state .historical_roots ) == 0 :
86+ num_historical = rng .randint (0 , min (3 , current_epoch ))
87+ for i in range (num_historical ):
88+ state .historical_roots .append (rng .randbytes (32 ))
89+
90+ # Randomize RANDAO mixes
91+ for i in range (min (len (state .randao_mixes ), spec .EPOCHS_PER_HISTORICAL_VECTOR )):
92+ if state .randao_mixes [i ] == b"\x00 " * 32 : # Only modify empty ones
93+ state .randao_mixes [i ] = rng .randbytes (32 )
94+
95+ # Add some slashing penalties
96+ current_epoch_index = current_epoch % spec .EPOCHS_PER_SLASHINGS_VECTOR
97+ if state .slashings [current_epoch_index ] == 0 :
98+ penalty = spec .EFFECTIVE_BALANCE_INCREMENT * rng .randint (0 , 10 )
99+ state .slashings [current_epoch_index ] = penalty
100+
101+
102+ def _randomize_altair_fields (spec , state ):
103+ """Set Altair-specific fields to realistic non-default values."""
104+ if not is_post_altair (spec ):
105+ return
106+
107+ rng = Random (4242 ) # consistent seed with inactivity scores
108+
109+ # Simulate sync committee rotation to catch transition bugs
110+ if hasattr (state , "current_sync_committee" ) and hasattr (state , "next_sync_committee" ):
111+ current_epoch = spec .get_current_epoch (state )
112+ active_validators = spec .get_active_validator_indices (state , current_epoch )
113+
114+ if len (active_validators ) >= spec .SYNC_COMMITTEE_SIZE :
115+ shuffled_validators = list (active_validators )
116+ rng .shuffle (shuffled_validators )
117+ next_committee_indices = shuffled_validators [: spec .SYNC_COMMITTEE_SIZE ]
118+ next_pubkeys = [state .validators [i ].pubkey for i in next_committee_indices ]
119+ state .next_sync_committee .pubkeys = next_pubkeys
120+
121+ if next_pubkeys :
122+ state .next_sync_committee .aggregate_pubkey = rng .randbytes (48 )
123+
124+
125+ def _randomize_bellatrix_fields (spec , state ):
126+ """Set Bellatrix-specific fields to realistic non-default values."""
127+ if not is_post_bellatrix (spec ):
128+ return
129+
130+ rng = Random (3456 ) # Use consistent seed with block randomization
131+
132+ if hasattr (state , "latest_execution_payload_header" ):
133+ empty_header = spec .ExecutionPayloadHeader ()
134+ if state .latest_execution_payload_header == empty_header :
135+ state .latest_execution_payload_header = spec .ExecutionPayloadHeader (
136+ parent_hash = rng .randbytes (32 ),
137+ fee_recipient = rng .randbytes (20 ),
138+ state_root = rng .randbytes (32 ),
139+ receipts_root = rng .randbytes (32 ),
140+ logs_bloom = rng .randbytes (spec .BYTES_PER_LOGS_BLOOM ),
141+ prev_randao = rng .randbytes (32 ),
142+ block_number = rng .randint (1 , 1000000 ),
143+ gas_limit = rng .randint (8000000 , 30000000 ),
144+ gas_used = rng .randint (100000 , 15000000 ),
145+ timestamp = rng .randint (1609459200 , 2000000000 ),
146+ extra_data = rng .randbytes (rng .randint (0 , 32 )),
147+ base_fee_per_gas = rng .randint (
148+ 1 , 100000000000
149+ ),
150+ block_hash = rng .randbytes (32 ),
151+ transactions_root = rng .randbytes (32 ),
152+ )
153+
154+
155+ def _randomize_capella_fields (spec , state ):
156+ """Set Capella-specific fields to realistic non-default values."""
157+ if not is_post_capella (spec ):
158+ return
159+
160+ rng = Random (7890 ) # Use consistent seed
161+
162+ # Randomize withdrawal credentials to simulate realistic validator states
163+ if hasattr (state , "validators" ):
164+ num_validators = len (state .validators )
165+
166+ # Set some validators to have ETH1 withdrawal credentials (0x01 prefix)
167+ # to simulate realistic pre-Capella state where some validators haven't
168+ # updated their credentials yet
169+ for i in range (min (num_validators , 20 )):
170+ validator = state .validators [i ]
171+
172+ # ~30% chance to set ETH1 withdrawal credentials
173+ if rng .random () < 0.3 :
174+ eth1_address = rng .randbytes (20 )
175+ validator .withdrawal_credentials = b"\x01 " + b"\x00 " * 11 + eth1_address
176+
177+
178+ def _randomize_deneb_fields (spec , state ):
179+ """Set Deneb-specific fields to realistic non-default values."""
180+ if not is_post_deneb (spec ):
181+ return
182+
183+ rng = Random (9999 ) # Use consistent seed
184+
185+ if hasattr (state , "historical_summaries" ) and len (state .historical_summaries ) == 0 :
186+ current_epoch = spec .get_current_epoch (state )
187+ num_summaries = rng .randint (0 , min (3 , current_epoch // 100 ))
188+
189+ for i in range (num_summaries ):
190+ historical_summary = spec .HistoricalSummary (
191+ block_summary_root = rng .randbytes (32 ),
192+ state_summary_root = rng .randbytes (32 ),
193+ )
194+ state .historical_summaries .append (historical_summary )
195+
196+
197+ def randomize_state_phase0 (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
198+ scenario_state = randomize_state (
199+ spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
200+ )
201+
202+ # Initialize Phase0-specific randomization
203+ _randomize_phase0_fields (spec , state )
204+ return scenario_state
205+
206+
63207def randomize_state (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
64208 randomize_state_helper (spec , state , exit_fraction = exit_fraction , slash_fraction = slash_fraction )
65209 scenario_state = _randomize_deposit_state (spec , state , stats )
@@ -71,30 +215,31 @@ def randomize_state_altair(spec, state, stats, exit_fraction=0.1, slash_fraction
71215 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
72216 )
73217 randomize_inactivity_scores (spec , state )
218+ _randomize_altair_fields (spec , state )
74219 return scenario_state
75220
76221
77222def randomize_state_bellatrix (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
78223 scenario_state = randomize_state_altair (
79224 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
80225 )
81- # TODO: randomize execution payload, merge status, etc.
226+ _randomize_bellatrix_fields ( spec , state )
82227 return scenario_state
83228
84229
85230def randomize_state_capella (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
86231 scenario_state = randomize_state_bellatrix (
87232 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
88233 )
89- # TODO: randomize withdrawals
234+ _randomize_capella_fields ( spec , state )
90235 return scenario_state
91236
92237
93238def randomize_state_deneb (spec , state , stats , exit_fraction = 0.1 , slash_fraction = 0.1 ):
94239 scenario_state = randomize_state_capella (
95240 spec , state , stats , exit_fraction = exit_fraction , slash_fraction = slash_fraction
96241 )
97- # TODO: randomize execution payload
242+ _randomize_deneb_fields ( spec , state )
98243 return scenario_state
99244
100245
0 commit comments