diff --git a/src/chain_sync/chain_follower.rs b/src/chain_sync/chain_follower.rs index 5a1313653a6..63b52d2000e 100644 --- a/src/chain_sync/chain_follower.rs +++ b/src/chain_sync/chain_follower.rs @@ -251,8 +251,7 @@ async fn chain_follower( if let Err(reason) = GossipBlockValidator::new(&b).validate_pre_fetch( &genesis, cfg.block_delay_secs, - cfg.policy.chain_finality, - cs.heaviest_tipset().epoch(), + cs.ec_calculator_finalized_epoch(), // Not using F3 finalized epoch as it could go above the chain head during catchup bad_block_cache.as_ref(), &seen_block_cache, ) { @@ -713,11 +712,9 @@ impl SyncStateMachine { return; } - // Check if tipset is outside the chain_finality window - let heaviest = self.cs.heaviest_tipset(); - let epoch_diff = heaviest.epoch() - tipset.epoch(); - - if epoch_diff > self.cs.chain_config().policy.chain_finality { + // Check if tipset is outside the chain finality window. + // Not using F3 finalized epoch as it could go above the chain head during catchup + if tipset.epoch() < self.cs.ec_calculator_finalized_epoch() { self.mark_bad_tipset(tipset); return; } diff --git a/src/chain_sync/validation.rs b/src/chain_sync/validation.rs index 8832be1619b..43c352f987a 100644 --- a/src/chain_sync/validation.rs +++ b/src/chain_sync/validation.rs @@ -169,8 +169,11 @@ impl TipsetValidator<'_> { pub enum GossipBlockRejectReason { #[error("block epoch {0} is too far in the future")] EpochTooFarAhead(ChainEpoch), - #[error("block epoch {0} is beyond finality (heaviest: {1})")] - EpochBeyondFinality(ChainEpoch, ChainEpoch), + #[error("block epoch {epoch} is beyond finality (finalized: {finalized_epoch})")] + EpochBeyondFinality { + epoch: ChainEpoch, + finalized_epoch: ChainEpoch, + }, #[error("block epoch {0} is negative")] NegativeEpoch(ChainEpoch), #[error("block timestamp {timestamp} inconsistent with epoch {epoch} (expected {expected})")] @@ -197,7 +200,7 @@ impl GossipBlockRejectReason { pub fn label(&self) -> &'static str { match self { Self::EpochTooFarAhead(_) => "epoch_too_far_ahead", - Self::EpochBeyondFinality(_, _) => "epoch_beyond_finality", + Self::EpochBeyondFinality { .. } => "epoch_beyond_finality", Self::NegativeEpoch(_) => "negative_epoch", Self::TimestampMismatch { .. } => "timestamp_mismatch", Self::MissingSignature => "missing_signature", @@ -228,14 +231,13 @@ impl<'a> GossipBlockValidator<'a> { &self, genesis_tipset: &Tipset, block_delay: u32, - chain_finality: ChainEpoch, - heaviest_epoch: ChainEpoch, + finalized_epoch: ChainEpoch, bad_block_cache: Option<&BadBlockCache>, seen_block_cache: &SeenBlockCache, ) -> Result<(), GossipBlockRejectReason> { let cid = *self.block.header.cid(); Self::check_bad_block_cache(cid, bad_block_cache)?; - self.validate_epoch_range(genesis_tipset, block_delay, chain_finality, heaviest_epoch)?; + self.validate_epoch_range(genesis_tipset, block_delay, finalized_epoch)?; self.validate_timestamp(genesis_tipset, block_delay)?; self.validate_election_proof()?; self.validate_signature_present()?; @@ -272,8 +274,7 @@ impl<'a> GossipBlockValidator<'a> { &self, genesis_tipset: &Tipset, block_delay: u32, - chain_finality: ChainEpoch, - heaviest_epoch: ChainEpoch, + finalized_epoch: ChainEpoch, ) -> Result<(), GossipBlockRejectReason> { let epoch = self.block.header.epoch; if epoch < 0 { @@ -284,11 +285,11 @@ impl<'a> GossipBlockValidator<'a> { if epoch > max { return Err(GossipBlockRejectReason::EpochTooFarAhead(epoch)); } - if heaviest_epoch.saturating_sub(epoch) > chain_finality { - return Err(GossipBlockRejectReason::EpochBeyondFinality( + if epoch < finalized_epoch { + return Err(GossipBlockRejectReason::EpochBeyondFinality { epoch, - heaviest_epoch, - )); + finalized_epoch, + }); } Ok(()) } @@ -450,8 +451,7 @@ mod tests { let result = GossipBlockValidator::new(&block).validate_pre_fetch( &genesis, 30, // block_delay - 900, // chain_finality - 0, // heaviest_epoch (same as block epoch) + 0, // finalized_epoch None, // no bad block cache &seen, ); @@ -466,12 +466,12 @@ mod tests { assert!( GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .is_ok() ); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::DuplicateBlock(_))); } @@ -485,7 +485,7 @@ mod tests { bad_cache.push(*block.header.cid()); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, Some(&bad_cache), &seen) + .validate_pre_fetch(&genesis, 30, 0, Some(&bad_cache), &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::BadBlock(_))); } @@ -497,7 +497,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::EpochTooFarAhead(_))); } @@ -509,11 +509,11 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 1000, None, &seen) + .validate_pre_fetch(&genesis, 30, 100, None, &seen) .unwrap_err(); assert!(matches!( err, - GossipBlockRejectReason::EpochBeyondFinality(_, _) + GossipBlockRejectReason::EpochBeyondFinality { .. } )); } @@ -524,7 +524,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::MissingElectionProof)); } @@ -541,7 +541,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::InvalidWinCount(0))); } @@ -553,7 +553,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::MissingSignature)); } @@ -566,7 +566,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::TooManyMessages(_))); } @@ -578,7 +578,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::NegativeEpoch(-1))); } @@ -592,7 +592,7 @@ mod tests { let seen = SeenBlockCache::default(); let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!( err, @@ -612,13 +612,13 @@ mod tests { // First attempt: rejected as too far ahead let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::EpochTooFarAhead(_))); // Second attempt: must still be EpochTooFarAhead, NOT DuplicateBlock let err = GossipBlockValidator::new(&block) - .validate_pre_fetch(&genesis, 30, 900, 0, None, &seen) + .validate_pre_fetch(&genesis, 30, 0, None, &seen) .unwrap_err(); assert!(matches!(err, GossipBlockRejectReason::EpochTooFarAhead(_))); }