Skip to content

Update block Emitter#2244

Open
ValuedMammal wants to merge 7 commits into
bitcoindevkit:masterfrom
ValuedMammal:feat/emitter-issue-2176
Open

Update block Emitter#2244
ValuedMammal wants to merge 7 commits into
bitcoindevkit:masterfrom
ValuedMammal:feat/emitter-issue-2176

Conversation

@ValuedMammal

Copy link
Copy Markdown
Collaborator

Description

Replace bitcoincore-rpc with bdk_bitcoind_client - Emitter and FilterIter now borrow a concrete &bdk_bitcoind_client::Client instead of being generic over C: Deref<Target: RpcApi>. Core v28 is the default target; passthrough features (29_0, 30_0) let callers target newer nodes.

Generic CheckPoint<B> in Emitter and FilterIter - Both types now carry a block-data type parameter B: From<Header>. This lets callers thread full Headers through emissions rather than only BlockHashes. BlockEvent<B> and bip158::Event<B> follow the same bound. An as_header helper in bip158.rs reconstructs a Header from GetBlockHeaderVerbose for the emitted checkpoint.

Tidy poll loop and Emitter API:

  • EmitterError is introduced; next_block and mempool methods now return it
  • EmitterError::AgreementNotFound surfaces the wrong-network case instead of silently replacing the passed-in checkpoint with a height-0 genesis checkpoint (bounded by MAX_AGREEMENT_FAILURES = 3)
  • start_height is converted from a constructor parameter to a builder method; Emitter::new defaults to last_cp.height()
  • BlockEvent::connected_to() is fixed to derive from (height - 1, prev_blockhash) when no previous checkpoint exists
  • raw_mempool() extracted as a private helper for tip-consistent snapshots
  • NO_EXPECTED_MEMPOOL_TXS removed (was hurting type inference at call sites)

Documentation — README rewritten with a usage overview and a runnable example, wired in as crate-level docs via #![doc = include_str!("../README.md")]. Poll state-machine docs (PollResponse, poll_once, poll), next_block, and connected_to are tidied throughout.

Test rewritetest_emitter.rs rewritten with 14 behavior-focused tests covering: block ordering, reorgs, mempool snapshots, eviction withholding behind tip, birthday checkpoint, start_height semantics, AgreementNotFound on wrong genesis, and out-of-bounds start_height.

electrsd bumped to 0.41.0 in bdk_core and bdk_testenv.

Fixes #2176

Notes to the reviewers

The bitcoincore-rpcbdk_bitcoind_client swap is the most structurally impactful change. The emission logic itself is unchanged — only the RPC call sites and response types are migrated. The generics on Emitter<'a, B> / FilterIter<'a, B> are purely additive: existing callers using CheckPoint<BlockHash> compile without changes (as BlockHash: From<Header> is already implemented).

The AgreementNotFound error is a deliberate behavior change — previously a sync against a wrong network would silently replace the caller's genesis checkpoint.

Changelog notice

bdk_bitcoind_rpc

  • Changed: Replace bitcoincore-rpc dependency with bdk_bitcoind_client; Emitter and FilterIter now borrow &bdk_bitcoind_client::Client directly
  • Changed: Emitter is now Emitter<'a, B> and BlockEvent is BlockEvent<B> where B: From<bitcoin::block::Header>; same for FilterIter<'a, B> and Event<B>
  • Changed: start_height moved from Emitter::new to a new builder method Emitter::start_height
  • Added: EmitterError enum returned by next_block, mempool, and mempool_at
  • Added: EmitterError::AgreementNotFound surfaces wrong-network / no-common-ancestor failures instead of silently replacing the checkpoint with genesis
  • Fixed: BlockEvent::connected_to() now correctly derives (height - 1, prev_blockhash) when no previous checkpoint exists
  • Removed: NO_EXPECTED_MEMPOOL_TXS constant
  • Docs: README rewritten with usage overview and runnable example

bdk_core / bdk_testenv

  • Changed: Bump electrsd to 0.41.0

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

bitcoincore-rpc is effectively unmaintained. Swap it out
for bdk_bitcoind_client (bitreq backend + corepc_types), which is
maintained under the bitcoindevkit org.

The Emitter and FilterIter now borrow a concrete `&Client` instead of being
generic over `C: Deref<Target: RpcApi>`, since the new client does not
implement the old RpcApi trait. All RPC call sites and response types
are migrated to the new client while preserving emission logic unchanged.

Core v28 is the default (`28_0`); `29_0`/`30_0` passthrough features let callers
target newer nodes.
The Emitter struct, its impl, MempoolEvent, BlockEvent, PollResponse,
the poll/poll_once helpers, NO_EXPECTED_MEMPOOL_TXS, and the inline
test module are relocated from lib.rs into a new src/emitter.rs.
lib.rs now declares `mod emitter` and re-exports its public items,
keeping the crate's public API unchanged.
Return a bounded `EmitterError::AgreementNotFound` instead of silently
resetting to the genesis checkpoint when no agreement block is found.
This surfaces the wrong-network case (e.g. a testnet checkpoint polled
against a mainnet node) to the caller rather than resetting emission
to genesis. Agreement failures are bounded by
`MAX_AGREEMENT_FAILURES` (3).

