Skip to content

Commit 836bfc0

Browse files
committed
preliminary_block_check and preliminary_headers_check now check if the block already exists
1 parent d805c6f commit 836bfc0

File tree

8 files changed

+412
-43
lines changed

8 files changed

+412
-43
lines changed

chainstate/src/detail/ban_score.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,11 @@ impl BanScore for CheckBlockError {
319319
CheckBlockError::PropertyQueryError(_) => 100,
320320
CheckBlockError::CheckpointMismatch { .. } => 100,
321321
CheckBlockError::GetAncestorError(_) => 100,
322-
CheckBlockError::AttemptedToAddBlockBeforeReorgLimit(_, _, _) => 100,
322+
CheckBlockError::AttemptedToAddBlockBeforeReorgLimit { .. } => 100,
323323
CheckBlockError::EpochSealError(err) => err.ban_score(),
324324
CheckBlockError::InvalidParent { .. } => 100,
325325
CheckBlockError::InMemoryReorgFailed(err) => err.ban_score(),
326+
CheckBlockError::InvalidBlockAlreadyProcessed(_) => 100,
326327
}
327328
}
328329
}

chainstate/src/detail/chainstateref/mod.rs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -583,11 +583,11 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
583583
if common_ancestor_height < min_allowed_height {
584584
let tip_block_height = self.get_best_block_index()?.block_height();
585585

586-
return Err(CheckBlockError::AttemptedToAddBlockBeforeReorgLimit(
586+
return Err(CheckBlockError::AttemptedToAddBlockBeforeReorgLimit {
587587
common_ancestor_height,
588588
tip_block_height,
589589
min_allowed_height,
590-
));
590+
});
591591
}
592592

593593
Ok(())
@@ -618,8 +618,45 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
618618
Ok(parent_block_index)
619619
}
620620

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+
621646
#[log_error]
622647
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> {
623660
let parent_block_index = self.check_block_parent(header)?;
624661
self.check_header_size(header)?;
625662
self.enforce_checkpoints(header)?;
@@ -681,7 +718,7 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
681718
ensure!(
682719
block_timestamp.as_duration_since_epoch() <= current_time_as_secs + max_future_offset,
683720
CheckBlockError::BlockFromTheFuture {
684-
block_id: header.block_id(),
721+
block_id: WithId::id(header),
685722
block_timestamp,
686723
current_time
687724
},
@@ -852,7 +889,14 @@ impl<'a, S: BlockchainStorageRead, V: TransactionVerificationStrategy> Chainstat
852889

853890
#[log_error]
854891
pub fn check_block(&self, block: &WithId<Block>) -> Result<(), CheckBlockError> {
855-
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)?;
856900

857901
self.check_block_size(block).map_err(CheckBlockError::BlockSizeError)?;
858902

chainstate/src/detail/error.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,17 @@ pub enum CheckBlockError {
167167
},
168168
#[error("CRITICAL: Failed to retrieve ancestor of submitted block: {0}")]
169169
GetAncestorError(#[from] GetAncestorError),
170-
#[error("Attempted to add a block before reorg limit (attempted at height: {0} while current height is: {1} and min allowed is: {2})")]
171-
AttemptedToAddBlockBeforeReorgLimit(BlockHeight, BlockHeight, BlockHeight),
170+
#[error(
171+
"Attempted to add a block before reorg limit (attempted at height: {} while current height is: {} and min allowed is: {})",
172+
common_ancestor_height,
173+
tip_block_height,
174+
min_allowed_height
175+
)]
176+
AttemptedToAddBlockBeforeReorgLimit {
177+
common_ancestor_height: BlockHeight,
178+
tip_block_height: BlockHeight,
179+
min_allowed_height: BlockHeight,
180+
},
172181
#[error("TransactionVerifier error: {0}")]
173182
TransactionVerifierError(#[from] TransactionVerifierStorageError),
174183
#[error("Error during sealing an epoch: {0}")]
@@ -180,6 +189,8 @@ pub enum CheckBlockError {
180189
},
181190
#[error("In-memory reorg failed: {0}")]
182191
InMemoryReorgFailed(#[from] InMemoryReorgError),
192+
#[error("Block {0} has already been processed and marked as invalid")]
193+
InvalidBlockAlreadyProcessed(Id<Block>),
183194
}
184195

185196
#[derive(Error, Debug, PartialEq, Eq, Clone)]

chainstate/src/detail/error_classification.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,11 @@ impl BlockProcessingErrorClassification for CheckBlockError {
170170
| CheckBlockError::BlockTimeOrderInvalid(_, _)
171171
| CheckBlockError::InvalidBlockRewardOutputType(_)
172172
| CheckBlockError::CheckpointMismatch { .. }
173-
| CheckBlockError::AttemptedToAddBlockBeforeReorgLimit(_, _, _)
174-
| CheckBlockError::InvalidParent { .. } => BlockProcessingErrorClass::BadBlock,
173+
| CheckBlockError::AttemptedToAddBlockBeforeReorgLimit { .. }
174+
| CheckBlockError::InvalidParent { .. }
175+
| CheckBlockError::InvalidBlockAlreadyProcessed(_) => {
176+
BlockProcessingErrorClass::BadBlock
177+
}
175178

176179
CheckBlockError::BlockFromTheFuture { .. } => {
177180
BlockProcessingErrorClass::TemporarilyBadBlock

0 commit comments

Comments
 (0)