diff --git a/CHANGELOG.md b/CHANGELOG.md index 43653feb63ae..dc9c4ad35adc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ ### Fixed +- [#6241](https://github.com/ChainSafe/forest/pull/6241) Fixed a sync issue that is caused by time traveling block(s). + ## Forest v0.30.3 "Trishul" This is a non-mandatory release that brings important enhancements in Forest's tooling capabilities. diff --git a/src/chain_sync/bad_block_cache.rs b/src/chain_sync/bad_block_cache.rs index d195e2f1747a..2c4e3802577e 100644 --- a/src/chain_sync/bad_block_cache.rs +++ b/src/chain_sync/bad_block_cache.rs @@ -31,6 +31,7 @@ impl BadBlockCache { pub fn push(&self, c: Cid) { self.cache.push(c.into(), ()); + tracing::warn!("Marked bad block: {c}"); } /// Returns `Some` if the block CID is in bad block cache. diff --git a/src/chain_sync/chain_follower.rs b/src/chain_sync/chain_follower.rs index c56e93f54c6f..651761b55c76 100644 --- a/src/chain_sync/chain_follower.rs +++ b/src/chain_sync/chain_follower.rs @@ -230,6 +230,7 @@ pub async fn chain_follower( let state_machine = state_machine.clone(); let state_changed = state_changed.clone(); let tasks = tasks.clone(); + let bad_block_cache = bad_block_cache.clone(); async move { loop { state_changed.notified().await; @@ -256,6 +257,7 @@ pub async fn chain_follower( network.clone(), state_manager.clone(), stateless_mode, + bad_block_cache.clone(), ); tokio::spawn({ let state_machine = state_machine.clone(); @@ -679,16 +681,8 @@ impl SyncStateMachine { // Remove all bad tipsets from the tipset map. fn mark_bad_tipset(&mut self, tipset: Arc) { let mut stack = vec![tipset]; - while let Some(tipset) = stack.pop() { self.tipsets.remove(tipset.key()); - // Mark all blocks in the tipset as bad - if let Some(bad_block_cache) = &self.bad_block_cache { - for block in tipset.blocks() { - bad_block_cache.push(*block.cid()); - } - } - // Find all descendant tipsets (tipsets that have this tipset as a parent) let mut to_remove = Vec::new(); let mut descendants = Vec::new(); @@ -837,6 +831,7 @@ impl SyncTask { network: SyncNetworkContext, state_manager: Arc>, stateless_mode: bool, + bad_block_cache: Option>, ) -> Option { tracing::trace!("SyncTask::execute {self}"); let cs = state_manager.chain_store(); @@ -853,8 +848,14 @@ impl SyncTask { is_proposed_head, } => { let genesis = cs.genesis_tipset(); - match validate_tipset(state_manager.clone(), cs, tipset.deref().clone(), &genesis) - .await + match validate_tipset( + state_manager.clone(), + cs, + tipset.deref().clone(), + &genesis, + bad_block_cache, + ) + .await { Ok(()) => Some(SyncEvent::ValidatedTipset { tipset, diff --git a/src/chain_sync/tipset_syncer.rs b/src/chain_sync/tipset_syncer.rs index 81808227edd7..4322d851f13b 100644 --- a/src/chain_sync/tipset_syncer.rs +++ b/src/chain_sync/tipset_syncer.rs @@ -3,6 +3,7 @@ use std::sync::Arc; +use crate::chain_sync::BadBlockCache; use crate::networks::Height; use crate::shim::clock::ALLOWABLE_CLOCK_DRIFT; use crate::shim::crypto::SignatureType; @@ -101,6 +102,7 @@ pub async fn validate_tipset( chainstore: &ChainStore, full_tipset: FullTipset, genesis: &Tipset, + bad_block_cache: Option>, ) -> Result<(), TipsetSyncerError> { if full_tipset.key().eq(genesis.key()) { trace!("Skipping genesis tipset validation"); @@ -124,12 +126,18 @@ pub async fn validate_tipset( chainstore.add_to_tipset_tracker(block.header()); } Err((cid, why)) => { - warn!( - "Validating block [CID = {}] in EPOCH = {} failed: {}", - cid.clone(), - epoch, - why - ); + warn!("Validating block [CID = {cid}] in EPOCH = {epoch} failed: {why}"); + match &why { + TipsetSyncerError::TimeTravellingBlock(_, _) => { + // Do not mark a block as bad for temporary errors. + // See in Lotus + } + _ => { + if let Some(bad_block_cache) = bad_block_cache { + bad_block_cache.push(cid); + } + } + }; return Err(why); } }