From 1a1e791b1ef7761616a2b7e4bfdf8529cdc1235c Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 22 Sep 2026 20:50:06 +0100 Subject: [PATCH] fix(chain): Assume CF header is checked when in header only Headers-only sync caused a condition where, if more than 1 peer is used to sync, the CF headers would not have a commitment when doing a handshake with the second peer, causing the node to download all the CF headers from the checkpoint. Whenever `assume_checked_to` is called, the filter headers are also marked as checked here. --- src/chain/chain.rs | 5 ++++- src/chain/graph.rs | 42 +++++++++++++----------------------------- 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/chain/chain.rs b/src/chain/chain.rs index 1899b8b..76088ee 100644 --- a/src/chain/chain.rs +++ b/src/chain/chain.rs @@ -270,6 +270,9 @@ impl Chain { if data.height.eq(&0) { break; } + if data.cf_header_assumed { + break; + } match data.filter_commitment { Some(commitment) => { prev_header = Some(commitment.header); @@ -393,7 +396,7 @@ impl Chain { // Clear the filter header cache to rescan the filters for new scripts. pub(crate) fn clear_filters(&mut self) { - self.header_chain.reset_all_filters(); + self.header_chain.reset_filter_state(); } pub(crate) fn send_chain_update(&self) { diff --git a/src/chain/graph.rs b/src/chain/graph.rs index 7704cb6..eeed8c4 100644 --- a/src/chain/graph.rs +++ b/src/chain/graph.rs @@ -74,6 +74,7 @@ pub(crate) struct BlockNode { pub acc_work: Work, pub filter_commitment: Option, pub filter_checked: bool, + pub cf_header_assumed: bool, } impl BlockNode { @@ -84,6 +85,7 @@ impl BlockNode { acc_work, filter_commitment: None, filter_checked: false, + cf_header_assumed: false, } } } @@ -404,7 +406,8 @@ impl BlockTree { let mut curr = self.tip_hash(); while let Some(node) = self.headers.get_mut(&curr) { if node.height <= assumed_height { - node.filter_checked = true + node.filter_checked = true; + node.cf_header_assumed = true; } curr = node.header.prev_blockhash } @@ -417,38 +420,17 @@ impl BlockTree { false } - pub(crate) fn reset_all_filters(&mut self) { - let mut curr = self.tip_hash(); - while self.headers.get_mut(&curr).is_some() { - match self.headers.get_mut(&curr) { - Some(node) => { - node.filter_checked = false; - curr = node.header.prev_blockhash; - } - None => break, - } - } - for fork in &self.candidate_forks { - curr = fork.hash; - while self.headers.get_mut(&curr).is_some() { - match self.headers.get_mut(&curr) { - Some(node) => { - if !node.filter_checked { - break; - } - node.filter_checked = false; - curr = node.header.prev_blockhash; - } - None => break, - } - } + pub(crate) fn reset_filter_state(&mut self) { + for node in self.headers.values_mut() { + node.filter_checked = false; + node.cf_header_assumed = false; + node.filter_commitment = None; } } pub(crate) fn filter_headers_synced(&self) -> bool { self.iter_data() - .map(|node| node.filter_commitment) - .all(|commitment| commitment.is_some()) + .all(|node| node.filter_commitment.is_some() || node.cf_header_assumed) } pub(crate) fn filters_synced(&self) -> bool { @@ -461,7 +443,7 @@ impl BlockTree { pub(crate) fn total_filter_headers_synced(&self) -> u32 { self.iter_data() - .filter(|node| node.filter_commitment.is_some()) + .filter(|node| node.filter_commitment.is_some() || node.cf_header_assumed) .count() as u32 } @@ -699,7 +681,9 @@ mod tests { } chain.assume_checked_to(3); assert!(!chain.filters_synced()); + assert!(!chain.filter_headers_synced()); chain.assume_checked_to(4); assert!(chain.filters_synced()); + assert!(chain.filter_headers_synced()); } }