Skip to main content

ab_client_block_verification/
beacon_chain.rs

1use crate::{BlockVerification, BlockVerificationError, GenericBody, GenericHeader};
2use ab_client_api::{BeaconChainInfo, BlockOrigin, ChainSyncStatus};
3use ab_client_consensus_common::ConsensusConstants;
4use ab_client_consensus_common::consensus_parameters::{
5    DeriveConsensusParametersChainInfo, DeriveConsensusParametersError,
6    DeriveSuperSegmentForBlockError, ShardMembershipEntropySourceChainInfo,
7    derive_consensus_parameters, derive_super_segments_for_block, shard_membership_entropy_source,
8};
9use ab_client_proof_of_time::PotNextSlotInput;
10use ab_client_proof_of_time::verifier::PotVerifier;
11use ab_core_primitives::block::body::{BeaconChainBody, IntermediateShardBlocksInfo, OwnSegments};
12use ab_core_primitives::block::header::{
13    BeaconChainHeader, BlockHeaderConsensusParameters, BlockHeaderPrefix,
14    OwnedBlockHeaderConsensusParameters,
15};
16use ab_core_primitives::block::owned::OwnedBeaconChainBlock;
17use ab_core_primitives::block::{BlockNumber, BlockRoot, BlockTimestamp};
18use ab_core_primitives::hashes::Blake3Hash;
19use ab_core_primitives::pot::{PotCheckpoints, PotOutput, PotParametersChange, SlotNumber};
20use ab_core_primitives::segments::{
21    HistorySize, SuperSegment, SuperSegmentIndex, SuperSegmentRoot,
22};
23use ab_core_primitives::shard::ShardIndex;
24use ab_core_primitives::solutions::{
25    SolutionVerifyError, SolutionVerifyPieceParams, SolutionVerifyStatelessParams,
26};
27use ab_proof_of_space::Table;
28use rand::prelude::*;
29use rayon::prelude::*;
30use std::future::ready;
31use std::iter;
32use std::marker::PhantomData;
33use std::time::SystemTime;
34use tracing::{debug, trace};
35
36/// Errors for [`BeaconChainBlockVerification`]
37#[derive(Debug, thiserror::Error)]
38pub enum BeaconChainBlockVerificationError {
39    /// Consensus parameters derivation error
40    #[error("Consensus parameters derivation error: {error}")]
41    ConsensusParametersDerivation {
42        /// Consensus parameters derivation error
43        #[from]
44        error: DeriveConsensusParametersError,
45    },
46    /// Super segment derivation error
47    #[error("Super segment derivation error: {error}")]
48    SuperSegmentDerivation {
49        /// Super segment derivation error
50        #[from]
51        error: DeriveSuperSegmentForBlockError,
52    },
53    /// Invalid consensus parameters
54    #[error("Invalid consensus parameters: expected {expected:?}, actual {actual:?}")]
55    InvalidConsensusParameters {
56        /// Expected consensus parameters
57        expected: Box<OwnedBlockHeaderConsensusParameters>,
58        /// Actual consensus parameters
59        actual: Box<OwnedBlockHeaderConsensusParameters>,
60    },
61    /// Missing a super segment in the first block
62    #[error("Missing super segment in the first block")]
63    MissingSuperSegmentInFirstBlock,
64    /// Previous super segment header not found
65    #[error("Previous super segment header not found")]
66    PreviousSuperSegmentHeaderNotFound,
67    /// Solution super segment not found
68    #[error("Solution super segment {index} not found")]
69    SolutionSuperSegmentNotFound {
70        /// Expected super segment index
71        index: SuperSegmentIndex,
72    },
73    /// Invalid history size
74    #[error("Invalid history size {history_size} (current {current_history_size})")]
75    InvalidHistorySize {
76        /// History size in the solution
77        history_size: HistorySize,
78        /// Current history size
79        current_history_size: HistorySize,
80    },
81    /// Invalid super segment root
82    #[error("Invalid super segment root: expected {expected:?}, actual {actual:?}")]
83    InvalidSuperSegmentRoot {
84        /// Expected super segment root
85        expected: Box<Option<SuperSegmentRoot>>,
86        /// Actual super segment root
87        actual: Box<Option<SuperSegmentRoot>>,
88    },
89    /// Invalid PoT checkpoints
90    #[error("Invalid PoT checkpoints")]
91    InvalidPotCheckpoints,
92    /// Invalid proof of time
93    #[error("Invalid proof of time")]
94    InvalidProofOfTime,
95    /// Solution error
96    #[error("Solution error: {error}")]
97    SolutionError {
98        /// Solution error
99        #[from]
100        error: SolutionVerifyError,
101    },
102}
103
104impl From<BeaconChainBlockVerificationError> for BlockVerificationError {
105    #[inline(always)]
106    fn from(error: BeaconChainBlockVerificationError) -> Self {
107        Self::Custom {
108            error: error.into(),
109        }
110    }
111}
112
113#[derive(Debug)]
114pub struct BeaconChainBlockVerification<PosTable, CI, CSS> {
115    consensus_constants: ConsensusConstants,
116    pot_verifier: PotVerifier,
117    chain_info: CI,
118    chain_sync_status: CSS,
119    _pos_table: PhantomData<PosTable>,
120}
121
122impl<PosTable, CI, CSS> BlockVerification<OwnedBeaconChainBlock, Option<SuperSegment>>
123    for BeaconChainBlockVerification<PosTable, CI, CSS>
124where
125    PosTable: Table,
126    CI: BeaconChainInfo,
127    CSS: ChainSyncStatus,
128{
129    #[inline(always)]
130    fn verify_concurrent<BCI>(
131        &self,
132        parent_header: &GenericHeader<'_, OwnedBeaconChainBlock>,
133        parent_block_mmr_root: &Blake3Hash,
134        header: &GenericHeader<'_, OwnedBeaconChainBlock>,
135        body: &GenericBody<'_, OwnedBeaconChainBlock>,
136        origin: &BlockOrigin,
137        beacon_chain_info: &BCI,
138    ) -> impl Future<Output = Result<(), BlockVerificationError>>
139    where
140        BCI: DeriveConsensusParametersChainInfo + ShardMembershipEntropySourceChainInfo,
141    {
142        ready(self.verify_concurrent(
143            parent_header,
144            parent_block_mmr_root,
145            header,
146            body,
147            origin,
148            beacon_chain_info,
149        ))
150    }
151
152    #[inline(always)]
153    fn verify_sequential(
154        &self,
155        parent_header: &GenericHeader<'_, OwnedBeaconChainBlock>,
156        parent_block_mmr_root: &Blake3Hash,
157        header: &GenericHeader<'_, OwnedBeaconChainBlock>,
158        body: &GenericBody<'_, OwnedBeaconChainBlock>,
159        origin: &BlockOrigin,
160    ) -> impl Future<Output = Result<Option<SuperSegment>, BlockVerificationError>> {
161        ready(self.verify_sequential(parent_header, parent_block_mmr_root, header, body, origin))
162    }
163}
164
165impl<PosTable, CI, CSS> BeaconChainBlockVerification<PosTable, CI, CSS>
166where
167    PosTable: Table,
168    CI: BeaconChainInfo,
169    CSS: ChainSyncStatus,
170{
171    /// Create a new instance
172    #[inline(always)]
173    pub fn new(
174        consensus_constants: ConsensusConstants,
175        pot_verifier: PotVerifier,
176        chain_info: CI,
177        chain_sync_status: CSS,
178    ) -> Self {
179        Self {
180            consensus_constants,
181            pot_verifier,
182            chain_info,
183            chain_sync_status,
184            _pos_table: PhantomData,
185        }
186    }
187
188    /// Determine if full proof of time verification is needed for this block number
189    fn full_pot_verification(&self, block_number: BlockNumber) -> bool {
190        let sync_target_block_number = self.chain_sync_status.target_block_number();
191        let Some(diff) = sync_target_block_number.checked_sub(block_number) else {
192            return true;
193        };
194        let diff = u64::from(diff);
195
196        let sample_size = match diff {
197            ..=1_581 => {
198                return true;
199            }
200            1_582..=6_234 => 1_581,
201            6_235..=63_240 => 3_162 * (diff - 3_162) / (diff - 1),
202            63_241..=3_162_000 => 3_162,
203            _ => diff / 1_000,
204        };
205
206        let n = rand::rng().random_range(0..=diff);
207
208        n < sample_size
209    }
210
211    fn check_header_prefix(
212        &self,
213        parent_header_prefix: &BlockHeaderPrefix,
214        parent_block_mmr_root: &Blake3Hash,
215        header_prefix: &BlockHeaderPrefix,
216    ) -> Result<(), BlockVerificationError> {
217        let basic_valid = header_prefix.number == parent_header_prefix.number + BlockNumber::ONE
218            && header_prefix.shard_index == parent_header_prefix.shard_index
219            && &header_prefix.mmr_root == parent_block_mmr_root
220            && header_prefix.timestamp > parent_header_prefix.timestamp;
221
222        if !basic_valid {
223            return Err(BlockVerificationError::InvalidHeaderPrefix);
224        }
225
226        let timestamp_now = SystemTime::now()
227            .duration_since(SystemTime::UNIX_EPOCH)
228            .unwrap_or_default()
229            .as_millis();
230        let timestamp_now =
231            BlockTimestamp::from_millis(u64::try_from(timestamp_now).unwrap_or(u64::MAX));
232
233        if header_prefix.timestamp
234            > timestamp_now.saturating_add(self.consensus_constants.max_block_timestamp_drift)
235        {
236            return Err(BlockVerificationError::TimestampTooFarInTheFuture);
237        }
238
239        Ok(())
240    }
241
242    fn check_consensus_parameters_concurrent<BCI>(
243        &self,
244        parent_block_root: &BlockRoot,
245        parent_header: &BeaconChainHeader<'_>,
246        header: &BeaconChainHeader<'_>,
247        beacon_chain_info: &BCI,
248    ) -> Result<(), BeaconChainBlockVerificationError>
249    where
250        BCI: DeriveConsensusParametersChainInfo,
251    {
252        let derived_consensus_parameters = derive_consensus_parameters(
253            &self.consensus_constants,
254            beacon_chain_info,
255            parent_block_root,
256            parent_header.consensus_parameters(),
257            parent_header.consensus_info.slot,
258            header.prefix.number,
259            header.consensus_info.slot,
260        )?;
261
262        let expected_consensus_parameters = OwnedBlockHeaderConsensusParameters {
263            fixed_parameters: derived_consensus_parameters.fixed_parameters,
264            // TODO: This field is verified separately in the sequential part
265            super_segment_root: header.consensus_parameters().super_segment_root.copied(),
266            next_solution_range: derived_consensus_parameters.next_solution_range,
267            pot_parameters_change: derived_consensus_parameters.pot_parameters_change,
268        };
269
270        if header.consensus_parameters() != &expected_consensus_parameters.as_ref() {
271            return Err(
272                BeaconChainBlockVerificationError::InvalidConsensusParameters {
273                    expected: Box::new(expected_consensus_parameters),
274                    actual: Box::new(OwnedBlockHeaderConsensusParameters {
275                        fixed_parameters: header.consensus_parameters().fixed_parameters,
276                        super_segment_root: header
277                            .consensus_parameters()
278                            .super_segment_root
279                            .copied(),
280                        next_solution_range: header.consensus_parameters().next_solution_range,
281                        pot_parameters_change: header
282                            .consensus_parameters()
283                            .pot_parameters_change
284                            .copied(),
285                    }),
286                },
287            );
288        }
289
290        Ok(())
291    }
292
293    // TODO: This is a blocking function, but ideally wouldn't be block an executor
294    /// Checks current/future proof of time in the consensus info for the slot and corresponding
295    /// checkpoints.
296    ///
297    /// `consensus_parameters` is assumed to be correct and needs to be verified separately.
298    ///
299    /// When `verify_checkpoints == false` checkpoints are assumed to be correct and verification
300    /// for them is skipped.
301    #[expect(
302        clippy::too_many_arguments,
303        reason = "Explicit minimal input for better testability"
304    )]
305    fn check_proof_of_time(
306        pot_verifier: &PotVerifier,
307        block_authoring_delay: SlotNumber,
308        parent_slot: SlotNumber,
309        parent_proof_of_time: PotOutput,
310        parent_future_proof_of_time: PotOutput,
311        parent_consensus_parameters: &BlockHeaderConsensusParameters<'_>,
312        slot: SlotNumber,
313        proof_of_time: PotOutput,
314        future_proof_of_time: PotOutput,
315        checkpoints: &[PotCheckpoints],
316        verify_checkpoints: bool,
317    ) -> Result<(), BeaconChainBlockVerificationError> {
318        let parent_pot_parameters_change = parent_consensus_parameters
319            .pot_parameters_change
320            .copied()
321            .map(PotParametersChange::from);
322
323        // The last checkpoint must be the future proof of time
324        if checkpoints.last().map(PotCheckpoints::output) != Some(future_proof_of_time) {
325            return Err(BeaconChainBlockVerificationError::InvalidPotCheckpoints);
326        }
327
328        let future_slot = slot + block_authoring_delay;
329        let parent_future_slot = if parent_slot == SlotNumber::ZERO {
330            parent_slot
331        } else {
332            parent_slot + block_authoring_delay
333        };
334
335        let slots_between_blocks = slot
336            .checked_sub(parent_slot)
337            .ok_or(BeaconChainBlockVerificationError::InvalidPotCheckpoints)?;
338        // The number of checkpoints must match the difference between parent's and this block's
339        // future slots. This also implicitly checks that there is a non-zero number of slots
340        // between this and parent block because the list of checkpoints is already known to be not
341        // empty from the check above.
342        //
343        // The first block after genesis is a special case and is handled separately here.
344        if !(u64::from(slots_between_blocks) == checkpoints.len() as u64
345            || (parent_slot == SlotNumber::ZERO
346                && u64::from(future_slot) == checkpoints.len() as u64))
347        {
348            return Err(BeaconChainBlockVerificationError::InvalidPotCheckpoints);
349        }
350
351        let mut pot_input = if parent_slot == SlotNumber::ZERO {
352            PotNextSlotInput {
353                slot: parent_slot + SlotNumber::ONE,
354                slot_iterations: parent_consensus_parameters.fixed_parameters.slot_iterations,
355                seed: pot_verifier.genesis_seed(),
356            }
357        } else {
358            // Calculate slot iterations as of parent future slot
359            let slot_iterations = parent_pot_parameters_change
360                .and_then(|parameters_change| {
361                    (parameters_change.slot <= parent_future_slot)
362                        .then_some(parameters_change.slot_iterations)
363                })
364                .unwrap_or(parent_consensus_parameters.fixed_parameters.slot_iterations);
365            // Derive inputs to the slot, which follows the parent future slot
366            PotNextSlotInput::derive(
367                slot_iterations,
368                parent_future_slot,
369                parent_future_proof_of_time,
370                &parent_pot_parameters_change,
371            )
372        };
373
374        // Collect all the data we will use for verification so we can process it in parallel
375        let checkpoints_verification_input = iter::once((
376            pot_input,
377            *checkpoints
378                .first()
379                .expect("Not empty, contents was checked above; qed"),
380        ));
381        let checkpoints_verification_input = checkpoints_verification_input
382            .chain(checkpoints.array_windows::<2>().map(|[left, right]| {
383                pot_input = PotNextSlotInput::derive(
384                    pot_input.slot_iterations,
385                    pot_input.slot,
386                    left.output(),
387                    &parent_pot_parameters_change,
388                );
389
390                (pot_input, *right)
391            }))
392            // TODO: Would be nice to avoid extra allocation here
393            .collect::<Vec<_>>();
394
395        // All checkpoints must be valid, search for the first verification failure
396        let all_checkpoints_valid =
397            checkpoints_verification_input
398                .into_par_iter()
399                .all(|(pot_input, checkpoints)| {
400                    if verify_checkpoints {
401                        pot_verifier.verify_checkpoints(
402                            pot_input.seed,
403                            pot_input.slot_iterations,
404                            &checkpoints,
405                        )
406                    } else {
407                        // Store checkpoints as verified when verification is skipped
408                        pot_verifier.inject_verified_checkpoints(
409                            pot_input.seed,
410                            pot_input.slot_iterations,
411                            checkpoints,
412                        );
413                        true
414                    }
415                });
416
417        if !all_checkpoints_valid {
418            return Err(BeaconChainBlockVerificationError::InvalidPotCheckpoints);
419        }
420
421        // Make sure proof of time of this block correctly extends proof of time of the parent block
422        {
423            let pot_input = if parent_slot == SlotNumber::ZERO {
424                PotNextSlotInput {
425                    slot: parent_slot + SlotNumber::ONE,
426                    slot_iterations: parent_consensus_parameters.fixed_parameters.slot_iterations,
427                    seed: pot_verifier.genesis_seed(),
428                }
429            } else {
430                // Calculate slot iterations as of the parent slot
431                let slot_iterations = parent_pot_parameters_change
432                    .and_then(|parameters_change| {
433                        (parameters_change.slot <= parent_slot)
434                            .then_some(parameters_change.slot_iterations)
435                    })
436                    .unwrap_or(parent_consensus_parameters.fixed_parameters.slot_iterations);
437                // Derive inputs to the slot, which follows the parent slot
438                PotNextSlotInput::derive(
439                    slot_iterations,
440                    parent_slot,
441                    parent_proof_of_time,
442                    &parent_pot_parameters_change,
443                )
444            };
445
446            if !pot_verifier.is_output_valid(
447                pot_input,
448                slots_between_blocks,
449                proof_of_time,
450                parent_pot_parameters_change,
451            ) {
452                return Err(BeaconChainBlockVerificationError::InvalidProofOfTime);
453            }
454        }
455
456        Ok(())
457    }
458
459    fn check_body(
460        &self,
461        block_number: BlockNumber,
462        own_segments: Option<OwnSegments<'_>>,
463        _intermediate_shard_blocks: &IntermediateShardBlocksInfo<'_>,
464    ) -> Result<(), BlockVerificationError> {
465        let expected_segment_headers = self.chain_info.segment_headers_for_block(block_number);
466        let expected_first_local_segment_index = expected_segment_headers
467            .first()
468            .map(|segment_header| segment_header.index.as_inner());
469        let correct_first_local_segment_index = expected_first_local_segment_index
470            == own_segments
471                .as_ref()
472                .map(|own_segments| own_segments.first_local_segment_index);
473        let correct_segment_roots = expected_segment_headers
474            .iter()
475            .map(|segment_header| &segment_header.root)
476            .eq(own_segments
477                .as_ref()
478                .map(|own_segments| own_segments.segment_roots)
479                .unwrap_or_default());
480        if !(correct_first_local_segment_index && correct_segment_roots) {
481            return Err(BlockVerificationError::InvalidOwnSegments {
482                expected_first_local_segment_index,
483                expected_segment_roots: expected_segment_headers
484                    .iter()
485                    .map(|segment_header| segment_header.root)
486                    .collect(),
487                actual_first_local_segment_index: own_segments
488                    .as_ref()
489                    .map(|own_segments| own_segments.first_local_segment_index),
490                actual_segment_roots: own_segments
491                    .as_ref()
492                    .map(|own_segments| own_segments.segment_roots.to_vec())
493                    .unwrap_or_default(),
494            });
495        }
496
497        // TODO: check intermediate shard blocks and all segment roots included in the body
498
499        Ok(())
500    }
501
502    fn verify_concurrent<BCI>(
503        &self,
504        parent_header: &BeaconChainHeader<'_>,
505        parent_block_mmr_root: &Blake3Hash,
506        header: &BeaconChainHeader<'_>,
507        body: &BeaconChainBody<'_>,
508        _origin: &BlockOrigin,
509        beacon_chain_info: &BCI,
510    ) -> Result<(), BlockVerificationError>
511    where
512        BCI: DeriveConsensusParametersChainInfo + ShardMembershipEntropySourceChainInfo,
513    {
514        trace!(header = ?header, "Verify concurrent");
515
516        let parent_block_root = parent_header.root();
517
518        let block_number = header.prefix.number;
519        let consensus_info = header.consensus_info;
520        let consensus_parameters = header.consensus_parameters();
521        let slot = consensus_info.slot;
522
523        let best_header = self.chain_info.best_header();
524        let best_header = best_header.header();
525        let best_number = best_header.prefix.number;
526
527        // Reject block below archiving point
528        if block_number + self.consensus_constants.block_confirmation_depth < best_number {
529            debug!(
530                ?header,
531                %best_number,
532                "Rejecting a block below the archiving point"
533            );
534
535            return Err(BlockVerificationError::BelowArchivingPoint);
536        }
537
538        self.check_header_prefix(parent_header.prefix, parent_block_mmr_root, header.prefix)?;
539
540        self.check_consensus_parameters_concurrent(
541            &parent_block_root,
542            parent_header,
543            header,
544            beacon_chain_info,
545        )?;
546
547        if !header.is_sealed_correctly() {
548            return Err(BlockVerificationError::InvalidSeal);
549        }
550
551        // Find shard membership entropy for the slot
552        let shard_membership_entropy = shard_membership_entropy_source(
553            header.prefix.number,
554            best_header,
555            self.consensus_constants.shard_rotation_interval,
556            self.consensus_constants.shard_rotation_delay,
557            beacon_chain_info,
558        )?;
559
560        // Verify that the solution is valid (stateless half)
561        consensus_info
562            .solution
563            .verify_stateless::<PosTable>(
564                slot,
565                &SolutionVerifyStatelessParams {
566                    shard_index: ShardIndex::BEACON_CHAIN,
567                    proof_of_time: consensus_info.proof_of_time,
568                    solution_range: consensus_parameters.fixed_parameters.solution_range,
569                    shard_membership_entropy,
570                    num_shards: consensus_parameters.fixed_parameters.num_shards,
571                },
572            )
573            .map_err(BeaconChainBlockVerificationError::from)?;
574
575        Self::check_proof_of_time(
576            &self.pot_verifier,
577            self.consensus_constants.block_authoring_delay,
578            parent_header.consensus_info.slot,
579            parent_header.consensus_info.proof_of_time,
580            parent_header.consensus_info.future_proof_of_time,
581            parent_header.consensus_parameters(),
582            consensus_info.slot,
583            consensus_info.proof_of_time,
584            consensus_info.future_proof_of_time,
585            body.pot_checkpoints(),
586            self.full_pot_verification(block_number),
587        )?;
588
589        // TODO: Do something about equivocation?
590
591        Ok(())
592    }
593
594    fn verify_sequential(
595        &self,
596        parent_header: &BeaconChainHeader<'_>,
597        // TODO: Probably remove unused arguments
598        _parent_block_mmr_root: &Blake3Hash,
599        header: &BeaconChainHeader<'_>,
600        body: &BeaconChainBody<'_>,
601        _origin: &BlockOrigin,
602    ) -> Result<Option<SuperSegment>, BlockVerificationError> {
603        trace!(header = ?header, "Verify sequential");
604
605        let block_number = header.prefix.number;
606        let consensus_info = header.consensus_info;
607
608        let best_header = self.chain_info.best_header();
609        let best_header = best_header.header();
610        let best_number = best_header.prefix.number;
611
612        // Reject block below archiving point
613        if block_number + self.consensus_constants.block_confirmation_depth < best_number {
614            debug!(
615                ?header,
616                %best_number,
617                "Rejecting a block below the archiving point"
618            );
619
620            return Err(BlockVerificationError::BelowArchivingPoint);
621        }
622
623        let maybe_super_segment = derive_super_segments_for_block(
624            &self.chain_info,
625            parent_header.prefix.number,
626            self.consensus_constants.block_confirmation_depth,
627            self.consensus_constants.shard_confirmation_depth,
628        )
629        .map_err(BeaconChainBlockVerificationError::from)?;
630        let maybe_super_segment_root = maybe_super_segment
631            .as_ref()
632            .map(|super_segment| super_segment.header.root);
633
634        if maybe_super_segment_root.as_ref() != header.consensus_parameters().super_segment_root {
635            return Err(BlockVerificationError::from(
636                BeaconChainBlockVerificationError::InvalidSuperSegmentRoot {
637                    expected: Box::new(maybe_super_segment_root),
638                    actual: Box::new(header.consensus_parameters().super_segment_root.copied()),
639                },
640            ));
641        }
642
643        // Verify that the solution is valid (piece verification half)
644        {
645            let (current_history_size, solution_num_segments, solution_super_segment_root) =
646                if block_number == BlockNumber::ONE {
647                    let latest_super_segment = maybe_super_segment.as_ref().ok_or(
648                        BeaconChainBlockVerificationError::MissingSuperSegmentInFirstBlock,
649                    )?;
650
651                    (
652                        HistorySize::ONE,
653                        latest_super_segment.header.num_segments,
654                        latest_super_segment.header.root,
655                    )
656                } else {
657                    let max_segment_index = self
658                        .chain_info
659                        .previous_super_segment_header(block_number)
660                        .ok_or(
661                            BeaconChainBlockVerificationError::PreviousSuperSegmentHeaderNotFound,
662                        )?
663                        .max_segment_index
664                        .as_inner();
665                    let current_history_size = HistorySize::from(max_segment_index);
666
667                    let solution_super_segment_header = self
668                        .chain_info
669                        .get_super_segment_header(consensus_info.solution.piece_super_segment_index)
670                        .ok_or(
671                            BeaconChainBlockVerificationError::SolutionSuperSegmentNotFound {
672                                index: consensus_info.solution.piece_super_segment_index,
673                            },
674                        )?;
675
676                    (
677                        current_history_size,
678                        solution_super_segment_header.num_segments,
679                        solution_super_segment_header.root,
680                    )
681                };
682
683            let sector_expiration_check_super_segment_root = self
684                .chain_info
685                .get_super_segment_header_for_segment_index(
686                    consensus_info
687                        .solution
688                        .history_size
689                        .sector_expiration_check(self.consensus_constants.min_sector_lifetime)
690                        .ok_or(BeaconChainBlockVerificationError::InvalidHistorySize {
691                            history_size: consensus_info.solution.history_size,
692                            current_history_size,
693                        })?
694                        .segment_index(),
695                )
696                .map(|super_segment_header| super_segment_header.root);
697
698            consensus_info
699                .solution
700                .verify_piece(&SolutionVerifyPieceParams {
701                    // TODO: Query it from an actual chain
702                    max_pieces_in_sector: 1000,
703                    super_segment_root: solution_super_segment_root,
704                    num_segments: solution_num_segments,
705                    recent_segments: self.consensus_constants.recent_segments,
706                    recent_history_fraction: self.consensus_constants.recent_history_fraction,
707                    min_sector_lifetime: self.consensus_constants.min_sector_lifetime,
708                    current_history_size,
709                    sector_expiration_check_super_segment_root,
710                })
711                .map_err(BeaconChainBlockVerificationError::from)?;
712        }
713
714        self.check_body(
715            block_number,
716            body.own_segments(),
717            body.intermediate_shard_blocks(),
718        )?;
719
720        Ok(maybe_super_segment)
721    }
722}