@@ -76,7 +76,7 @@ pub use in_memory_reorg::InMemoryReorgError;
7676
7777pub struct ChainstateRef < ' a , S , V > {
7878 chain_config : & ' a ChainConfig ,
79- _chainstate_config : & ' a ChainstateConfig ,
79+ chainstate_config : & ' a ChainstateConfig ,
8080 tx_verification_strategy : & ' a V ,
8181 db_tx : S ,
8282 time_getter : & ' a TimeGetter ,
@@ -141,7 +141,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
141141 ) -> Self {
142142 ChainstateRef {
143143 chain_config,
144- _chainstate_config : chainstate_config,
144+ chainstate_config,
145145 db_tx,
146146 tx_verification_strategy,
147147 time_getter,
@@ -157,7 +157,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
157157 ) -> Self {
158158 ChainstateRef {
159159 chain_config,
160- _chainstate_config : chainstate_config,
160+ chainstate_config,
161161 db_tx,
162162 tx_verification_strategy,
163163 time_getter,
@@ -457,22 +457,45 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
457457 Ok ( result)
458458 }
459459
460+ fn enforce_checkpoint_impl (
461+ & self ,
462+ height : BlockHeight ,
463+ expected : & Id < GenBlock > ,
464+ given : & Id < GenBlock > ,
465+ ) -> Result < ( ) , CheckBlockError > {
466+ if given != expected {
467+ // Note: we only log the mismatch if we're going to ignore it (because if it's
468+ // not ignored, we'll log the error anyway).
469+ if self . chainstate_config . checkpoints_mismatch_allowed ( ) {
470+ log:: warn!(
471+ "Checkpoint mismatch at height {}, expected: {:x}, actual: {:x}" ,
472+ height,
473+ expected,
474+ given,
475+ ) ;
476+ } else {
477+ return Err ( CheckBlockError :: CheckpointMismatch {
478+ height,
479+ expected : * expected,
480+ given : * given,
481+ } ) ;
482+ }
483+ }
484+
485+ Ok ( ( ) )
486+ }
487+
460488 // If the header height is at an exact checkpoint height, check that the block id matches the checkpoint id.
461489 // Return true if the header height is at an exact checkpoint height.
462490 fn enforce_exact_checkpoint_assuming_height (
463491 & self ,
464492 header : & SignedBlockHeader ,
465493 header_height : BlockHeight ,
466494 ) -> Result < bool , CheckBlockError > {
467- if let Some ( e) = self . chain_config . height_checkpoints ( ) . checkpoint_at_height ( & header_height)
495+ if let Some ( expected_id) =
496+ self . chain_config . height_checkpoints ( ) . checkpoint_at_height ( & header_height)
468497 {
469- let expected_id = Id :: < Block > :: new ( e. to_hash ( ) ) ;
470- if expected_id != header. get_id ( ) {
471- return Err ( CheckBlockError :: CheckpointMismatch (
472- expected_id,
473- header. get_id ( ) ,
474- ) ) ;
475- }
498+ self . enforce_checkpoint_impl ( header_height, expected_id, & header. get_id ( ) . into ( ) ) ?;
476499 Ok ( true )
477500 } else {
478501 Ok ( false )
@@ -499,17 +522,13 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
499522
500523 let parent_checkpoint_block_index =
501524 self . get_ancestor ( & prev_block_index, expected_checkpoint_height) ?;
502-
503525 let parent_checkpoint_id = parent_checkpoint_block_index. block_id ( ) ;
504526
505- if parent_checkpoint_id != expected_checkpoint_id {
506- return Err ( CheckBlockError :: ParentCheckpointMismatch (
507- expected_checkpoint_height,
508- expected_checkpoint_id,
509- parent_checkpoint_id,
510- ) ) ;
511- }
512-
527+ self . enforce_checkpoint_impl (
528+ expected_checkpoint_height,
529+ & expected_checkpoint_id,
530+ & parent_checkpoint_id,
531+ ) ?;
513532 Ok ( ( ) )
514533 }
515534
@@ -564,11 +583,11 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
564583 if common_ancestor_height < min_allowed_height {
565584 let tip_block_height = self . get_best_block_index ( ) ?. block_height ( ) ;
566585
567- return Err ( CheckBlockError :: AttemptedToAddBlockBeforeReorgLimit (
586+ return Err ( CheckBlockError :: AttemptedToAddBlockBeforeReorgLimit {
568587 common_ancestor_height,
569588 tip_block_height,
570589 min_allowed_height,
571- ) ) ;
590+ } ) ;
572591 }
573592
574593 Ok ( ( ) )
@@ -599,8 +618,45 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
599618 Ok ( parent_block_index)
600619 }
601620
621+ /// This function is intended to be used in check_block and check_block_header.
622+ ///
623+ /// Return true if the block already exists in the chainstate and has an "ok" status
624+ /// with the validation stage CheckBlockOk or later.
625+ /// If it has a non-"ok" status, return an error.
626+ /// If the block is new, or if its validation stage is below CheckBlockOk (i.e. it's Unchecked),
627+ /// return false.
628+ fn skip_check_block_because_block_exists_and_is_checked (
629+ & self ,
630+ block_id : & Id < Block > ,
631+ ) -> Result < bool , CheckBlockError > {
632+ if let Some ( block_index) = self . get_block_index ( block_id) ? {
633+ let status = block_index. status ( ) ;
634+
635+ if status. is_ok ( ) {
636+ let checked = status. last_valid_stage ( ) >= BlockValidationStage :: CheckBlockOk ;
637+ Ok ( checked)
638+ } else {
639+ Err ( CheckBlockError :: InvalidBlockAlreadyProcessed ( * block_id) )
640+ }
641+ } else {
642+ Ok ( false )
643+ }
644+ }
645+
602646 #[ log_error]
603647 pub fn check_block_header ( & self , header : & SignedBlockHeader ) -> Result < ( ) , CheckBlockError > {
648+ let header = WithId :: new ( header) ;
649+ if self . skip_check_block_because_block_exists_and_is_checked ( & WithId :: id ( & header) ) ? {
650+ return Ok ( ( ) ) ;
651+ }
652+
653+ self . check_block_header_impl ( & header)
654+ }
655+
656+ fn check_block_header_impl (
657+ & self ,
658+ header : & WithId < & SignedBlockHeader > ,
659+ ) -> Result < ( ) , CheckBlockError > {
604660 let parent_block_index = self . check_block_parent ( header) ?;
605661 self . check_header_size ( header) ?;
606662 self . enforce_checkpoints ( header) ?;
@@ -662,7 +718,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
662718 ensure ! (
663719 block_timestamp. as_duration_since_epoch( ) <= current_time_as_secs + max_future_offset,
664720 CheckBlockError :: BlockFromTheFuture {
665- block_id: header . block_id ( ) ,
721+ block_id: WithId :: id ( header ) ,
666722 block_timestamp,
667723 current_time
668724 } ,
@@ -833,7 +889,14 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
833889
834890 #[ log_error]
835891 pub fn check_block ( & self , block : & WithId < Block > ) -> Result < ( ) , CheckBlockError > {
836- self . check_block_header ( block. header ( ) ) ?;
892+ let header_with_id = WithId :: as_sub_obj ( block) ;
893+ if self
894+ . skip_check_block_because_block_exists_and_is_checked ( & WithId :: id ( & header_with_id) ) ?
895+ {
896+ return Ok ( ( ) ) ;
897+ }
898+
899+ self . check_block_header_impl ( & header_with_id) ?;
837900
838901 self . check_block_size ( block) . map_err ( CheckBlockError :: BlockSizeError ) ?;
839902
0 commit comments