Skip to content

feat: rebroadcast unconfirmed self-sent transactions#627

Open
xdustinface wants to merge 1 commit intov0.42-devfrom
feat/rebroadcast-unconfirmed-tx
Open

feat: rebroadcast unconfirmed self-sent transactions#627
xdustinface wants to merge 1 commit intov0.42-devfrom
feat/rebroadcast-unconfirmed-tx

Conversation

@xdustinface
Copy link
Copy Markdown
Collaborator

@xdustinface xdustinface commented Apr 3, 2026

Each self-sent transaction in recent_sends tracks when it was last broadcast. Transactions whose last broadcast was more than 10 minutes ago are rebroadcast to all peers on each tick.

Add BroadcastMessage variant to NetworkRequest so the mempool manager can request a broadcast via the request queue.

Clean up recent_sends when a transaction is confirmed or IS-locked, since there is no need to rebroadcast finalized transactions.

This will only work properly with #626 merged since we don't record self sent transactions without that PR.

Summary by CodeRabbit

  • New Features

    • Unconfirmed transactions are now automatically rebroadcast to peers on a scheduled/dued basis.
  • Improvements

    • Transaction tracking data is cleaned up promptly when transactions are confirmed or marked InstantSend-locked.
    • Rebroadcasts are integrated into the periodic sync cycle so eligible transactions are retried without user action.
  • Bug Fixes

    • Broadcast attempts now log failures and report the number of peers that failed to receive a message.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5858b2b8-c96c-4ead-9158-da2628bdecc0

📥 Commits

Reviewing files that changed from the base of the PR and between b747d56 and ea9cd8e.

📒 Files selected for processing (5)
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/mod.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
  • dash-spv/src/types.rs
✅ Files skipped from review due to trivial changes (2)
  • dash-spv/src/network/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • dash-spv/src/types.rs

📝 Walkthrough

Walkthrough

A mempool rebroadcast feature was added: the mempool manager identifies recently-sent unconfirmed transactions due for rebroadcast and issues NetworkMessage::Tx broadcasts via a new NetworkRequest::BroadcastMessage path; network manager spawns async tasks to broadcast to peers and logs per-peer failures.

Changes

Cohort / File(s) Summary
Network Broadcast Infrastructure
dash-spv/src/network/mod.rs, dash-spv/src/network/manager.rs
Added NetworkRequest::BroadcastMessage(NetworkMessage) and RequestSender::broadcast(...) to enqueue broadcasts. Manager handles BroadcastMessage by spawning an async task that calls broadcast(msg) and logs number of per-peer Err results.
Mempool Rebroadcast Logic
dash-spv/src/sync/mempool/manager.rs, dash-spv/src/sync/mempool/sync_manager.rs, dash-spv/src/types.rs
Added MempoolState::take_rebroadcast_due(Duration) and MempoolManager::rebroadcast_if_due(...). Integrated rebroadcast call into tick() after send_queued. Adjusted recent_sends cleanup on confirmations and InstantSend locking; tests updated/added to cover rebroadcast timing and semantics.

Sequence Diagram(s)

sequenceDiagram
    participant Sync as Sync Tick
    participant MempoolMgr as Mempool Manager
    participant State as MempoolState
    participant Request as RequestSender
    participant NetMgr as Network Manager
    participant Peers as Peers

    Sync->>MempoolMgr: tick()
    MempoolMgr->>MempoolMgr: send_queued(requests)
    MempoolMgr->>MempoolMgr: rebroadcast_if_due(requests)
    MempoolMgr->>State: take_rebroadcast_due(interval)
    State-->>MempoolMgr: Vec<Transaction>
    loop per transaction
        MempoolMgr->>Request: broadcast(NetworkMessage::Tx)
        Request->>NetMgr: enqueue BroadcastMessage
        NetMgr->>Peers: send to all peers (async)
        Peers-->>NetMgr: per-peer results
        NetMgr-->>MempoolMgr: counts of failures/total
        MempoolMgr->>MempoolMgr: log if failures > 0
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through mempool lanes tonight,

Rebroadcasting sparks of light,
I nudged each peer to hear the call,
So pending txs won't fade or fall,
A tiny rabbit, network-wide delight.

🚥 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 'feat: rebroadcast unconfirmed self-sent transactions' directly and accurately summarizes the main change across all modified files, clearly conveying the primary feature addition.
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rebroadcast-unconfirmed-tx

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.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 86.02151% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.87%. Comparing base (88e8a9a) to head (ea9cd8e).

Files with missing lines Patch % Lines
dash-spv/src/network/manager.rs 0.00% 12 Missing ⚠️
dash-spv/src/sync/mempool/manager.rs 98.43% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##           v0.42-dev     #627      +/-   ##
=============================================
+ Coverage      67.81%   67.87%   +0.05%     
=============================================
  Files            318      318              
  Lines          67976    68069      +93     
