-
Notifications
You must be signed in to change notification settings - Fork 3
feat: validate cfilter hash against cfheader chain #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1362,6 +1362,32 @@ impl< | |
| let mut wallet = self.wallet.write().await; | ||
|
|
||
| // Check filter against wallet if available | ||
| // First, verify filter data matches expected filter header chain | ||
| let height = storage | ||
| .get_header_height_by_hash(&cfilter.block_hash) | ||
| .await | ||
| .map_err(|e| SyncError::Storage(format!("Failed to get filter block height: {}", e)))? | ||
| .ok_or_else(|| { | ||
| SyncError::Validation(format!( | ||
| "Block height not found for cfilter block {}", | ||
| cfilter.block_hash | ||
| )) | ||
| })?; | ||
|
|
||
| let header_ok = self | ||
| .filter_sync | ||
| .verify_cfilter_against_headers(&cfilter.filter, height, &*storage) | ||
| .await?; | ||
|
|
||
| if !header_ok { | ||
| tracing::warn!( | ||
| "Rejecting CFilter for block {} at height {} due to header mismatch", | ||
| cfilter.block_hash, | ||
| height | ||
| ); | ||
| return Ok(()); | ||
| } | ||
|
Comment on lines
+1365
to
+1389
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t leak filter requests on header mismatch We bail out before calling 🤖 Prompt for AI Agents |
||
|
|
||
| let matches = self | ||
| .filter_sync | ||
| .check_filter_for_matches( | ||
|
|
@@ -1963,6 +1989,20 @@ impl< | |
| .map_err(|e| SyncError::Storage(format!("Failed to get filter block height: {}", e)))? | ||
| .ok_or(SyncError::InvalidState("Filter block height not found".to_string()))?; | ||
|
|
||
| // Verify against expected header chain before storing | ||
| let header_ok = self | ||
| .filter_sync | ||
| .verify_cfilter_against_headers(&cfilter.filter, height, &*storage) | ||
| .await?; | ||
| if !header_ok { | ||
| tracing::warn!( | ||
| "Rejecting post-sync CFilter for block {} at height {} due to header mismatch", | ||
| cfilter.block_hash, | ||
| height | ||
| ); | ||
| return Ok(()); | ||
| } | ||
|
Comment on lines
+1993
to
+2004
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Post-sync filters need a retry path on header mismatch In the FullySynced path we drop the filter and return without re-requesting it, leaving that block without a stored filter (and the next header validation will continue to fail). We should treat this the same as the main sync path—clear the outstanding request and schedule a retry from another peer—instead of silently abandoning it. 🤖 Prompt for AI Agents |
||
|
|
||
| // Store the filter | ||
| storage | ||
| .store_filter(height, &cfilter.filter) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checkpoint-start filters are rejected forever
When we sync from a checkpoint the first filter range starts at
sync_base_height. This code then tries to load the previous filter header (height - 1), but we never store headers below the checkpoint base. As a resultprev_headerisNone,verify_cfilter_against_headersreturnsOk(false), and every filter at the base height is dropped. Because that height never marks as received, theDownloadingFiltersphase can’t finish and the sync stalls. Please skip header-chain verification for heights at or below the checkpoint base (mirroring the CFHeader logic) instead of rejecting those filters.if height == 0 { tracing::debug!("Skipping cfilter verification at genesis height 0"); return Ok(true); } + if self.sync_base_height > 0 && height <= self.sync_base_height { + tracing::debug!( + "Skipping cfilter verification at checkpoint base height {}", + height + ); + return Ok(true); + } + // Load previous and expected headers📝 Committable suggestion
🤖 Prompt for AI Agents