Other changes:
- Extract private `Emitter::raw_mempool()` for the tip-consistent mempool
  snapshot, replacing the mut-before-loop pattern.
- Fix `BlockEvent::connected_to()` to derive the connected block from
  `(height - 1, header.prev_blockhash)` instead of returning itself when
  there is no previous checkpoint.
- Convert `start_height` from a constructor parameter into a
  builder method. `Emitter::new` is simplified by using last_cp.height()
  as the default start height.
- Introde `EmitterError` and use it in the definition of `next_block`
  and mempool methods.
- Remove NO_EXPECTED_MEMPOOL_TXS, which restricted type inference
  at the call site.
…ADME

Rewrite the crate README with a usage overview and a runnable example, and
wire it in as the crate-level docs via `#![doc = include_str!("../README.md")]`.

Correct the mempool timestamp semantics: `MempoolEvent::update`/`evicted` are
timestamped with the `sync_time` of the call, not the time a transaction was
first seen or evicted. Document that this is a full snapshot, not a delta, and
that evictions are only reported once the emitter is at tip.

Tidy the poll state-machine docs (`PollResponse`, `poll_once`, `poll`),
`next_block`, and `connected_to`, and trim redundant field comments so the
code stays self-describing. Documentation only; no behavior change.
Rewrite the emitter integration tests with behavior-focused names and
expanded coverage. Each test now documents the scenario it exercises:

- blocks_emitted_in_order_and_after_reorg
- unconfirmed_txs_anchored_on_confirmation
- ensure_block_emitted_after_reorg_is_at_reorg_height
- tx_can_become_unconfirmed_after_reorg
- mempool_update_is_complete_snapshot
- reorg_past_start_checkpoint
- double_spent_tx_evicted_and_removed_from_canonical_set
- detect_new_mempool_txs
- birthday_checkpoint_skips_earlier_blocks
- mempool_at_uses_provided_timestamp
- start_height_reset_on_reorg_prevents_height_gaps
- evictions_withheld_until_at_tip
- start_height_beyond_tip_returns_rpc_error
- wrong_genesis_returns_agreement_not_found

New coverage includes behind-tip eviction withholding (live and
wallet-restart paths), the start_height birthday guarantee, custom
mempool_at sync_time semantics, and the recoverable EmitterError cases
(Rpc for an out-of-range start_height, AgreementNotFound for a wrong
genesis hash).
Introduce a generic block-data type parameter B on Emitter and FilterIter
so callers can thread full Headers through emissions, not just BlockHash.

- emitter: Emitter<'a> is now Emitter<'a, B> and BlockEvent is
  BlockEvent<B>. Added B: From<Header> to existing generic bounds.
- bip158: FilterIter<'a> and Event are now FilterIter<'a, B> and Event<B>
  with the same bound on its Iterator impl. A private `as_header` helper
  reconstructs a bitcoin Header from GetBlockHeaderVerbose so the emitted
  checkpoint carries the header.
- bip158: Error is now #[non_exhaustive]
- test: Add `emitter_collects_header_checkpoints` to exercise Emitter with
  CheckPoint<Header>
The `download` feature now enables electrsd/bitcoind_30_2.
This brings the downloaded bitcoind version in line with the
latest version feature in bdk_bitcoind_rpc.

electrsd 0.41.0 throws a compile error if no electrs version is
set, causing compilation of bdk_core to fail when no testenv
default features are set. We can't specify an electrs version
directly as a feature of electrsd because that breaks docs.rs
which has no network access when building the docs.

Instead enable bdk_testenv default features in
crates/core/Cargo.toml to ensure a version of electrs is set.
However it's valid to note that since bdk_core only uses
the test macros (not TestEnv), another option is to make
electrsd an optional dependency in bdk_testenv, explicitly
enabled by the download feature. That way crates like bdk_core
can use bdk_testenv without pulling in the extra download
dependencies.
@ValuedMammal ValuedMammal self-assigned this Jul 13, 2026
@ValuedMammal ValuedMammal added dependencies Pull requests that update a dependency file new feature New feature or request discussion There's still a discussion ongoing labels Jul 13, 2026
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.77249% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.60%. Comparing base (1acb4d0) to head (5a3298e).

Files with missing lines Patch % Lines
crates/bitcoind_rpc/src/emitter.rs 86.74% 19 Missing and 3 partials ⚠️
crates/bitcoind_rpc/src/bip158.rs 86.95% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2244      +/-   ##
==========================================
+ Coverage   78.32%   78.60%   +0.27%     
==========================================
  Files          30       31       +1     
  Lines        5934     5922      -12     
  Branches      281      276       -5     
==========================================
+ Hits         4648     4655       +7     
+ Misses       1210     1192      -18     
+ Partials       76       75       -1     
Flag Coverage Δ
rust 78.60% <86.77%> (+0.27%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file discussion There's still a discussion ongoing new feature New feature or request

Projects

Status: Needs Review

Development

Successfully merging this pull request may close these issues.

[bitcoind_rpc] Plans to stabilize the Emitter API?

2 participants