=============================================
+ Hits           46100    46201     +101     
+ Misses         21876    21868       -8     
Flag Coverage Δ
core 75.21% <ø> (ø)
ffi 38.34% <ø> (+0.05%) ⬆️
rpc 19.92% <ø> (ø)
spv 85.34% <86.02%> (+0.11%) ⬆️
wallet 67.56% <ø> (ø)
Files with missing lines Coverage Δ
dash-spv/src/network/mod.rs 74.19% <100.00%> (+1.46%) ⬆️
dash-spv/src/sync/mempool/sync_manager.rs 98.76% <ø> (ø)
dash-spv/src/types.rs 90.56% <100.00%> (+0.77%) ⬆️
dash-spv/src/sync/mempool/manager.rs 97.66% <98.43%> (+0.04%) ⬆️
dash-spv/src/network/manager.rs 64.34% <0.00%> (+0.91%) ⬆️

... and 16 files with indirect coverage changes

Copy link
Copy Markdown
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

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

349-353: Keep recent_sends cleanup inside MempoolState::remove_transaction.

Handling it only here fixes confirmations, but it leaves the invariant split across callers. Any other path removing a tx in dash-spv/src/types.rs can still orphan a recent_sends entry.

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

In `@dash-spv/src/sync/mempool/manager.rs` around lines 349 - 353, The loop in
manager.rs is performing recent_sends cleanup outside the state, splitting the
invariant; update MempoolState::remove_transaction (in dash-spv/src/types.rs) to
also remove the txid from its recent_sends map when a transaction is removed and
return a clear indicator (e.g., Option or bool) that the tx was removed, then
delete the external recent_sends removal from the loop in mempool/manager.rs and
use the returned indicator to populate removed; ensure the unique symbol names
referenced are MempoolState::remove_transaction, recent_sends, and the loop over
txids in the mempool_state.write() block so all callers rely on the single
internal cleanup.
🤖 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/mempool/manager.rs`:
- Around line 423-427: Current code calls take_rebroadcast_due() which resets
timestamps up front and then ignores the Result from RequestSender::broadcast(),
causing txs to be suppressed even on send failure; change the flow to first
collect due txs without consuming the rebroadcast window (e.g., add or use a
non-destructive accessor like list_rebroadcast_due or a peek_rebroadcast_due on
mempool_state), attempt to broadcast each tx and check the Result from
RequestSender::broadcast(), and only call the mutation that marks a tx as
rebroadcasted (the counterpart of take_rebroadcast_due, e.g., mark_rebroadcasted
or consuming method) after a successful send; also stop discarding the broadcast
Result so failures can be retried or the timestamp left unchanged.
- Around line 371-374: The code marks a transaction InstantSend
(tx.is_instant_send = true) but doesn't move its pending amount into the
InstantSend bucket, causing pending balance drift and negative totals when
remove_transaction() later subtracts from pending_instant_balance. Fix by
transferring the transaction's pending amount from state.pending_balance into
state.pending_instant_balance at the same spot where you set tx.is_instant_send
(i.e., decrement state.pending_balance and increment
state.pending_instant_balance by the transaction's pending amount field), and
ensure the same unique field name you use for the tx pending amount is used so
remove_transaction() and other logic remain consistent.

---

Nitpick comments:
In `@dash-spv/src/sync/mempool/manager.rs`:
- Around line 349-353: The loop in manager.rs is performing recent_sends cleanup
outside the state, splitting the invariant; update
MempoolState::remove_transaction (in dash-spv/src/types.rs) to also remove the
txid from its recent_sends map when a transaction is removed and return a clear
indicator (e.g., Option or bool) that the tx was removed, then delete the
external recent_sends removal from the loop in mempool/manager.rs and use the
returned indicator to populate removed; ensure the unique symbol names
referenced are MempoolState::remove_transaction, recent_sends, and the loop over
txids in the mempool_state.write() block so all callers rely on the single
internal cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c86f9796-b03b-4c13-9304-093f3d938b20

📥 Commits

Reviewing files that changed from the base of the PR and between a7acd5a and b747d56.

📒 Files selected for processing (5)
  • dash-spv/src/network/manager.rs
  • dash-spv/src/network/mod.rs
  • dash-spv/src/sync/mempool/manager.rs
  • dash-spv/src/sync/mempool/sync_manager.rs
  • dash-spv/src/types.rs

@xdustinface xdustinface marked this pull request as draft April 3, 2026 06:28
@github-actions github-actions bot added the merge-conflict The PR conflicts with the target branch. label Apr 6, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

This PR has merge conflicts with the base branch. Please rebase or merge the base branch into your branch to resolve them.

Each self-sent transaction in `recent_sends` tracks when it was last
broadcast. Transactions whose last broadcast was more than 10 minutes
ago are rebroadcast to all peers on each tick.

Add `BroadcastMessage` variant to `NetworkRequest` so the mempool
manager can request a broadcast via the request queue.

Clean up `recent_sends` when a transaction is confirmed or IS-locked,
since there is no need to rebroadcast finalized transactions.
@xdustinface xdustinface force-pushed the feat/rebroadcast-unconfirmed-tx branch from b747d56 to ea9cd8e Compare April 7, 2026 00:02
@github-actions github-actions bot removed the merge-conflict The PR conflicts with the target branch. label Apr 7, 2026
@xdustinface xdustinface marked this pull request as ready for review April 7, 2026 00:03
@xdustinface
Copy link
Copy Markdown
Collaborator Author

xdustinface commented Apr 7, 2026

@CodeRabbit review!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 7, 2026

@xdustinface Sure, I'll review the changes in PR #627 right away!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions github-actions bot added the ready-for-review CodeRabbit has approved this PR label Apr 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-review CodeRabbit has approved this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant