Skip to content

feat: stream transactions and progress during sync#2242

Open
ANAMASGARD wants to merge 1 commit into
bitcoindevkit:masterfrom
ANAMASGARD:feature/stream-sync-progress-2202
Open

feat: stream transactions and progress during sync#2242
ANAMASGARD wants to merge 1 commit into
bitcoindevkit:masterfrom
ANAMASGARD:feature/stream-sync-progress-2202

Conversation

@ANAMASGARD

Copy link
Copy Markdown

Closes #2202

Description

Adds incremental sync progress and transaction streaming to Electrum and Esplora backends.

Wallets can register .on_event() on SyncRequest / FullScanRequest to receive:

  • PartialUpdate(TxUpdate) — newly discovered txs/metadata at fetch boundaries
  • AnchorsResolved(TxUpdate) — anchor-only delta after Electrum resolves confirmation proofs
  • ItemStarted / SpkStarted — progress events (.inspect() is sugar over these)

TxUpdate::drain_since() destructively moves data out of the working buffer at each emission boundary. When events consume everything, response.tx_update is naturally empty — applying it at the end is a safe no-op (no double-apply footgun).

Consumer contract:

let request = SyncRequest::builder()
    .spks(spks)
    .on_event(|event| match event {
        SyncRequestEvent::PartialUpdate(delta)
        | SyncRequestEvent::AnchorsResolved(delta) => graph.apply_update(delta),
        _ => { /* progress UI */ }
    })
    .build();

let response = client.sync(request, batch_size, true)?;
graph.apply_update(response.tx_update); // empty when events drained all data

Emission points:

  • Electrum: after each SPK batch; once after txid/outpoint passes; AnchorsResolved after batch_fetch_anchors
  • Esplora: after each parallel batch (blocking + async)

Existing sync/full_scan signatures are unchanged (additive API).

Notes to the reviewers

  • .inspect() now routes through .on_event() internally (single reporter path); progress-only users are unaffected unless they also call .on_event().
  • emit_partial_updates defaults to false — draining only happens when .on_event() is used.
  • Electrum confirmed txs in PartialUpdate intentionally lack anchors until AnchorsResolved; see sync_defers_anchors_until_resolved test.
  • Esplora async uses EmitPartial trait on the concrete request (not dyn across .await) to satisfy Send.

Changelog notice

  • Added SyncRequestEvent, FullScanRequestEvent, FullScanProgress, and TxUpdateCursor types.
  • Added SyncRequestBuilder::on_event() and FullScanRequestBuilder::on_event().
  • Added TxUpdate::drain_since() for incremental delta extraction.
  • Electrum and Esplora now emit PartialUpdate events during sync/full_scan when .on_event() is registered.

Checklists

All Submissions:

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR
Screenshot From 2026-07-11 14-54-26 --- image --- image --- image

---

## What each test proves for #2202

| Test | Proves |
|---|---|
| `drain_since_*` | Delta extraction is correct; partials reconstruct full update |
| `final_response_empty_after_full_drain` | No double-apply when streaming |
| `sync_on_event_fires_item_started` | `.on_event()` builder works |
| `sync_streams_partial_updates` (electrum + esplora) | `PartialUpdate` fires **before** sync returns; final `tx_update` empty |
| `sync_defers_anchors_until_resolved` | Electrum anchor semantics correct |

Add .on_event() API with PartialUpdate and AnchorsResolved events,
TxUpdate::drain_since() for safe incremental application, and wire
emission into Electrum and Esplora backends. Addresses bitcoindevkit#2202.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Stream transactions and progress in syncing

1 participant