@@ -4,15 +4,12 @@ use std::{
44 sync:: Arc ,
55} ;
66
7- use ethlambda_blockchain:: { MILLISECONDS_PER_INTERVAL , MILLISECONDS_PER_SLOT , store} ;
7+ use ethlambda_blockchain:: { spec_test_runner :: apply_fork_choice_step , store} ;
88use ethlambda_storage:: { Store , backend:: InMemoryBackend } ;
99use ethlambda_types:: {
10- attestation:: {
11- AttestationData , HashedAttestationData , SignedAggregatedAttestation , SignedAttestation ,
12- validator_indices,
13- } ,
14- block:: { Block , SingleMessageAggregate } ,
15- primitives:: { ByteList , H256 , HashTreeRoot as _} ,
10+ attestation:: { AttestationData , validator_indices} ,
11+ block:: Block ,
12+ primitives:: { H256 , HashTreeRoot as _} ,
1613 state:: { State , anchor_pair_is_consistent} ,
1714} ;
1815
@@ -57,8 +54,6 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
5754 // `get_forkchoice_store`'s assert! panic out of the test harness.
5855 let mut anchor_state: State = test. anchor_state . into ( ) ;
5956 let anchor_block: Block = test. anchor_block . into ( ) ;
60- let genesis_time = anchor_state. config . genesis_time ;
61-
6257 let pair_ok = anchor_pair_is_consistent ( & mut anchor_state, & anchor_block) ;
6358 if test. steps . is_empty ( ) {
6459 if pair_ok {
@@ -107,132 +102,16 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
107102 let old_head = store. head ( ) ?;
108103 // Block built/imported this step, for the block-body checks. Mirrors
109104 // leanSpec's per-step `filled_block` (only a block step sets it).
110- let mut filled_block: Option < Block > = None ;
111- match step. step_type . as_str ( ) {
112- "block" => {
113- let block_data = step. block . expect ( "block step missing block data" ) ;
114-
115- // Register block label if present
116- if let Some ( ref label) = block_data. block_root_label {
117- let block: Block = block_data. to_block ( ) ;
118- let root = block. hash_tree_root ( ) ;
119- block_registry. insert ( label. clone ( ) , root) ;
120- }
121-
122- // The block this step delivers is the leanSpec `filled_block`.
123- filled_block = Some ( block_data. to_block ( ) ) ;
124-
125- let signed_block = block_data. to_blank_signed_block ( ) ;
126-
127- // Advance time to the block's slot unless the test delivers
128- // the block ahead of the store clock.
129- // NOTE: the has_proposal argument is set to true, following the spec
130- if step. tick_to_slot {
131- let block_time_ms =
132- genesis_time * 1000 + signed_block. message . slot * MILLISECONDS_PER_SLOT ;
133- store:: on_tick ( & mut store, block_time_ms, true ) ;
134- }
135- let result = store:: on_block_without_verification ( & mut store, signed_block) ;
136- let import_ok = result. is_ok ( ) ;
137- assert_step_outcome ( step_idx, step. valid , result) ?;
138-
139- // Deconstruct the imported block into per-attestation
140- // single-message aggregates, mirroring the node's post-import
141- // reaggregation. The real node SNARK-splits the block's merged
142- // multi-message aggregate proof and folds the recovered
143- // single-message aggregates into the pool so block-borne votes carry
144- // fork-choice weight; leanSpec's fork-choice harness gets the
145- // same effect by simulating the proposer build. Fixture blocks
146- // are blank (no real proof to split), so reconstruct structurally
147- // from the body's aggregation_bits — fork choice reads only the
148- // participant set, not the proof bytes. The recovered entries go
149- // straight into the known pool to match the proposer-view store
150- // the fixtures encode.
151- if import_ok {
152- let block = block_data. to_block ( ) ;
153- let entries: Vec < ( HashedAttestationData , SingleMessageAggregate ) > = block
154- . body
155- . attestations
156- . iter ( )
157- . map ( |att| {
158- (
159- HashedAttestationData :: new ( att. data . clone ( ) ) ,
160- SingleMessageAggregate :: empty ( att. aggregation_bits . clone ( ) ) ,
161- )
162- } )
163- . collect ( ) ;
164- store. insert_known_aggregated_payloads_batch ( entries) ;
165- // on_block already ran the head update before these votes
166- // existed; recompute so the head reflects the block's own
167- // attestations, matching the proposer-view store.
168- store:: update_head ( & mut store, false ) ;
169- }
170- }
171- "tick" => {
172- // Fixtures use either `time` (UNIX seconds) or `interval`
173- // (absolute interval count since genesis). Interval fixtures
174- // encode `genesis_time_ms + interval * MILLISECONDS_PER_INTERVAL`.
175- let timestamp_ms = match ( step. time , step. interval ) {
176- ( Some ( time_s) , _) => time_s * 1000 ,
177- ( None , Some ( interval) ) => {
178- genesis_time * 1000 + interval * MILLISECONDS_PER_INTERVAL
179- }
180- ( None , None ) => panic ! ( "tick step missing both time and interval" ) ,
181- } ;
182- let has_proposal = step. has_proposal . unwrap_or ( false ) ;
183- store:: on_tick ( & mut store, timestamp_ms, has_proposal) ;
184- }
185- "attestation" => {
186- let att_data = step
187- . attestation
188- . expect ( "attestation step missing attestation data" ) ;
189- let signed_attestation = SignedAttestation {
190- validator_id : att_data
191- . validator_id
192- . expect ( "attestation step missing validator_id" ) ,
193- data : att_data. data . into ( ) ,
194- signature : att_data
195- . signature
196- . expect ( "attestation step missing signature" ) ,
197- } ;
198- let is_aggregator = step. is_aggregator . unwrap_or ( false ) ;
199-
200- let result = store:: on_gossip_attestation (
201- & mut store,
202- & signed_attestation,
203- is_aggregator,
204- ) ;
205- assert_step_outcome ( step_idx, step. valid , result) ?;
206- }
207- "gossipAggregatedAttestation" => {
208- let att_data = step
209- . attestation
210- . expect ( "gossipAggregatedAttestation step missing attestation data" ) ;
211- let proof_fixture = att_data
212- . proof
213- . expect ( "gossipAggregatedAttestation step missing proof" ) ;
214- let proof_bytes: Vec < u8 > = proof_fixture. proof . into ( ) ;
215- let proof_data = ByteList :: try_from ( proof_bytes)
216- . expect ( "aggregated proof data fits in ByteList512KiB" ) ;
217- let data: AttestationData = att_data. data . into ( ) ;
218- let proof =
219- SingleMessageAggregate :: new ( proof_fixture. participants . into ( ) , proof_data) ;
220- let aggregated = SignedAggregatedAttestation { data, proof } ;
221-
222- let result = if proofs_are_mocked {
223- store:: on_gossip_aggregated_attestation_without_verification (
224- & mut store, aggregated,
225- )
226- } else {
227- store:: on_gossip_aggregated_attestation ( & mut store, aggregated)
228- } ;
229- assert_step_outcome ( step_idx, step. valid , result) ?;
230- }
231- other => {
232- return Err ( format ! ( "Unsupported step type '{other}'" ) . into ( ) ) ;
233- }
105+ let filled_block = step. block . as_ref ( ) . map ( |block_data| block_data. to_block ( ) ) ;
106+ if let Some ( block_data) = step. block . as_ref ( )
107+ && let Some ( label) = block_data. block_root_label . as_ref ( )
108+ {
109+ block_registry. insert ( label. clone ( ) , block_data. to_block ( ) . hash_tree_root ( ) ) ;
234110 }
235111
112+ let result = apply_fork_choice_step ( & mut store, & step, Some ( proofs_are_mocked) ) ;
113+ assert_step_outcome ( step_idx, step. valid , result) ?;
114+
236115 // Fold this step's blocks into the cumulative tree before checks so
237116 // ancestry walks see blocks finalization may have just pruned from
238117 // the live-chain index (see `all_blocks` above).
0 commit comments