1+ from eth2spec .test .context import (
2+ spec_state_test ,
3+ with_eip7732_and_later ,
4+ )
5+ from eth2spec .test .helpers .block import (
6+ build_empty_block_for_next_slot ,
7+ )
8+ from eth2spec .test .helpers .fork_choice import (
9+ check_head_against_root ,
10+ get_anchor_root ,
11+ get_genesis_forkchoice_store_and_block ,
12+ on_tick_and_append_step ,
13+ output_head_check ,
14+ tick_and_add_block ,
15+ )
16+ from eth2spec .test .helpers .state import (
17+ state_transition_and_sign_block ,
18+ )
19+
20+
21+ def build_and_yield_execution_payload_envelope (spec , beacon_block ):
22+ """
23+ Build a SignedExecutionPayloadEnvelope for the given beacon block and yield it for testing.
24+ This simulates the builder revealing the execution payload after the beacon block.
25+ """
26+ # Get the execution payload header from the beacon block
27+ payload_header = beacon_block .body .signed_execution_payload_header .message
28+
29+ # Build execution payload from the header
30+ execution_payload = spec .ExecutionPayload (
31+ parent_hash = payload_header .parent_block_hash ,
32+ fee_recipient = payload_header .fee_recipient ,
33+ state_root = payload_header .state_root ,
34+ receipts_root = payload_header .receipts_root ,
35+ logs_bloom = payload_header .logs_bloom ,
36+ prev_randao = payload_header .prev_randao ,
37+ block_number = payload_header .block_number ,
38+ gas_limit = payload_header .gas_limit ,
39+ gas_used = payload_header .gas_used ,
40+ timestamp = payload_header .timestamp ,
41+ extra_data = payload_header .extra_data ,
42+ base_fee_per_gas = payload_header .base_fee_per_gas ,
43+ block_hash = payload_header .block_hash ,
44+ transactions = [], # Empty for testing
45+ withdrawals = payload_header .withdrawals ,
46+ blob_gas_used = payload_header .blob_gas_used ,
47+ excess_blob_gas = payload_header .excess_blob_gas ,
48+ )
49+
50+ # Create execution payload envelope
51+ envelope = spec .ExecutionPayloadEnvelope (
52+ beacon_block_root = spec .hash_tree_root (beacon_block ),
53+ payload = execution_payload ,
54+ builder_index = spec .ValidatorIndex (0 ), # Use validator 0 as builder for testing
55+ blob_kzg_commitments = [], # Empty for basic test
56+ payload_withheld = False ,
57+ )
58+
59+ # Sign the envelope (empty signature for testing)
60+ signed_envelope = spec .SignedExecutionPayloadEnvelope (
61+ message = envelope ,
62+ signature = spec .BLSSignature (),
63+ )
64+
65+ return signed_envelope
66+
67+
68+ def process_execution_payload_reveal (spec , store , signed_block , test_steps ):
69+ """
70+ Process execution payload reveal for a beacon block in EIP7732.
71+ This simulates the full flow: beacon block -> execution payload reveal -> store update.
72+ """
73+ # Build and yield the execution payload envelope
74+ signed_envelope = build_and_yield_execution_payload_envelope (spec , signed_block .message )
75+
76+ # Yield the execution payload envelope for test file generation
77+ envelope_name = f"execution_payload_envelope_{ spec .hash_tree_root (signed_block .message ).hex ()[:8 ]} "
78+ yield envelope_name , signed_envelope
79+
80+ # Process the execution payload envelope through the store
81+ spec .on_execution_payload (store , signed_envelope )
82+
83+ # Add test step for execution payload processing
84+ test_steps .append ({
85+ "execution_payload" : envelope_name ,
86+ "valid" : True ,
87+ })
88+
89+ return signed_envelope
90+
91+
92+ @with_eip7732_and_later
93+ @spec_state_test
94+ def test_genesis (spec , state ):
95+ """Test genesis initialization with EIP7732 fork choice modifications"""
96+ test_steps = []
97+ # Initialization
98+ store , anchor_block = get_genesis_forkchoice_store_and_block (spec , state )
99+ yield "anchor_state" , state
100+ yield "anchor_block" , anchor_block
101+
102+ anchor_root = get_anchor_root (spec , state )
103+ check_head_against_root (spec , store , anchor_root )
104+
105+ # EIP7732-specific assertions
106+ assert hasattr (store , "execution_payload_states" ), (
107+ "Store should have execution_payload_states field"
108+ )
109+ assert hasattr (store , "ptc_vote" ), "Store should have ptc_vote field"
110+ assert anchor_root in store .execution_payload_states , (
111+ "Anchor block should be in execution_payload_states"
112+ )
113+ assert anchor_root in store .ptc_vote , "Anchor block should have ptc_vote entry"
114+
115+ # Check PTC vote initialization
116+ ptc_vote = store .ptc_vote [anchor_root ]
117+ assert len (ptc_vote ) == spec .PTC_SIZE , f"PTC vote should have { spec .PTC_SIZE } entries"
118+ assert all (vote == False for vote in ptc_vote ), "All PTC votes should be False initially"
119+
120+ # Verify get_head returns ForkChoiceNode
121+ head = spec .get_head (store )
122+ assert isinstance (head , spec .ForkChoiceNode ), "get_head should return ForkChoiceNode in EIP7732"
123+
124+ output_head_check (spec , store , test_steps )
125+
126+ yield "steps" , test_steps
127+
128+
129+ @with_eip7732_and_later
130+ @spec_state_test
131+ def test_basic (spec , state ):
132+ """Basic EIP7732 fork choice test - similar to phase0 test_basic but with payload status tracking"""
133+ test_steps = []
134+
135+ # Add EIP7732-specific metadata
136+ yield "test_scenario" , "meta" , "basic_fork_choice"
137+ yield "tests_payload_status" , "meta" , True
138+ yield "tests_execution_payload_states" , "meta" , True
139+
140+ # Initialization
141+ store , anchor_block = get_genesis_forkchoice_store_and_block (spec , state )
142+ yield "anchor_state" , state
143+ yield "anchor_block" , anchor_block
144+
145+ # Set initial time and record tick
146+ current_time = state .slot * spec .config .SECONDS_PER_SLOT + store .genesis_time
147+ on_tick_and_append_step (spec , store , current_time , test_steps )
148+ assert store .time == current_time
149+
150+ # Verify initial EIP7732 state
151+ anchor_root = get_anchor_root (spec , state )
152+ check_head_against_root (spec , store , anchor_root )
153+
154+ # Check initial head has PENDING payload status
155+ head = spec .get_head (store )
156+ assert head .payload_status == spec .PAYLOAD_STATUS_PENDING , "Initial head should have PENDING status"
157+
158+ # On receiving a block of `GENESIS_SLOT + 1` slot
159+ block = build_empty_block_for_next_slot (spec , state )
160+ signed_block = state_transition_and_sign_block (spec , state , block )
161+ yield from tick_and_add_block (spec , store , signed_block , test_steps )
162+
163+ # Verify block was added to both stores
164+ block_root = signed_block .message .hash_tree_root ()
165+ assert block_root in store .blocks , "Block should be in store.blocks"
166+ assert block_root in store .block_states , "Block should have block state"
167+ assert block_root in store .ptc_vote , "Block should have PTC vote entry"
168+
169+ # Head should now be the new block with PENDING status
170+ check_head_against_root (spec , store , block_root )
171+ head = spec .get_head (store )
172+ assert head .payload_status == spec .PAYLOAD_STATUS_PENDING , "New head should have PENDING status"
173+
174+ # Process execution payload reveal (simulates builder revealing payload)
175+ yield from process_execution_payload_reveal (spec , store , signed_block , test_steps )
176+
177+ # Verify block now has execution payload state
178+ assert block_root in store .execution_payload_states , "Block should now have execution payload state"
179+
180+ # On receiving a block of next slot
181+ store .time = current_time + spec .config .SECONDS_PER_SLOT * 2
182+ block_2 = build_empty_block_for_next_slot (spec , state )
183+ signed_block_2 = state_transition_and_sign_block (spec , state , block_2 )
184+ yield from tick_and_add_block (spec , store , signed_block_2 , test_steps )
185+
186+ # Process execution payload reveal for second block
187+ block_2_root = signed_block_2 .message .hash_tree_root ()
188+ check_head_against_root (spec , store , block_2_root )
189+ yield from process_execution_payload_reveal (spec , store , signed_block_2 , test_steps )
190+
191+ # Add EIP7732-specific checks to test steps
192+ test_steps .append ({
193+ "checks" : {
194+ "execution_payload_states_count" : len (store .execution_payload_states ),
195+ "blocks_with_ptc_votes" : len (store .ptc_vote ),
196+ "head_payload_status" : int (spec .get_head (store ).payload_status ),
197+ }
198+ })
199+
200+ yield "steps" , test_steps
0 commit comments