sc_consensus_subspace/
block_import.rs

1//! Block import for Subspace, which includes stateful verification and corresponding notifications.
2//!
3//! In most cases block import happens after stateless block verification using [`verifier`](crate::verifier),
4//! the only exception to that is locally authored blocks.
5//!
6//! Since [`verifier`](crate::verifier) is stateless, the remaining checks in block import are those
7//! that require presence of the parent block or its state in the database. Specifically for Proof
8//! of Time individual checkpoints are assumed to be checked already and only PoT inputs need to be
9//! checked to correspond to the state of the parent block.
10//!
11//! After all checks and right before importing the block notification ([`SubspaceLink::block_importing_notification_stream`])
12//! will be sent that [`archiver`](crate::archiver) among other things is subscribed to.
13
14use crate::archiver::SegmentHeadersStore;
15use crate::verifier::VerificationError;
16use crate::{SubspaceLink, aux_schema, slot_worker};
17use futures::StreamExt;
18use futures::channel::mpsc;
19use sc_client_api::BlockBackend;
20use sc_client_api::backend::AuxStore;
21use sc_consensus::StateAction;
22use sc_consensus::block_import::{
23    BlockCheckParams, BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult,
24};
25use sc_proof_of_time::source::pot_next_slot_input;
26use sc_proof_of_time::verifier::PotVerifier;
27use sp_api::{ApiError, ApiExt, ProvideRuntimeApi};
28use sp_block_builder::BlockBuilder as BlockBuilderApi;
29use sp_blockchain::HeaderBackend;
30use sp_consensus_slots::Slot;
31use sp_consensus_subspace::digests::{
32    SubspaceDigestItems, extract_pre_digest, extract_subspace_digest_items,
33};
34use sp_consensus_subspace::{PotNextSlotInput, SubspaceApi, SubspaceJustification};
35use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
36use sp_runtime::Justifications;
37use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor, One};
38use std::marker::PhantomData;
39use std::sync::Arc;
40use subspace_core_primitives::sectors::SectorId;
41use subspace_core_primitives::segments::{HistorySize, SegmentHeader, SegmentIndex};
42use subspace_core_primitives::solutions::SolutionRange;
43use subspace_core_primitives::{BlockNumber, PublicKey};
44use subspace_proof_of_space::Table;
45use subspace_verification::{PieceCheckParams, VerifySolutionParams, calculate_block_weight};
46use tracing::warn;
47
48/// Notification with number of the block that is about to be imported and acknowledgement sender
49/// that can be used to pause block production if desired.
50#[derive(Debug, Clone)]
51pub struct BlockImportingNotification<Block>
52where
53    Block: BlockT,
54{
55    /// Block number
56    pub block_number: NumberFor<Block>,
57    /// Sender for pausing the block import when operator is not fast enough to process
58    /// the consensus block.
59    pub acknowledgement_sender: mpsc::Sender<()>,
60}
61use subspace_verification::Error as VerificationPrimitiveError;
62
63/// Errors encountered by the Subspace authorship task.
64#[derive(Debug, thiserror::Error)]
65pub enum Error<Header: HeaderT> {
66    /// Inner block import error
67    #[error("Inner block import error: {0}")]
68    InnerBlockImportError(#[from] sp_consensus::Error),
69    /// Error during digest item extraction
70    #[error("Digest item error: {0}")]
71    DigestItemError(#[from] sp_consensus_subspace::digests::Error),
72    /// Parent unavailable. Cannot import
73    #[error("Parent ({0}) of {1} unavailable. Cannot import")]
74    ParentUnavailable(Header::Hash, Header::Hash),
75    /// Genesis block unavailable. Cannot import
76    #[error("Genesis block unavailable. Cannot import")]
77    GenesisUnavailable,
78    /// Slot number must increase
79    #[error("Slot number must increase: parent slot: {0}, this slot: {1}")]
80    SlotMustIncrease(Slot, Slot),
81    /// Header has a bad seal
82    #[error("Header {0:?} has a bad seal")]
83    HeaderBadSeal(Header::Hash),
84    /// Header is unsealed
85    #[error("Header {0:?} is unsealed")]
86    HeaderUnsealed(Header::Hash),
87    /// Bad reward signature
88    #[error("Bad reward signature on {0:?}")]
89    BadRewardSignature(Header::Hash),
90    /// Missing Subspace justification
91    #[error("Missing Subspace justification")]
92    MissingSubspaceJustification,
93    /// Invalid Subspace justification
94    #[error("Invalid Subspace justification: {0}")]
95    InvalidSubspaceJustification(parity_scale_codec::Error),
96    /// Invalid Subspace justification contents
97    #[error("Invalid Subspace justification contents")]
98    InvalidSubspaceJustificationContents,
99    /// Invalid proof of time
100    #[error("Invalid proof of time")]
101    InvalidProofOfTime,
102    /// Solution is outside of solution range
103    #[error(
104        "Solution distance {solution_distance} is outside of solution range \
105        {half_solution_range} (half of actual solution range) for slot {slot}"
106    )]
107    OutsideOfSolutionRange {
108        /// Time slot
109        slot: Slot,
110        /// Half of solution range
111        half_solution_range: SolutionRange,
112        /// Solution distance
113        solution_distance: SolutionRange,
114    },
115    /// Invalid proof of space
116    #[error("Invalid proof of space")]
117    InvalidProofOfSpace,
118    /// Invalid audit chunk offset
119    #[error("Invalid audit chunk offset")]
120    InvalidAuditChunkOffset,
121    /// Invalid chunk
122    #[error("Invalid chunk: {0}")]
123    InvalidChunk(String),
124    /// Invalid chunk witness
125    #[error("Invalid chunk witness")]
126    InvalidChunkWitness,
127    /// Piece verification failed
128    #[error("Piece verification failed")]
129    InvalidPieceOffset {
130        /// Time slot
131        slot: Slot,
132        /// Index of the piece that failed verification
133        piece_offset: u16,
134        /// How many pieces one sector is supposed to contain (max)
135        max_pieces_in_sector: u16,
136    },
137    /// History size is in the future
138    #[error("History size {solution} is in the future, current is {current}")]
139    FutureHistorySize {
140        /// Current history size
141        current: HistorySize,
142        /// History size solution was created for
143        solution: HistorySize,
144    },
145    /// Piece verification failed
146    #[error("Piece verification failed for slot {0}")]
147    InvalidPiece(Slot),
148    /// Block has invalid associated solution range
149    #[error("Invalid solution range for block {0}")]
150    InvalidSolutionRange(Header::Hash),
151    /// Invalid set of segment headers
152    #[error("Invalid set of segment headers")]
153    InvalidSetOfSegmentHeaders,
154    /// Stored segment header extrinsic was not found
155    #[error("Stored segment header extrinsic was not found: {0:?}")]
156    SegmentHeadersExtrinsicNotFound(Vec<SegmentHeader>),
157    /// Segment header not found
158    #[error("Segment header for index {0} not found")]
159    SegmentHeaderNotFound(SegmentIndex),
160    /// Different segment commitment found
161    #[error(
162        "Different segment commitment for segment index {0} was found in storage, likely fork \
163        below archiving point"
164    )]
165    DifferentSegmentCommitment(SegmentIndex),
166    /// Segment commitment not found
167    #[error("Segment commitment for segment index {0} not found")]
168    SegmentCommitmentNotFound(SegmentIndex),
169    /// Sector expired
170    #[error("Sector expired")]
171    SectorExpired {
172        /// Expiration history size
173        expiration_history_size: HistorySize,
174        /// Current history size
175        current_history_size: HistorySize,
176    },
177    /// Invalid history size
178    #[error("Invalid history size")]
179    InvalidHistorySize,
180    /// Only root plot public key is allowed
181    #[error("Only root plot public key is allowed")]
182    OnlyRootPlotPublicKeyAllowed,
183    /// Check inherents error
184    #[error("Checking inherents failed: {0}")]
185    CheckInherents(sp_inherents::Error),
186    /// Unhandled check inherents error
187    #[error("Checking inherents unhandled error: {}", String::from_utf8_lossy(.0))]
188    CheckInherentsUnhandled(sp_inherents::InherentIdentifier),
189    /// Create inherents error.
190    #[error("Creating inherents failed: {0}")]
191    CreateInherents(sp_inherents::Error),
192    /// Client error
193    #[error(transparent)]
194    Client(#[from] sp_blockchain::Error),
195    /// Runtime Api error.
196    #[error(transparent)]
197    RuntimeApi(#[from] ApiError),
198}
199
200impl<Header> From<VerificationError<Header>> for Error<Header>
201where
202    Header: HeaderT,
203{
204    #[inline]
205    fn from(error: VerificationError<Header>) -> Self {
206        match error {
207            VerificationError::HeaderBadSeal(block_hash) => Error::HeaderBadSeal(block_hash),
208            VerificationError::HeaderUnsealed(block_hash) => Error::HeaderUnsealed(block_hash),
209            VerificationError::BadRewardSignature(block_hash) => {
210                Error::BadRewardSignature(block_hash)
211            }
212            VerificationError::MissingSubspaceJustification => Error::MissingSubspaceJustification,
213            VerificationError::InvalidSubspaceJustification(error) => {
214                Error::InvalidSubspaceJustification(error)
215            }
216            VerificationError::InvalidSubspaceJustificationContents => {
217                Error::InvalidSubspaceJustificationContents
218            }
219            VerificationError::InvalidProofOfTime => Error::InvalidProofOfTime,
220            VerificationError::VerificationError(slot, error) => match error {
221                VerificationPrimitiveError::InvalidPieceOffset {
222                    piece_offset,
223                    max_pieces_in_sector,
224                } => Error::InvalidPieceOffset {
225                    slot,
226                    piece_offset,
227                    max_pieces_in_sector,
228                },
229                VerificationPrimitiveError::FutureHistorySize { current, solution } => {
230                    Error::FutureHistorySize { current, solution }
231                }
232                VerificationPrimitiveError::InvalidPiece => Error::InvalidPiece(slot),
233                VerificationPrimitiveError::OutsideSolutionRange {
234                    half_solution_range,
235                    solution_distance,
236                } => Error::OutsideOfSolutionRange {
237                    slot,
238                    half_solution_range,
239                    solution_distance,
240                },
241                VerificationPrimitiveError::InvalidProofOfSpace => Error::InvalidProofOfSpace,
242                VerificationPrimitiveError::InvalidAuditChunkOffset => {
243                    Error::InvalidAuditChunkOffset
244                }
245                VerificationPrimitiveError::InvalidChunk(error) => Error::InvalidChunk(error),
246                VerificationPrimitiveError::InvalidChunkWitness => Error::InvalidChunkWitness,
247                VerificationPrimitiveError::SectorExpired {
248                    expiration_history_size,
249                    current_history_size,
250                } => Error::SectorExpired {
251                    expiration_history_size,
252                    current_history_size,
253                },
254                VerificationPrimitiveError::InvalidHistorySize => Error::InvalidHistorySize,
255            },
256        }
257    }
258}
259
260impl<Header> From<Error<Header>> for String
261where
262    Header: HeaderT,
263{
264    #[inline]
265    fn from(error: Error<Header>) -> String {
266        error.to_string()
267    }
268}
269
270/// A block-import handler for Subspace.
271pub struct SubspaceBlockImport<PosTable, Block, Client, I, CIDP, AS>
272where
273    Block: BlockT,
274{
275    inner: I,
276    client: Arc<Client>,
277    subspace_link: SubspaceLink<Block>,
278    create_inherent_data_providers: CIDP,
279    segment_headers_store: SegmentHeadersStore<AS>,
280    pot_verifier: PotVerifier,
281    _pos_table: PhantomData<PosTable>,
282}
283
284impl<PosTable, Block, I, Client, CIDP, AS> Clone
285    for SubspaceBlockImport<PosTable, Block, Client, I, CIDP, AS>
286where
287    Block: BlockT,
288    I: Clone,
289    CIDP: Clone,
290{
291    fn clone(&self) -> Self {
292        SubspaceBlockImport {
293            inner: self.inner.clone(),
294            client: self.client.clone(),
295            subspace_link: self.subspace_link.clone(),
296            create_inherent_data_providers: self.create_inherent_data_providers.clone(),
297            segment_headers_store: self.segment_headers_store.clone(),
298            pot_verifier: self.pot_verifier.clone(),
299            _pos_table: PhantomData,
300        }
301    }
302}
303
304impl<PosTable, Block, Client, I, CIDP, AS> SubspaceBlockImport<PosTable, Block, Client, I, CIDP, AS>
305where
306    PosTable: Table,
307    Block: BlockT,
308    Client: ProvideRuntimeApi<Block> + BlockBackend<Block> + HeaderBackend<Block> + AuxStore,
309    Client::Api: BlockBuilderApi<Block> + SubspaceApi<Block, PublicKey> + ApiExt<Block>,
310    CIDP: CreateInherentDataProviders<Block, ()> + Send + Sync + 'static,
311    AS: AuxStore + Send + Sync + 'static,
312    BlockNumber: From<NumberFor<Block>>,
313{
314    /// Produce a Subspace block-import object to be used later on in the construction of an import-queue.
315    pub fn new(
316        client: Arc<Client>,
317        block_import: I,
318        subspace_link: SubspaceLink<Block>,
319        create_inherent_data_providers: CIDP,
320        segment_headers_store: SegmentHeadersStore<AS>,
321        pot_verifier: PotVerifier,
322    ) -> Self {
323        Self {
324            client,
325            inner: block_import,
326            subspace_link,
327            create_inherent_data_providers,
328            segment_headers_store,
329            pot_verifier,
330            _pos_table: PhantomData,
331        }
332    }
333
334    async fn block_import_verification(
335        &self,
336        block_hash: Block::Hash,
337        header: Block::Header,
338        extrinsics: Option<Vec<Block::Extrinsic>>,
339        root_plot_public_key: &Option<PublicKey>,
340        subspace_digest_items: &SubspaceDigestItems<PublicKey>,
341        justifications: &Option<Justifications>,
342    ) -> Result<(), Error<Block::Header>> {
343        let block_number = *header.number();
344        let parent_hash = *header.parent_hash();
345
346        let pre_digest = &subspace_digest_items.pre_digest;
347        if let Some(root_plot_public_key) = root_plot_public_key
348            && &pre_digest.solution().public_key != root_plot_public_key
349        {
350            // Only root plot public key is allowed.
351            return Err(Error::OnlyRootPlotPublicKeyAllowed);
352        }
353
354        let parent_header = self
355            .client
356            .header(parent_hash)?
357            .ok_or(Error::ParentUnavailable(parent_hash, block_hash))?;
358
359        let parent_pre_digest = extract_pre_digest(&parent_header)?;
360        let parent_slot = parent_pre_digest.slot();
361
362        // Make sure that slot number is strictly increasing
363        if pre_digest.slot() <= parent_slot {
364            return Err(Error::SlotMustIncrease(parent_slot, pre_digest.slot()));
365        }
366
367        let parent_subspace_digest_items = if block_number.is_one() {
368            None
369        } else {
370            Some(extract_subspace_digest_items::<_, PublicKey>(
371                &parent_header,
372            )?)
373        };
374
375        let correct_solution_range = if block_number.is_one() {
376            slot_worker::extract_solution_ranges_for_block(self.client.as_ref(), parent_hash)?.0
377        } else {
378            let parent_subspace_digest_items = parent_subspace_digest_items
379                .as_ref()
380                .expect("Always Some for non-first block; qed");
381
382            match parent_subspace_digest_items.next_solution_range {
383                Some(solution_range) => solution_range,
384                None => parent_subspace_digest_items.solution_range,
385            }
386        };
387
388        if subspace_digest_items.solution_range != correct_solution_range {
389            return Err(Error::InvalidSolutionRange(block_hash));
390        }
391
392        let chain_constants = self.subspace_link.chain_constants();
393
394        // For PoT justifications we only need to check the seed and number of checkpoints, the rest
395        // was already checked during stateless block verification.
396        {
397            let runtime_api = self.client.runtime_api();
398            let parent_pot_parameters = runtime_api
399                .pot_parameters(parent_hash)
400                .map_err(|_| Error::ParentUnavailable(parent_hash, block_hash))?;
401
402            let pot_input = pot_next_slot_input::<Block>(
403                parent_header.number(),
404                parent_slot,
405                &parent_pot_parameters,
406                self.pot_verifier.genesis_seed(),
407                parent_pre_digest.pot_info().proof_of_time(),
408            );
409
410            // Ensure proof of time is valid according to parent block
411            if !self.pot_verifier.is_output_valid(
412                pot_input,
413                pre_digest.slot() - parent_slot,
414                pre_digest.pot_info().proof_of_time(),
415                parent_pot_parameters.next_parameters_change(),
416            ) {
417                return Err(Error::InvalidProofOfTime);
418            }
419
420            let Some(subspace_justification) = justifications
421                .as_ref()
422                .and_then(|justifications| {
423                    justifications
424                        .iter()
425                        .find_map(SubspaceJustification::try_from_justification)
426                })
427                .transpose()
428                .map_err(Error::InvalidSubspaceJustification)?
429            else {
430                return Err(Error::MissingSubspaceJustification);
431            };
432
433            let SubspaceJustification::PotCheckpoints { seed, checkpoints } =
434                subspace_justification;
435
436            let future_slot = pre_digest.slot() + chain_constants.block_authoring_delay();
437
438            if block_number.is_one() {
439                // In case of first block seed must match genesis seed
440                if seed != self.pot_verifier.genesis_seed() {
441                    return Err(Error::InvalidSubspaceJustificationContents);
442                }
443
444                // Number of checkpoints must match future slot number
445                if checkpoints.len() as u64 != *future_slot {
446                    return Err(Error::InvalidSubspaceJustificationContents);
447                }
448            } else {
449                let parent_subspace_digest_items = parent_subspace_digest_items
450                    .as_ref()
451                    .expect("Always Some for non-first block; qed");
452
453                let parent_future_slot = parent_slot + chain_constants.block_authoring_delay();
454
455                let correct_input_parameters = PotNextSlotInput::derive(
456                    subspace_digest_items.pot_slot_iterations,
457                    parent_future_slot,
458                    parent_subspace_digest_items
459                        .pre_digest
460                        .pot_info()
461                        .future_proof_of_time(),
462                    &subspace_digest_items.pot_parameters_change,
463                );
464
465                if seed != correct_input_parameters.seed {
466                    return Err(Error::InvalidSubspaceJustificationContents);
467                }
468
469                // Number of checkpoints must match number of proofs that were not yet seen on chain
470                if checkpoints.len() as u64 != (*future_slot - *parent_future_slot) {
471                    return Err(Error::InvalidSubspaceJustificationContents);
472                }
473            }
474        }
475
476        let sector_id = SectorId::new(
477            pre_digest.solution().public_key.hash(),
478            pre_digest.solution().sector_index,
479            pre_digest.solution().history_size,
480        );
481
482        let max_pieces_in_sector = self
483            .client
484            .runtime_api()
485            .max_pieces_in_sector(parent_hash)?;
486        let piece_index = sector_id.derive_piece_index(
487            pre_digest.solution().piece_offset,
488            pre_digest.solution().history_size,
489            max_pieces_in_sector,
490            chain_constants.recent_segments(),
491            chain_constants.recent_history_fraction(),
492        );
493        let segment_index = piece_index.segment_index();
494
495        let segment_commitment = self
496            .segment_headers_store
497            .get_segment_header(segment_index)
498            .map(|segment_header| segment_header.segment_commitment())
499            .ok_or(Error::SegmentCommitmentNotFound(segment_index))?;
500
501        let sector_expiration_check_segment_commitment = self
502            .segment_headers_store
503            .get_segment_header(
504                subspace_digest_items
505                    .pre_digest
506                    .solution()
507                    .history_size
508                    .sector_expiration_check(chain_constants.min_sector_lifetime())
509                    .ok_or(Error::InvalidHistorySize)?
510                    .segment_index(),
511            )
512            .map(|segment_header| segment_header.segment_commitment());
513
514        // Piece is not checked during initial block verification because it requires access to
515        // segment header and runtime, check it now.
516        subspace_verification::verify_solution::<PosTable, _>(
517            pre_digest.solution(),
518            // Slot was already checked during initial block verification
519            pre_digest.slot().into(),
520            &VerifySolutionParams {
521                proof_of_time: subspace_digest_items.pre_digest.pot_info().proof_of_time(),
522                solution_range: subspace_digest_items.solution_range,
523                piece_check_params: Some(PieceCheckParams {
524                    max_pieces_in_sector,
525                    segment_commitment,
526                    recent_segments: chain_constants.recent_segments(),
527                    recent_history_fraction: chain_constants.recent_history_fraction(),
528                    min_sector_lifetime: chain_constants.min_sector_lifetime(),
529                    current_history_size: self.client.runtime_api().history_size(parent_hash)?,
530                    sector_expiration_check_segment_commitment,
531                }),
532            },
533            &self.subspace_link.kzg,
534        )
535        .map_err(|error| VerificationError::VerificationError(pre_digest.slot(), error))?;
536
537        // If the body is passed through, we need to use the runtime to check that the
538        // internally-set timestamp in the inherents actually matches the slot set in the seal
539        // and segment headers in the inherents are set correctly.
540        if let Some(extrinsics) = extrinsics {
541            let create_inherent_data_providers = self
542                .create_inherent_data_providers
543                .create_inherent_data_providers(parent_hash, ())
544                .await
545                .map_err(|error| Error::Client(sp_blockchain::Error::from(error)))?;
546
547            let inherent_data = create_inherent_data_providers
548                .create_inherent_data()
549                .await
550                .map_err(Error::CreateInherents)?;
551
552            let inherent_res = self.client.runtime_api().check_inherents(
553                parent_hash,
554                Block::new(header, extrinsics),
555                inherent_data,
556            )?;
557
558            if !inherent_res.ok() {
559                for (i, e) in inherent_res.into_errors() {
560                    match create_inherent_data_providers
561                        .try_handle_error(&i, &e)
562                        .await
563                    {
564                        Some(res) => res.map_err(Error::CheckInherents)?,
565                        None => return Err(Error::CheckInherentsUnhandled(i)),
566                    }
567                }
568            }
569        }
570
571        Ok(())
572    }
573}
574
575#[async_trait::async_trait]
576impl<PosTable, Block, Client, Inner, CIDP, AS> BlockImport<Block>
577    for SubspaceBlockImport<PosTable, Block, Client, Inner, CIDP, AS>
578where
579    PosTable: Table,
580    Block: BlockT,
581    Inner: BlockImport<Block, Error = sp_consensus::Error> + Send + Sync,
582    Client: ProvideRuntimeApi<Block>
583        + BlockBackend<Block>
584        + HeaderBackend<Block>
585        + AuxStore
586        + Send
587        + Sync,
588    Client::Api: BlockBuilderApi<Block> + SubspaceApi<Block, PublicKey> + ApiExt<Block>,
589    CIDP: CreateInherentDataProviders<Block, ()> + Send + Sync + 'static,
590    AS: AuxStore + Send + Sync + 'static,
591    BlockNumber: From<NumberFor<Block>>,
592{
593    type Error = Error<Block::Header>;
594
595    async fn import_block(
596        &self,
597        mut block: BlockImportParams<Block>,
598    ) -> Result<ImportResult, Self::Error> {
599        let block_hash = block.post_hash();
600        let block_number = *block.header.number();
601
602        // Early exit if block already in chain
603        match self.client.status(block_hash)? {
604            sp_blockchain::BlockStatus::InChain => {
605                block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
606                return self
607                    .inner
608                    .import_block(block)
609                    .await
610                    .map_err(Error::InnerBlockImportError);
611            }
612            sp_blockchain::BlockStatus::Unknown => {}
613        }
614
615        let subspace_digest_items = extract_subspace_digest_items(&block.header)?;
616
617        // Only do import verification if we do not have the state already. If state needs to be
618        // applied this means verification and execution already happened before and doesn't need to
619        // be done again here (often can't because parent block would be missing in special sync
620        // modes).
621        if !matches!(block.state_action, StateAction::ApplyChanges(_)) {
622            let root_plot_public_key = self
623                .client
624                .runtime_api()
625                .root_plot_public_key(*block.header.parent_hash())?;
626
627            self.block_import_verification(
628                block_hash,
629                block.header.clone(),
630                block.body.clone(),
631                &root_plot_public_key,
632                &subspace_digest_items,
633                &block.justifications,
634            )
635            .await?;
636        }
637
638        let parent_weight = if block_number.is_one() {
639            0
640        } else {
641            // Parent block weight might be missing in special sync modes where block is imported in
642            // the middle of the blockchain history directly
643            aux_schema::load_block_weight(self.client.as_ref(), block.header.parent_hash())?
644                .unwrap_or_default()
645        };
646
647        let added_weight = calculate_block_weight(subspace_digest_items.solution_range);
648        let total_weight = parent_weight.saturating_add(added_weight);
649
650        aux_schema::write_block_weight(block_hash, total_weight, |values| {
651            block
652                .auxiliary
653                .extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))))
654        });
655
656        for (&segment_index, segment_commitment) in &subspace_digest_items.segment_commitments {
657            let found_segment_commitment = self
658                .segment_headers_store
659                .get_segment_header(segment_index)
660                .ok_or_else(|| Error::SegmentHeaderNotFound(segment_index))?
661                .segment_commitment();
662
663            if &found_segment_commitment != segment_commitment {
664                warn!(
665                    "Different segment commitment for segment index {} was found in storage, \
666                    likely fork below archiving point. expected {:?}, found {:?}",
667                    segment_index, segment_commitment, found_segment_commitment
668                );
669                return Err(Error::DifferentSegmentCommitment(segment_index));
670            }
671        }
672
673        // The fork choice rule is that we pick the heaviest chain (i.e. smallest solution range),
674        // if there's a tie we go with the longest chain
675        let fork_choice = {
676            let info = self.client.info();
677
678            let last_best_weight = if &info.best_hash == block.header.parent_hash() {
679                // the parent=genesis case is already covered for loading parent weight, so we don't
680                // need to cover again here
681                parent_weight
682            } else {
683                // Best block weight might be missing in special sync modes where block is imported
684                // in the middle of the blockchain history right after genesis
685                aux_schema::load_block_weight(&*self.client, info.best_hash)?.unwrap_or_default()
686            };
687
688            ForkChoiceStrategy::Custom(total_weight > last_best_weight)
689        };
690        block.fork_choice = Some(fork_choice);
691
692        let (acknowledgement_sender, mut acknowledgement_receiver) = mpsc::channel(0);
693
694        self.subspace_link
695            .block_importing_notification_sender
696            .notify(move || BlockImportingNotification {
697                block_number,
698                acknowledgement_sender,
699            });
700
701        while acknowledgement_receiver.next().await.is_some() {
702            // Wait for all the acknowledgements to finish.
703        }
704
705        self.inner
706            .import_block(block)
707            .await
708            .map_err(Error::InnerBlockImportError)
709    }
710
711    async fn check_block(
712        &self,
713        block: BlockCheckParams<Block>,
714    ) -> Result<ImportResult, Self::Error> {
715        self.inner.check_block(block).await.map_err(Into::into)
716    }
717}