Skip to content

fix: avoid extra GetHeaders after post-sync header processing#486

Merged
xdustinface merged 1 commit intov0.42-devfrom
fix/avoid-tip-header-requests
Mar 4, 2026
Merged

fix: avoid extra GetHeaders after post-sync header processing#486
xdustinface merged 1 commit intov0.42-devfrom
fix/avoid-tip-header-requests

Conversation

@xdustinface
Copy link
Collaborator

@xdustinface xdustinface commented Mar 3, 2026

When synced, handle_headers_pipeline always calls send_pending() after receiving each new block header, triggering a follow-up GetHeaders that always returned empty. This causes lot of unnecessary round-trips and leaves the tip segment in a pending state that rejected the next actual header as "unrequested headers".

Use tip_was_complete to distinguish unsolicited headers from active catch-up responses. Only skip send_pending() and mark the tip complete for the unsolicited case, so multi-batch catch-up still works correctly.

Summary by CodeRabbit

  • New Features

    • Added a test helper to generate chained headers for easier end-to-end header tests.
  • Bug Fixes

    • Stop sending extra header requests after initial sync when the tip is already complete.
    • Ignore duplicate unsolicited headers that match the current tip to avoid redundant processing.
    • Improve tip-complete state management for cleaner handling of subsequent headers.
  • Tests

    • Added unit tests validating duplicate-header ignoring and tip-complete lifecycle.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

These changes add tip-complete state tracking and gating in block header sync to avoid unsolicited post‑sync requests, introduce a duplicate‑header guard, new pipeline helpers (is_tip_complete, mark_tip_complete), and a test utility to generate linked header chains for tests.

Changes

Cohort / File(s) Summary
Tip Completion & Manager Logic
dash-spv/src/sync/block_headers/manager.rs
Adds tip_was_complete tracking and uses it to conditionally avoid sending extra GetHeaders requests for unsolicited post‑sync headers; marks tip complete after storing ready batches when appropriate.
Pipeline: duplicate guard & tip helpers
dash-spv/src/sync/block_headers/pipeline.rs
Adds duplicate-header guard (ignore if first incoming header matches a segment's current_tip_hash). Adds pub(super) fn is_tip_complete(&self) and pub(super) fn mark_tip_complete(&mut self) and related tests for tip life‑cycle and duplicate handling.
Test utilities
dash/src/test_utils/block.rs
Adds Header::dummy_chain(count: usize, prev_blockhash: BlockHash) -> Vec<Header> to build linked header sequences for tests.
Tests & Test helpers
dash-spv/src/sync/block_headers/*.rs (tests)
Updates test imports to include NetworkRequest, RequestSender, adds create_test_request_sender helper and new test test_unsolicited_post_sync_header_does_not_trigger_get_headers.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Manager as BlockHeadersManager
    participant Pipeline as HeadersPipeline
    participant Network as NetworkRequest

    Client->>Manager: receive_headers(headers)
    activate Manager

    Manager->>Pipeline: receive_headers(headers)
    activate Pipeline
    alt Duplicate header (matches segment tip)
        Pipeline-->>Manager: None (ignored)
    else New headers
        Pipeline->>Pipeline: buffer/process headers
        Pipeline-->>Manager: Some(events)
    end
    deactivate Pipeline

    Manager->>Manager: tip_was_complete = Pipeline.is_tip_complete()

    alt Not syncing AND tip_was_complete
        Manager->>Manager: mark_tip_complete()
        Note over Manager: prepare state for next unsolicited header
    else Syncing OR tip not complete
        Manager->>Network: send_request(GetHeaders)
        activate Network
        Network-->>Manager: Request sent
        deactivate Network
    end

    deactivate Manager
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hop through headers, one by one,
Duplicates dropped, the clean work done.
Tips marked ready, no noisy asks,
Tests chained neatly for all my tasks.
✨ A quiet sync — that's my kind of fun!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix: avoiding extra GetHeaders requests after post-sync header processing, which is the core objective of the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/avoid-tip-header-requests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dash-spv/src/sync/block_headers/pipeline.rs`:
- Around line 195-201: The current guard in the pipeline that checks
headers[0].block_hash() against self.segments' current_tip_hash and returns
Ok(None) drops valid trailing headers when the batch overlaps; change it to skip
only the duplicate prefix: iterate over headers from the front,
advancing/trimming while header.block_hash() matches any s.current_tip_hash in
self.segments, then continue processing the remaining headers; if trimming
consumes the entire batch, then return Ok(None). Update the logic around the
existing first_hash / self.segments.iter().any(...) check in pipeline.rs so it
trims the duplicate prefix (using headers slicing/drain) instead of rejecting
the whole headers vector.

In `@dash/src/test_utils/block.rs`:
- Around line 47-57: The dummy_chain helper currently hardcodes consensus bits
(0x2100ffff) when constructing Header.bits; change dummy_chain to accept a bits
parameter (e.g., bits: u32 or CompactTarget) or retrieve the value from the
test/network params and pass it into the Header construction so Header { ...,
bits: CompactTarget::from_consensus(bits), ... } instead of the literal; update
all callers of dummy_chain to provide the appropriate bits (or wrap a
convenience overload that uses the network default) and ensure
CompactTarget::from_consensus is used consistently.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2824e52 and ee275b7.

📒 Files selected for processing (3)
  • dash-spv/src/sync/block_headers/manager.rs
  • dash-spv/src/sync/block_headers/pipeline.rs
  • dash/src/test_utils/block.rs

When synced, `handle_headers_pipeline` always calls
`send_pending()` after receiving each new block header, triggering a
follow-up `GetHeaders` that always returned empty. This causes lot of
unnecessary round-trips and leaves the tip segment in a pending state
that rejected the next actual header as "unrequested headers".

Use `tip_was_complete` to distinguish unsolicited headers from active
catch-up responses. Only skip `send_pending()` and mark the tip complete
for the unsolicited case, so multi-batch catch-up still works correctly.
@xdustinface xdustinface force-pushed the fix/avoid-tip-header-requests branch from ee275b7 to 7acd86f Compare March 3, 2026 07:12
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
dash-spv/src/sync/block_headers/manager.rs (1)

313-317: Consider extracting duplicated test helper.

create_test_request_sender is duplicated in both pipeline.rs (lines 348-352) and here. Consider extracting to a shared test utilities module to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dash-spv/src/sync/block_headers/manager.rs` around lines 313 - 317, The
helper function create_test_request_sender is duplicated between manager.rs and
pipeline.rs; extract it into a shared test utilities module (e.g., tests::utils
or a new crate-level tests_helpers module) and update both locations to import
and call that single helper; ensure the new module returns (RequestSender,
tokio::sync::mpsc::UnboundedReceiver<NetworkRequest>) and constructs the channel
with unbounded_channel() so RequestSender::new(tx) behavior is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@dash-spv/src/sync/block_headers/manager.rs`:
- Around line 313-317: The helper function create_test_request_sender is
duplicated between manager.rs and pipeline.rs; extract it into a shared test
utilities module (e.g., tests::utils or a new crate-level tests_helpers module)
and update both locations to import and call that single helper; ensure the new
module returns (RequestSender,
tokio::sync::mpsc::UnboundedReceiver<NetworkRequest>) and constructs the channel
with unbounded_channel() so RequestSender::new(tx) behavior is preserved.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee275b7 and 7acd86f.

📒 Files selected for processing (3)
  • dash-spv/src/sync/block_headers/manager.rs
  • dash-spv/src/sync/block_headers/pipeline.rs
  • dash/src/test_utils/block.rs

@xdustinface xdustinface merged commit 0882855 into v0.42-dev Mar 4, 2026
53 checks passed
@xdustinface xdustinface deleted the fix/avoid-tip-header-requests branch March 4, 2026 04:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants