]> Untitled Git - bdk/log
bdk
3 hours agoMerge bitcoindevkit/bdk#2354: docs(core): document hashbrown feature's fixed-key... master github/master
merge-script [Mon, 19 Oct 2026 12:33:12 +0000 (23:33 +1100)]
Merge bitcoindevkit/bdk#2354: docs(core): document hashbrown feature's fixed-key hashing

360898e08226482b0ed4951f28b63f53053b89da docs(core): document hashbrown feature's fixed-key hashing (Romansage6)

Pull request description:

  ### Description

  With the `hashbrown` feature, `bdk_core`'s `HashMap`/`HashSet` are hashbrown's, on `std` builds too. These hash with `ahash`, which uses fixed, publicly known keys unless `ahash`'s `runtime-rng` or `compile-time-rng` feature is enabled somewhere in the dependency graph. With fixed keys, a malicious chain source can send transactions whose txids collide in the hash table and make `TxGraph` insertion quadratic.

  This adds a `# Features` section to `bdk_core`'s crate docs listing every feature: `std` (default), `serde` and `hashbrown`. The `hashbrown` entry documents the fixed keys and how to randomize them: depend on `ahash = { version = "0.8", features = ["runtime-rng"] }` (or `compile-time-rng`) directly.

  Docs only; no behaviour change.

  ### Changelog notice

  - `bdk_core`: Document the crate's features, including that `hashbrown` hashes with fixed keys unless `ahash`'s `runtime-rng`/`compile-time-rng` is enabled. #2354

  ### Checklists

  #### All Submissions:

  * [x] I followed the contribution guidelines

  Fixes #2342

ACKs for top commit:
  evanlinjin:
    ACK 360898e08226482b0ed4951f28b63f53053b89da

Tree-SHA512: f3032bb917820ee57970e48cc367d3baca0afaecda3404302d607bd1b684814d025b5cb2a611bba2260d420ee3e100994e2e77fc6fc29aa4bb04907ae47801bf

4 hours agoMerge bitcoindevkit/bdk#2401: fix(bitcoind_rpc): allow filter_iter example to compile...
merge-script [Mon, 19 Oct 2026 12:29:33 +0000 (23:29 +1100)]
Merge bitcoindevkit/bdk#2401: fix(bitcoind_rpc): allow filter_iter example to compile on Windows

32795f95dd3f99b4d2dcb3ffb29b894b028504e3 fix(bitcoind_rpc): allow filter_iter example to compile on Windows (Ravindra Kumar Bundela)

Pull request description:

  ### Description

  Fixes #2338.

  On Windows hosts, compiling the `filter_iter` example via `cargo build -p bdk_bitcoind_rpc --example filter_iter` failed because Cargo compiles package-level dev-dependencies for examples. This pulled in `bdk_testenv` -> `electrsd`, whose `build.rs` relies on `std::os::unix::fs::PermissionsExt` and fails on Windows.

  `filter_iter` connects to an external `bitcoind` RPC instance and only used `bdk_testenv` to import `anyhow`.

  This patch:
  - Adds `anyhow = "1"` directly to `[dev-dependencies]` of `bdk_bitcoind_rpc`.
  - Removes the indirect `bdk_testenv::anyhow` import in `filter_iter.rs`.
  - Moves `bdk_testenv` under `[target.'cfg(not(windows))'.dev-dependencies]`.
  - Conditionally gates the local integration/unit tests that use the `TestEnv` regtest harness on non-Windows targets.

  ### Verification

  Tested locally on Windows (`x86_64-pc-windows-msvc`):
  - `cargo build -p bdk_bitcoind_rpc --example filter_iter` compiles cleanly.
  - `target/debug/examples/filter_iter.exe` runs and displays the expected RPC argument requirements.
  - `cargo check --tests -p bdk_bitcoind_rpc` passes with 0 warnings.
  - `cargo test -p bdk_bitcoind_rpc` passes.
  - `cargo clippy -p bdk_bitcoind_rpc --all-targets` passes with 0 warnings.
  - `cargo fmt --package bdk_bitcoind_rpc -- --check` passes.

  ### Checklists

  - [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  - [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  evanlinjin:
    ACK 32795f95dd3f99b4d2dcb3ffb29b894b028504e3

Tree-SHA512: cbe8548b363a5a1e64955b55c18cb2f4efa264221d0ef6a2bf544923e1d564731aba5f8df7990626a7ef5640c94f39ac05f12e025ad2310bcb20334c076d1b9e

4 hours agofix(bitcoind_rpc): allow filter_iter example to compile on Windows
Ravindra Kumar Bundela [Mon, 19 Oct 2026 11:38:28 +0000 (17:08 +0530)]
fix(bitcoind_rpc): allow filter_iter example to compile on Windows

The filter_iter example previously imported anyhow indirectly from
bdk_testenv. Because Cargo includes package dev-dependencies when
building examples, having bdk_testenv as an unconditional
dev-dependency pulled in electrsd, whose build script relies on Unix
permissions (std::os::unix::fs::PermissionsExt) and fails on Windows.

This change:
- Adds anyhow directly to [dev-dependencies] for bdk_bitcoind_rpc.
- Removes the indirect bdk_testenv::anyhow import in filter_iter.rs.
- Moves bdk_testenv to [target.'cfg(not(windows))'.dev-dependencies].
- Conditionally gates the bdk_testenv-dependent tests on not(windows).

Closes #2338.

4 hours agodocs(core): document hashbrown feature's fixed-key hashing
Romansage6 [Wed, 14 Oct 2026 07:02:11 +0000 (00:02 -0700)]
docs(core): document hashbrown feature's fixed-key hashing

With the `hashbrown` feature, this crate's `HashMap`/`HashSet` are
hashbrown's (on `std` builds too), which hash with `ahash`. Unless
`ahash`'s `runtime-rng` or `compile-time-rng` feature is enabled
somewhere in the dependency graph, `ahash` uses fixed, publicly known
keys, so a malicious chain source can craft colliding txids and make
`TxGraph` insertion quadratic.

Document this, and how to randomize the keys by depending on `ahash`
directly with one of those features.

Fixes #2342

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 hours agoMerge bitcoindevkit/bdk#2234: chore(deps): bump actions/checkout from 6 to 7
merge-script [Mon, 19 Oct 2026 12:01:21 +0000 (23:01 +1100)]
Merge bitcoindevkit/bdk#2234: chore(deps): bump actions/checkout from 6 to 7

a22459fd62516fdfaec674792942fc0ed5a35e5e chore(deps): bump actions/checkout from 6 to 7 (dependabot[bot])

Pull request description:

  Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
  <details>
  <summary>Release notes</summary>
  <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
  <blockquote>
  <h2>v7.0.0</h2>
  <h2>What's Changed</h2>
  <ul>
  <li>block checking out fork pr for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
  <li>Bump actions/publish-immutable-action from 0.0.3 to 0.0.4 in the minor-actions-dependencies group across 1 directory by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2458">actions/checkout#2458</a></li>
  <li>Bump flatted from 3.3.1 to 3.4.2 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2460">actions/checkout#2460</a></li>
  <li>Bump js-yaml from 4.1.0 to 4.2.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2461">actions/checkout#2461</a></li>
  <li>Bump <code>@actions/core</code> and <code>@actions/tool-cache</code> and Remove uuid by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2459">actions/checkout#2459</a></li>
  <li>upgrade module to esm and update dependencies by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2463">actions/checkout#2463</a></li>
  <li>Bump the minor-npm-dependencies group across 1 directory with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/actions/checkout/pull/2462">actions/checkout#2462</a></li>
  <li>getting ready for checkout v7 release by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2464">actions/checkout#2464</a></li>
  <li>update error wording by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2467">actions/checkout#2467</a></li>
  </ul>
  <h2>New Contributors</h2>
  <ul>
  <li><a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.3...v7.0.0">https://github.com/actions/checkout/compare/v6.0.3...v7.0.0</a></p>
  <h2>v6.1.0</h2>
  <h2>What's Changed</h2>
  <ul>
  <li><strong>[BREAKING]</strong> backport <code>allow-unsafe-pr-checkout</code> to v6 by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2500">actions/checkout#2500</a></li>
  <li>backport fixes to releases-v6 by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2527">actions/checkout#2527</a></li>
  </ul>
  <p><a href="https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/">https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/</a> for more details about this breaking change</p>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.3...v6.1.0">https://github.com/actions/checkout/compare/v6.0.3...v6.1.0</a></p>
  <h2>v6.0.3</h2>
  <h2>What's Changed</h2>
  <ul>
  <li>Update changelog by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2357">actions/checkout#2357</a></li>
  <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
  <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li>
  <li>Update changelog for v6.0.3 by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2446">actions/checkout#2446</a></li>
  </ul>
  <h2>New Contributors</h2>
  <ul>
  <li><a href="https://github.com/yaananth"><code>@yaananth</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6...v6.0.3">https://github.com/actions/checkout/compare/v6...v6.0.3</a></p>
  <h2>v6.0.2</h2>
  <h2>What's Changed</h2>
  <ul>
  <li>Add orchestration_id to git user-agent when ACTIONS_ORCHESTRATION_ID is set by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2355">actions/checkout#2355</a></li>
  <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v6.0.1...v6.0.2">https://github.com/actions/checkout/compare/v6.0.1...v6.0.2</a></p>
  <h2>v6.0.1</h2>
  <h2>What's Changed</h2>
  <ul>
  <li>Update all references from v5 and v4 to v6 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2314">actions/checkout#2314</a></li>
  <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li>
  <li>Clarify v6 README by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2328">actions/checkout#2328</a></li>
  </ul>
  <!-- raw HTML omitted -->
  </blockquote>
  <p>... (truncated)</p>
  </details>
  <details>
  <summary>Changelog</summary>
  <p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
  <blockquote>
  <h1>Changelog</h1>
  <h2>v7.0.1</h2>
  <ul>
  <li>Skip running unsafe pr check if input is default by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li>
  <li>Trim only ascii whitespace for branch by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li>
  <li>Escape values passed to --unset by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li>
  <li>Various dependency updates</li>
  </ul>
  <h2>v7.0.0</h2>
  <ul>
  <li>Block checking out fork PR for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li>
  <li>Various dependency updates</li>
  </ul>
  <h2>v6.0.3</h2>
  <ul>
  <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li>
  <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li>
  </ul>
  <h2>v6.0.2</h2>
  <ul>
  <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li>
  </ul>
  <h2>v6.0.1</h2>
  <ul>
  <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li>
  </ul>
  <h2>v6.0.0</h2>
  <ul>
  <li>Persist creds to a separate file by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
  <li>Update README to include Node.js 24 support details and requirements by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
  </ul>
  <h2>v5.0.1</h2>
  <ul>
  <li>Port v6 cleanup to v5 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
  </ul>
  <h2>v5.0.0</h2>
  <ul>
  <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
  </ul>
  <h2>v4.3.1</h2>
  <ul>
  <li>Port v6 cleanup to v4 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
  </ul>
  <h2>v4.3.0</h2>
  <ul>
  <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
  <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
  <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
  <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
  <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
  <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
  <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
  </ul>
  <h2>v4.2.2</h2>
  <ul>
  <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
  <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
  </ul>
  <h2>v4.2.1</h2>
  <ul>
  <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
  </ul>
  <!-- raw HTML omitted -->
  </blockquote>
  <p>... (truncated)</p>
  </details>
  <details>
  <summary>Commits</summary>
  <ul>
  <li><a href="https://github.com/actions/checkout/commit/3d3c42e5aac5ba805825da76410c181273ba90b1"><code>3d3c42e</code></a> prep v7.0.1 release (<a href="https://redirect.github.com/actions/checkout/issues/2531">#2531</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/28802689a136bfcdb721715abd713740beecbe07"><code>2880268</code></a> escape values passed to --unset (<a href="https://redirect.github.com/actions/checkout/issues/2530">#2530</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/12cd2235efa0937479335606d7c3ac9f6c0973b1"><code>12cd223</code></a> trim only ascii whitespace for branch (<a href="https://redirect.github.com/actions/checkout/issues/2521">#2521</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/62661c4e71a304b2823ed026347b8d34c3eac541"><code>62661c4</code></a> skip running unsafe pr check if input is default (<a href="https://redirect.github.com/actions/checkout/issues/2518">#2518</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/e8d4307400f9427dba7cb98e488d6ab85f1cec5f"><code>e8d4307</code></a> Bump the minor-actions-dependencies group with 2 updates (<a href="https://redirect.github.com/actions/checkout/issues/2499">#2499</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/631c942040754b6e095e929c1677c07e10ed4f87"><code>631c942</code></a> eslint 9 (<a href="https://redirect.github.com/actions/checkout/issues/2474">#2474</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/4f1f4aec02e41874fa0262ea8ff5172d7978ad1e"><code>4f1f4ae</code></a> Bump actions/upload-artifact from 4 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2476">#2476</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/ba097532fb203f7e88c9c3c0b899b49469908a92"><code>ba09753</code></a> Bump actions/checkout from 6 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2488">#2488</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/b9e0990d219a03df7633c93f6f005a8fecbcab22"><code>b9e0990</code></a> Bump docker/login-action from 3.3.0 to 4.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2479">#2479</a>)</li>
  <li><a href="https://github.com/actions/checkout/commit/e8cb398be4a550817e382abf69e4c12c76fce1f2"><code>e8cb398</code></a> Bump docker/build-push-action from 6.5.0 to 7.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2478">#2478</a>)</li>
  <li>Additional commits viewable in <a href="https://github.com/actions/checkout/compare/v6...v7">compare view</a></li>
  </ul>
  </details>
  <br />

  > **Note**
  > Automatic rebases have been disabled on this pull request as it has been open for over 30 days.

ACKs for top commit:
  evanlinjin:
    ACK a22459fd62516fdfaec674792942fc0ed5a35e5e

Tree-SHA512: 060e5433b01a89dd0b908431e3cb707fe31bfd74f219c514867cedfad86ebb254e8da668a8cb1161c860c41561ce8050fde8c08fc9bad3a85e53f97856c9e642

4 hours agoMerge bitcoindevkit/bdk#2223: chore(deps): bump codecov/codecov-action from 6.0.0...
merge-script [Mon, 19 Oct 2026 11:59:17 +0000 (22:59 +1100)]
Merge bitcoindevkit/bdk#2223: chore(deps): bump codecov/codecov-action from 6.0.0 to 7.0.0

9dbeb6fefb20e96d58bcdbe574805690468e743a chore(deps): bump codecov/codecov-action from 6.0.0 to 7.0.0 (dependabot[bot])

Pull request description:

  Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6.0.0 to 7.0.0.
  <details>
  <summary>Release notes</summary>
  <p><em>Sourced from <a href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's releases</a>.</em></p>
  <blockquote>
  <h2>v7.0.0</h2>
  <p>⚠️ Due to migration issues with keybase, we are unable to update our keys under the <code>codecovsecurity</code> account. We have deleted the account and are using <code>codecovsecops</code> with the original gpg key</p>
  <h2>What's Changed</h2>
  <ul>
  <li>ci: remove Enforce License Compliance workflow by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1950">codecov/codecov-action#1950</a></li>
  <li>chore(release): 7.0.0 by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1957">codecov/codecov-action#1957</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v6.0.1...v7.0.0">https://github.com/codecov/codecov-action/compare/v6.0.1...v7.0.0</a></p>
  <h2>v6.0.2</h2>
  <p>This is a copy of the <code>v7.0.0</code> release to make updates easier</p>
  <h2>What's Changed</h2>
  <ul>
  <li>ci: remove Enforce License Compliance workflow by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1950">codecov/codecov-action#1950</a></li>
  <li>chore(release): 7.0.0 by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1957">codecov/codecov-action#1957</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v6.0.1...v6.0.2">https://github.com/codecov/codecov-action/compare/v6.0.1...v6.0.2</a></p>
  <h2>v6.0.1</h2>
  <h2>What's Changed</h2>
  <ul>
  <li>fix: prevent template injection in run: steps (VULN-1652) by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1947">codecov/codecov-action#1947</a></li>
  <li>chore(release): 6.0.1 by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1949">codecov/codecov-action#1949</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v6.0.0...v6.0.1">https://github.com/codecov/codecov-action/compare/v6.0.0...v6.0.1</a></p>
  </blockquote>
  </details>
  <details>
  <summary>Changelog</summary>
  <p><em>Sourced from <a href="https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md">codecov/codecov-action's changelog</a>.</em></p>
  <blockquote>
  <h2>v5.5.2</h2>
  <h3>What's Changed</h3>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.5.1..v5.5.2">https://github.com/codecov/codecov-action/compare/v5.5.1..v5.5.2</a></p>
  <h2>v5.5.1</h2>
  <h3>What's Changed</h3>
  <ul>
  <li>fix: overwrite pr number on fork by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1871">codecov/codecov-action#1871</a></li>
  <li>build(deps): bump actions/checkout from 4.2.2 to 5.0.0 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1868">codecov/codecov-action#1868</a></li>
  <li>build(deps): bump github/codeql-action from 3.29.9 to 3.29.11 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1867">codecov/codecov-action#1867</a></li>
  <li>fix: update to use local app/ dir by <a href="https://github.com/thomasrockhu-codecov"><code>@thomasrockhu-codecov</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1872">codecov/codecov-action#1872</a></li>
  <li>docs: fix typo in README by <a href="https://github.com/datalater"><code>@datalater</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1866">codecov/codecov-action#1866</a></li>
  <li>Document a <code>codecov-cli</code> version reference example by <a href="https://github.com/webknjaz"><code>@webknjaz</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1774">codecov/codecov-action#1774</a></li>
  <li>build(deps): bump github/codeql-action from 3.28.18 to 3.29.9 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1861">codecov/codecov-action#1861</a></li>
  <li>build(deps): bump ossf/scorecard-action from 2.4.1 to 2.4.2 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1833">codecov/codecov-action#1833</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.5.0..v5.5.1">https://github.com/codecov/codecov-action/compare/v5.5.0..v5.5.1</a></p>
  <h2>v5.5.0</h2>
  <h3>What's Changed</h3>
  <ul>
  <li>feat: upgrade wrapper to 0.2.4 by <a href="https://github.com/jviall"><code>@jviall</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1864">codecov/codecov-action#1864</a></li>
  <li>Pin actions/github-script by Git SHA by <a href="https://github.com/martincostello"><code>@martincostello</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1859">codecov/codecov-action#1859</a></li>
  <li>fix: check reqs exist by <a href="https://github.com/joseph-sentry"><code>@joseph-sentry</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1835">codecov/codecov-action#1835</a></li>
  <li>fix: Typo in README by <a href="https://github.com/spalmurray"><code>@spalmurray</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1838">codecov/codecov-action#1838</a></li>
  <li>docs: Refine OIDC docs by <a href="https://github.com/spalmurray"><code>@spalmurray</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1837">codecov/codecov-action#1837</a></li>
  <li>build(deps): bump github/codeql-action from 3.28.17 to 3.28.18 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1829">codecov/codecov-action#1829</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.4.3..v5.5.0">https://github.com/codecov/codecov-action/compare/v5.4.3..v5.5.0</a></p>
  <h2>v5.4.3</h2>
  <h3>What's Changed</h3>
  <ul>
  <li>build(deps): bump github/codeql-action from 3.28.13 to 3.28.17 by <code>@app/dependabot</code> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1822">codecov/codecov-action#1822</a></li>
  <li>fix: OIDC on forks by <a href="https://github.com/joseph-sentry"><code>@joseph-sentry</code></a> in <a href="https://redirect.github.com/codecov/codecov-action/pull/1823">codecov/codecov-action#1823</a></li>
  </ul>
  <p><strong>Full Changelog</strong>: <a href="https://github.com/codecov/codecov-action/compare/v5.4.2..v5.4.3">https://github.com/codecov/codecov-action/compare/v5.4.2..v5.4.3</a></p>
  <h2>v5.4.2</h2>
  <!-- raw HTML omitted -->
  </blockquote>
  <p>... (truncated)</p>
  </details>
  <details>
  <summary>Commits</summary>
  <ul>
  <li><a href="https://github.com/codecov/codecov-action/commit/fb8b3582c8e4def4969c97caa2f19720cb33a72f"><code>fb8b358</code></a> chore(release): 7.0.0 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1957">#1957</a>)</li>
  <li><a href="https://github.com/codecov/codecov-action/commit/ca0a928a4cb3911011e868128a5cd90437c12db1"><code>ca0a928</code></a> ci: remove Enforce License Compliance workflow (<a href="https://redirect.github.com/codecov/codecov-action/issues/1950">#1950</a>)</li>
  <li><a href="https://github.com/codecov/codecov-action/commit/e79a6962e0d4c0c17b229090214935d2e33f8354"><code>e79a696</code></a> chore(release): 6.0.1 (<a href="https://redirect.github.com/codecov/codecov-action/issues/1949">#1949</a>)</li>
  <li><a href="https://github.com/codecov/codecov-action/commit/51e64229ac331acb0d7f7b17c67423995f991c79"><code>51e6422</code></a> fix: prevent template injection in run: steps (VULN-1652) (<a href="https://redirect.github.com/codecov/codecov-action/issues/1947">#1947</a>)</li>
  <li>See full diff in <a href="https://github.com/codecov/codecov-action/compare/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...fb8b3582c8e4def4969c97caa2f19720cb33a72f">compare view</a></li>
  </ul>
  </details>
  <br />

ACKs for top commit:
  evanlinjin:
    ACK 9dbeb6fefb20e96d58bcdbe574805690468e743a

Tree-SHA512: c03e7c4377f8b945b9e3d499ca89dc2153c759fbceb6f1c4cc0629aab92bd62cb793709264973fb9e0e42046ad99152fb781612c862094972bc3eeb024f9792c

4 hours agoMerge bitcoindevkit/bdk#2350: docs(esplora): explain prevout trust assumptions
merge-script [Mon, 19 Oct 2026 11:55:18 +0000 (22:55 +1100)]
Merge bitcoindevkit/bdk#2350: docs(esplora): explain prevout trust assumptions

e634a5ab935a3afe9a0a683f79d54989508a16ef docs(esplora): explain prevout trust assumptions (hassan)

Pull request description:

  ### Description

  Refs #2343

  Esplora `sync` and `full_scan` insert server-reported previous outputs into `TxUpdate::txouts` without fetching the parent transactions to verify them. If a parent isn't in the `TxGraph`, fee and sent/received figures rely on these unverified values.

  This adds a "Trust assumptions" section to the `bdk_esplora` README saying so, and links `sync`/`full_scan` (blocking and async) to it. Docs only; no behaviour change.

  ### Changelog notice

  #### Added

  - `bdk_esplora`: document that the previous outputs `sync` and `full_scan` add to `TxUpdate::txouts` are server-reported and unverified (new "Trust assumptions" section in the crate docs). #2350

  ### Checklists

  #### All Submissions:

  * [x] I followed the contribution guidelines

ACKs for top commit:
  evanlinjin:
    ACK e634a5ab935a3afe9a0a683f79d54989508a16ef

Tree-SHA512: d96822f29a0ef012fa1acc272ca91c1fa6979c0182809be45edd45b5c85213e11f3732275411b5d62c6bb9f16f252b1ad535df2a93247262116ac55a39a01c80

4 hours agoMerge bitcoindevkit/bdk#2236: feat(core): add non-consuming SyncRequest iterators
merge-script [Mon, 19 Oct 2026 11:53:58 +0000 (22:53 +1100)]
Merge bitcoindevkit/bdk#2236: feat(core): add non-consuming SyncRequest iterators

b3a3087823a51c2dc2e87ae6fc29716a7e5c5028 feat(core): split SyncRequest consuming and read-only iterators (Alok)

Pull request description:

  ### Description

  `SyncRequest`'s `iter_*` methods drain the request despite their names, and there is no way to inspect pending items without consuming them. This PR, as proposed in #2220:

  - Adds read-only `spks()`, `txids()` and `outpoints()`.
  - Adds consuming `drain_spks_with_expected_txids()`, `drain_txids()` and `drain_outpoints()`.
  - Deprecates `iter_spks_with_expected_txids()`, `iter_txids()` and `iter_outpoints()` as wrappers around the `drain_*` methods, so existing callers keep working.
  - Moves the electrum and esplora call sites to `drain_*`.

  No new tests: the behaviour is evident from the signatures.

  Fixes #2220

  ### Changelog notice

  ### Added
  - `SyncRequest::spks`, `SyncRequest::txids` and `SyncRequest::outpoints` for read-only access to pending sync items.
  - `SyncRequest::drain_spks_with_expected_txids`, `SyncRequest::drain_txids` and `SyncRequest::drain_outpoints` for consuming iteration.

  ### Deprecated
  - `SyncRequest::iter_spks_with_expected_txids`, `SyncRequest::iter_txids` and `SyncRequest::iter_outpoints`; use the `drain_*` methods, or the read-only accessors.

  ### Checklists
  **All Submissions**
  - [x] I followed the contribution guidelines.

  **New Features**
  - [ ] I've added tests for the new feature.
  - [x] I've added docs for the new feature.

ACKs for top commit:
  evanlinjin:
    ACK b3a3087823a51c2dc2e87ae6fc29716a7e5c5028

Tree-SHA512: afb94312fe4adf42beb344a3885c1d799a5d3ba866f1ab11c653da88b597d86af069b322781dc09c453fb59253c2c63fb4da8f214038815862ea24fb3f94e774

4 hours agofeat(core): split SyncRequest consuming and read-only iterators
Alok [Mon, 19 Oct 2026 11:39:53 +0000 (22:39 +1100)]
feat(core): split SyncRequest consuming and read-only iterators

Rename the consuming iterators to `drain_*` and deprecate the `iter_*`
methods as wrappers. Add read-only `spks`, `txids` and `outpoints`.

Fixes #2220

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 hours agodocs(esplora): explain prevout trust assumptions
hassan [Thu, 8 Oct 2026 07:31:37 +0000 (13:01 +0530)]
docs(esplora): explain prevout trust assumptions

Refs #2343

Signed-off-by: Hassan Rehman <144530465+h55n@users.noreply.github.com>
6 hours agochore(deps): bump actions/checkout from 6 to 7 github/dependabot/github_actions/actions/checkout-7
dependabot[bot] [Mon, 19 Oct 2026 10:17:48 +0000 (10:17 +0000)]
chore(deps): bump actions/checkout from 6 to 7

Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
8 hours agoMerge bitcoindevkit/bdk#2321: feat(chain)!: classify timelocked outputs as `Immature`
merge-script [Mon, 19 Oct 2026 08:06:19 +0000 (19:06 +1100)]
Merge bitcoindevkit/bdk#2321: feat(chain)!: classify timelocked outputs as `Immature`

ba702bd73a3a318347cc7be7afea41a3a4ea50ed test(chain): cover timelocked outputs classified as `Immature` (Dmenec)
7d7e746e03960d3e21bf5afded40eacaeb77d569 feat(chain)!: classify timelocked outputs as `Immature` (Dmenec)

Pull request description:

  ### Description

  Classifies outputs whose timelock hasn't matured yet as `Eligibility::Immature`, so they aren't counted as spendable balance. `classify_outpoints` and `balance` take a new `is_locked` predicate, evaluated on every output.

  ### Notes to the reviewers

  The timelock lives in the output's descriptor, so `CanonicalView` can't evaluate it from chain data alone. The caller passes `is_locked` instead, the same way it passes `does_taint`. The wallet side will follow in a separate `bdk_wallet` PR.

  Timelocked (CSV, CLTV) outputs share `Immature` with immature coinbase outputs, since neither is spendable by consensus. Callers that need to tell them apart can check `is_on_coinbase`. Other locked coins that aren't enforced by consensus, like frozen or reserved coins, shouldn't use `is_locked` and are expected to be handled in the wallet.

  ### Changelog notice

  Breaking: `CanonicalView::classify_outpoints` and `balance` take a new `is_locked` predicate which classifies outputs with unmet timelocks as `Immature`.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [x] 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

ACKs for top commit:
  evanlinjin:
    ACK ba702bd73a3a318347cc7be7afea41a3a4ea50ed

Tree-SHA512: da563990f79a87e2b45286ca3913229d27f3d72fd88b1391f3e7eeba833fe9879c6e0bd690f9160cf5132d6aefe0e73f99afdc326c9d0ea599c857b316911850

10 hours agotest(chain): cover timelocked outputs classified as `Immature`
Dmenec [Thu, 15 Oct 2026 14:22:56 +0000 (16:22 +0200)]
test(chain): cover timelocked outputs classified as `Immature`

10 hours agofeat(chain)!: classify timelocked outputs as `Immature`
Dmenec [Thu, 15 Oct 2026 14:22:53 +0000 (16:22 +0200)]
feat(chain)!: classify timelocked outputs as `Immature`

Add an `is_locked` predicate on `classify_outpoints`/`balance` so the caller marks outputs whose timelock has not matured yet. They are classified as `Immature`.

12 hours agoMerge bitcoindevkit/bdk#2031: Add `verify-standalone` and `verify-standalone-all...
merge-script [Mon, 19 Oct 2026 04:01:53 +0000 (15:01 +1100)]
Merge bitcoindevkit/bdk#2031: Add `verify-standalone` and `verify-standalone-all` recipes

eeb69dced18785408b735c64522430a2104b885e chore(just): Add `verify-standalone-all` recipe (志宇)
37411ea4713a8706c3af9beda24494f99649fed7 chore(just): Add `verify-standalone` recipe (志宇)

Pull request description:

  Fixes #2030

  ### Description

  Adds two `just` recipes that check whether workspace crates build as they would once published:

  * `verify-standalone <crate> [args]` (alias `vs`): packages the crate and builds the packaged copy outside the workspace, so sibling workspace crates resolve from crates.io instead of local paths. A failure usually means the crate uses something from a sibling crate that isn't released yet. Extra args are passed to `cargo build`.
  * `verify-standalone-all [args]` (alias `vsa`): runs `verify-standalone --all-features` on every publishable workspace crate, stopping at the first failure. `--all-features` catches uses of unreleased APIs behind non-default features.

  The registry cache and build artifacts are kept under the target directory, so repeat runs are fast.

  ### Notes to the reviewers

  Neither recipe is wired into CI. On current `master`, `bdk_chain` and `bdk_bitcoind_rpc` fail because they use `bdk_core` APIs that aren't released yet, which is what the check is for.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  evanlinjin:
    self-ACK eeb69dced18785408b735c64522430a2104b885e

Tree-SHA512: e8891f7cf2e0d84402ee1c9c7626c5025c55a4464e7b2a68e513ad92201ee6d7d5b8fa1387d9e7c315940926b6b5b032e20c5e3bfea8e784ae6496a79cc0e4cb

12 hours agochore(just): Add `verify-standalone-all` recipe
志宇 [Thu, 17 Sep 2026 04:24:09 +0000 (04:24 +0000)]
chore(just): Add `verify-standalone-all` recipe

Runs `verify-standalone` with `--all-features` on every publishable
workspace crate, so feature-gated uses of unreleased workspace APIs are
also caught. Stops at the first failure.

Added alias `vsa` for quick access.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 hours agochore(just): Add `verify-standalone` recipe
志宇 [Thu, 17 Sep 2026 04:24:09 +0000 (04:24 +0000)]
chore(just): Add `verify-standalone` recipe

Packages a crate and builds the packaged copy outside the workspace, so
sibling workspace crates resolve from crates.io instead of local paths.

This catches crates that accidentally depend on unreleased changes in
other crates of the same workspace. Extra args are passed to `cargo build`.

Added alias `vs` for quick access.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 hours agoMerge bitcoindevkit/bdk#2360: fix(esplora,electrum): treat `parallel_requests`/`batch...
merge-script [Mon, 19 Oct 2026 02:52:27 +0000 (13:52 +1100)]
Merge bitcoindevkit/bdk#2360: fix(esplora,electrum): treat `parallel_requests`/`batch_size` of 0 as 1

0d8d26c3b1977bd55544eeb064d108046dcf9f67 fix(electrum): treat batch_size of 0 as 1 (yahia008)
e990ea78e9fd4dd6fbcb1577f43873d9234ab87a fix(esplora): treat parallel_requests of 0 as 1 (yahia008)

Pull request description:

  ## Description

  Fixes #2291.

  Passing `parallel_requests = 0` to `bdk_esplora`, or `batch_size = 0` to `bdk_electrum`, caused `full_scan` and `sync` to skip all requests and return an empty `Ok`. This made the wallet appear synced despite missing transactions, without indicating that the scan or sync had failed.

  ### Notes to the reviewers

  This change ensures that zero-valued request limits do not cause `full_scan` or `sync` to silently skip the requested work and return an apparently successful result.

  The regression tests use an unreachable server endpoint to verify that the requested fetches are not skipped when the relevant parameter is zero. The Esplora tests cover `parallel_requests = 0`, while the Electrum tests cover `batch_size = 0` and `parallel_requests = 0`, including the separate fetch loops for script pubkeys, transaction IDs, and outpoints.

  ### Changelog notice

  ```text
  ### Fixed

  - Prevent `full_scan` and `sync` from silently skipping requests when `parallel_requests` or `batch_size` is zero in `bdk_esplora` and `bdk_electrum.
  ```

  ### Checklists

  #### All Submissions:

  - [x] I followed the [[contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md).

ACKs for top commit:
  evanlinjin:
    ACK 0d8d26c3b1977bd55544eeb064d108046dcf9f67

Tree-SHA512: c2753075284f43a0ac32787cda8843e804a08d79632898aff6f848595b7c3716acd92994b4cf8e5f567974a30a726afeebdd3ad352945a8ae8436d8c156fd509

13 hours agofix(electrum): treat batch_size of 0 as 1
yahia008 [Fri, 9 Oct 2026 13:54:26 +0000 (14:54 +0100)]
fix(electrum): treat batch_size of 0 as 1

A `batch_size` of 0 made `full_scan` and `sync` skip every request and
return an empty `Ok`, because `populate_with_spks` builds each batch with
`(0..batch_size)` and returns when the batch is empty.

Clamp it to 1 in `populate_with_spks`, document this on `full_scan` and
`sync`, and add a test covering both.

Fixes #2291

13 hours agofix(esplora): treat parallel_requests of 0 as 1
yahia008 [Fri, 9 Oct 2026 13:54:26 +0000 (14:54 +0100)]
fix(esplora): treat parallel_requests of 0 as 1

A `parallel_requests` of 0 made `full_scan` and `sync` skip every request
and return an empty `Ok`, because each fetch loop takes its next batch with
`.take(parallel_requests)` and stops when the batch is empty.

Clamp it to 1 in each fetch loop, for both the blocking and async clients,
as `stop_gap` already is. Document this on `full_scan` and `sync`, and add
tests covering the spk, txid and outpoint paths.

Fixes #2291

25 hours agoMerge bitcoindevkit/bdk#2394: test(chain): reuse indexer helper in apply_changeset...
merge-script [Sun, 18 Oct 2026 14:54:37 +0000 (01:54 +1100)]
Merge bitcoindevkit/bdk#2394: test(chain): reuse indexer helper in apply_changeset clamp test

b7931c88fe70a1a4633873b5cf6fab805ff4a62a test(chain): reuse indexer helper in apply_changeset clamp test (busayo-OD)

Pull request description:

  ### Description

  Reuse indexer_with_last_revealed_at in apply_changeset_clamps_out_of_range_index_and_derives_correct_spk to simplify the test setup. The helper is also moved above its first use for readability.

  Follow-up to #2265.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

ACKs for top commit:
  evanlinjin:
    ACK b7931c88fe70a1a4633873b5cf6fab805ff4a62a

Tree-SHA512: 9960ae6ee4813617e107cf050786b284dc75784a5d2f54008a4fa755972e44805518d588eb09b1defdf88fb050072ab56925b6d11500e9fe7dc43549a02f7931

25 hours agoMerge bitcoindevkit/bdk#2382: docs(file_store): correct Store creation and recovery...
merge-script [Sun, 18 Oct 2026 14:51:10 +0000 (01:51 +1100)]
Merge bitcoindevkit/bdk#2382: docs(file_store): correct Store creation and recovery guidance

aa60b2dad847e8ff2f0cccf30a3e59c175194c4b docs(file_store): correct Store creation and recovery guidance (luangucun)

Pull request description:

  <!-- You can erase any parts of this template not applicable to your Pull Request. -->

  ### Description

  Correct two misleading `Store` API comments:

  - `Store::create` opens the file for both reading and writing.
  - Creating a recovered store at an existing path fails; replacing the original file requires a separate operation, as the recovery example demonstrates.

  ### Notes to the reviewers

  <!-- In this section you can include notes directed to the reviewers, like explaining why some parts
  of the PR were done in a specific way -->

  ### Changelog notice

  <!-- Notice the release manager should include in the release tag message changelog -->
  <!-- See https://keepachangelog.com/en/1.0.0/ for examples -->

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### 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

ACKs for top commit:
  evanlinjin:
    ACK aa60b2dad847e8ff2f0cccf30a3e59c175194c4b

Tree-SHA512: e1ec2e8437288c6ec9927b6cde64067bc6ccf25d5c6f8f6824c4e8e874485a95fdefcbdf22424e07df41ee3b20b8d36035849a02e4d3cc599d110a6b197ae77a

25 hours agodocs(file_store): correct Store creation and recovery guidance
luangucun [Thu, 15 Oct 2026 10:29:24 +0000 (18:29 +0800)]
docs(file_store): correct Store creation and recovery guidance

Signed-off-by: luangucun <luangucun@outlook.com>
26 hours agoMerge bitcoindevkit/bdk#2349: fix(electrum): return error when tip height overflows u32
merge-script [Sun, 18 Oct 2026 14:31:28 +0000 (01:31 +1100)]
Merge bitcoindevkit/bdk#2349: fix(electrum): return error when tip height overflows u32

92ca220ce1c3346af9ec8ab7e36dc4280dc2c385 fix(electrum): return error when tip height overflows u32 (bhuvan-somisetty)

Pull request description:

  ### Description

  `fetch_tip_and_latest_blocks` cast the server's `usize` tip height with `height as u32`, so on 64-bit a height above `u32::MAX` was silently truncated. It now uses `u32::try_from` and returns `Error::Message` on overflow.

  Fixes #2294.

  ### Changelog notice

  - Fixed: an overflowing Electrum tip height now returns an error instead of being truncated (#2294).

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [ ] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 92ca220ce1c3346af9ec8ab7e36dc4280dc2c385

Tree-SHA512: 88c9a187c99ba063a07b0f1cbcfe46c05c3b51f8ddb4e0ef6e2e3e3d9da25ec12576d8739c0a6445a36802b4a20554e816e56693680ee1785025ebae45caca43

26 hours agofix(electrum): return error when tip height overflows u32
bhuvan-somisetty [Thu, 8 Oct 2026 02:48:08 +0000 (08:18 +0530)]
fix(electrum): return error when tip height overflows u32

Validate that the tip height returned from `block_headers_subscribe`
fits in `u32` via `u32::try_from` instead of silently truncating with
`as u32`.

Signed-off-by: bhuvan-somisetty <somisettybhuvan5@gmail.com>
26 hours agotest(chain): reuse indexer helper in apply_changeset clamp test
busayo-OD [Sun, 18 Oct 2026 08:05:56 +0000 (09:05 +0100)]
test(chain): reuse indexer helper in apply_changeset clamp test

Reuse `indexer_with_last_revealed_at` to simplify the test setup for
`apply_changeset_clamps_out_of_range_index_and_derives_correct_spk`.

Move the helper above its first use.

26 hours agoMerge bitcoindevkit/bdk#2334: fix(electrum): error on batch response length mismatch
merge-script [Sun, 18 Oct 2026 13:37:17 +0000 (00:37 +1100)]
Merge bitcoindevkit/bdk#2334: fix(electrum): error on batch response length mismatch

3f6c74cd25a055d86f8dbffdcad3703e55e18595 fix(electrum): check batch response lengths before pairing (Brijesh-Thakkar)

Pull request description:

  ### Description

  `BdkElectrumClient` pairs each batch request with its response using `zip`, without checking that the lengths match. With a short response, trailing scripts, txids and anchors are silently dropped while `sync` returns `Ok`. A short `batch_block_header` response makes `height_to_hash[&h]` panic.

  This adds a `check_batch_len` helper, called after each of the five batch calls. It returns `Error::Message` on a length mismatch. No public API changes.

  Fixes #2305

  ### Notes to the reviewers

  The stock `electrum_client::Client` can't return a short batch (its `batch_call` waits for every request id), so this only hardens custom `ElectrumApi` implementations. No tests: a mock `ElectrumApi` takes ~180 lines of stubs to guard five one-line checks.

  This issue was found by AI, and I used Claude Code to assist with this PR.

  ### Changelog notice

  Fixed: `bdk_electrum` returns an error instead of silently truncating results or panicking when an Electrum batch response length doesn't match the request.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [ ] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 3f6c74cd25a055d86f8dbffdcad3703e55e18595

Tree-SHA512: 19dc656dd5635b4209e974d2025fc42c39b0ac09c40beddcf7d51872d953ae209dccc9fb912f2809ac911670f1eaa53f809db7c9032235f3a654efd179e4e4f2

27 hours agofix(electrum): check batch response lengths before pairing
Brijesh-Thakkar [Wed, 30 Sep 2026 19:21:08 +0000 (00:51 +0530)]
fix(electrum): check batch response lengths before pairing

`BdkElectrumClient` paired every batch request with its response via
`zip` without checking that the response had one entry per request. A
short response (possible with any non-stock `ElectrumApi` implementation)
silently dropped trailing scripts, txids and anchors while `sync` still
returned `Ok`, and a short `batch_block_header` response left
`height_to_hash` incomplete so `height_to_hash[&h]` panicked.

Add `check_batch_len` and call it after each of the five batch calls
(`batch_script_get_history` x3, `batch_block_header`,
`batch_transaction_get_merkle`), returning `Error::Message` on any
length mismatch, in line with the other server-misbehaviour checks in
this crate.

Fixes #2305

29 hours agoMerge bitcoindevkit/bdk#2361: fix(esplora): stop paging when the server does not...
merge-script [Sun, 18 Oct 2026 10:54:08 +0000 (21:54 +1100)]
Merge bitcoindevkit/bdk#2361: fix(esplora): stop paging when the server does not advance the cursor

19ba4e3d67516e4a3045b9870ccf7bfd6173579e fix(esplora): stop paging when the server does not advance the cursor (luhrhenz)

Pull request description:

  ### Description

  Fixes #2339.

  `bdk_esplora` pages each script pubkey's history by passing the previous page's last txid as the cursor, and stops only on a page of fewer than 25 txs. A server that ignores the cursor returns the same full page forever, so `sync`/`full_scan` never returns and memory grows without bound.

  The loop now records every cursor it pages from and returns `InvalidResponse` when a full page ends on one it has already seen. This catches both a repeated page and a cycle (`A -> B -> A`). Applied to both `async_ext.rs` and `blocking_ext.rs`.

  ### Notes to the reviewers

  - It returns an error rather than `break`ing, so that `sync` never reports a truncated history as success.
  - There is no page cap, so long histories aren't truncated. A correct server never repeats a cursor, so the check can't trigger on one.
  - The regression tests use a small `TcpListener` stub in `tests/common` that always returns the same page, since `TestEnv` can't fake a misbehaving server. `tokio`'s `time` dev-dep feature is for the async test's timeout.

  ### Changelog notice

  - Fixed `bdk_esplora`'s pagination loop hanging when a chain source returns a page that does not advance the `last_seen` cursor.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 19ba4e3d67516e4a3045b9870ccf7bfd6173579e

Tree-SHA512: c67af0009ad6e84cf13d681aedcb4bf241bc22199d7cf32c76d0f0e2559bdc01253856523262f7016f31d1ae0b834bd1a4fc4a513232fe22b827d507da4b538d

29 hours agoMerge bitcoindevkit/bdk#2325: fix(electrum): propagate transport errors when syncing...
merge-script [Sun, 18 Oct 2026 10:39:52 +0000 (21:39 +1100)]
Merge bitcoindevkit/bdk#2325: fix(electrum): propagate transport errors when syncing outpoints

edbf4a38847f0fc96f7aad72fbda0221d9c7e063 test(electrum): cover transport errors when syncing outpoints (chaitika)
84b0acf323e2f95919f39a3c3102925ce64174dd fix(electrum): propagate transport errors when syncing outpoints (chaitika)

Pull request description:

  ### Description

  Fixes #2293

  `populate_with_outpoints` discarded every `fetch_tx` error, so a connection/TLS/timeout failure made `sync` return `Ok` without data for the outpoint.

  It now skips only on `Error::Protocol` and propagates other errors, matching `populate_with_txids`.

  ### Changelog notice

   - `bdk_electrum`: `sync` now returns an error instead of silently skipping an outpoint when fetching its transaction fails for a non-protocol reason

  ### Checklists
  #### All Submissions:
  * [X] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  #### Bugfixes:
  * [ ] This pull request breaks the existing API
  * [X] I've added tests to reproduce the issue which are now passing
  * [X] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK edbf4a38847f0fc96f7aad72fbda0221d9c7e063

Tree-SHA512: 182464b58b9ea3a1637f4b9fb9a1b0aff878b48aea806691c7938ca14459711043a4695402b739a000c5744cc6541f17cdaa68964a38536524213ded7db83a09

30 hours agoMerge bitcoindevkit/bdk#2377: fix(chain): don't panic on overlapping descriptors...
merge-script [Sun, 18 Oct 2026 10:18:54 +0000 (21:18 +1100)]
Merge bitcoindevkit/bdk#2377: fix(chain): don't panic on overlapping descriptors in release

bb7373ba163fccf578113a601e3e4fb5205bc230 fix(chain): don't panic on overlapping descriptors in release (志宇)

Pull request description:

  ### Description

  Fixes #2277. Supersedes #2327.

  Overlapping descriptors stay a caller contract violation. The docs now say so (debug builds panic), and `reveal_next_spk` returns `None` instead of panicking in release.

  ### Notes to the reviewers

  The regression test is gated on `cfg(not(debug_assertions))`, so it only runs with `--release` (not in CI).

  ### Changelog notice

  * Fixed: `KeychainTxOutIndex::reveal_next_spk` no longer panics in release builds on overlapping descriptors.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  oleonardolima:
    utACK bb7373ba163fccf578113a601e3e4fb5205bc230
  evanlinjin:
    self-ACK bb7373ba163fccf578113a601e3e4fb5205bc230

Tree-SHA512: 4f65e9ebbb35ec09528c6c29dda9e898939c75ce72033ca9335180d5d6160ab2877d5091379d85d08621a07e75d0da34e2e8162743e5c3c33654885a6dffef93

30 hours agofix(esplora): stop paging when the server does not advance the cursor
luhrhenz [Fri, 9 Oct 2026 14:43:14 +0000 (15:43 +0100)]
fix(esplora): stop paging when the server does not advance the cursor

The per-script-pubkey pagination loop paged forward by asking for the
transactions following the last txid of the previous page, but never
checked that the cursor moved. A server that answers every request with
the same full page of 25 transactions kept the cursor in place, so the
loop re-requested that page forever while appending to an unbounded
`Vec`, and `sync`/`full_scan` never returned.

Track every cursor paged from, and return `InvalidResponse` if a full
page ends on one we have already seen. This catches a cursor that repeats
straight away as well as one that cycles (`A -> B -> A`). Erroring rather
than breaking avoids silently handing the caller a truncated transaction
history, which is how `chain_update` already treats a server it cannot
reconcile with.

Closes #2339

30 hours agoMerge bitcoindevkit/bdk#2335: fix(chain): reject an occupied index in insert_spk
merge-script [Sun, 18 Oct 2026 09:32:32 +0000 (20:32 +1100)]
Merge bitcoindevkit/bdk#2335: fix(chain): reject an occupied index in insert_spk

ee1909efdbe53261e268819cf7d1cd9260a03df0 fix(chain): reject an occupied index in insert_spk (hassan)

Pull request description:

  ### Description

  Fixes #2279.

  SpkTxOutIndex::insert_spk currently accepts a different script at an existing index. The forward lookup is overwritten while the old reverse lookup remains, so the two maps disagree.

  This returns false without changing state when the index is already occupied, matching the existing behavior for an occupied script. The method documentation now covers both cases.

  ### Notes to the reviewers

  Added regressions for both lookup directions, unused scripts and previously scanned outputs. A rejected insertion preserves the old output and used status, and the rejected script is not tracked.

  Testing on the refreshed master base:
  - Rust 1.85: bdk_chain with all features, 99 tests/doctests pass, 1 ignored.
  - Changed-crate clippy with all targets and features passes with warnings denied.
  - Workspace formatting check passes.
  - Full workspace pre-push suite has not been rerun on this base.

  ### Changelog notice

  Reject duplicate indices in SpkTxOutIndex::insert_spk without changing existing script or output state.

ACKs for top commit:
  evanlinjin:
    ACK ee1909efdbe53261e268819cf7d1cd9260a03df0

Tree-SHA512: 21c1220dabc4c36cc8161eeb7f8b0284baac6c575b64aac5802dfec04416dbdf964f672b0aad36f3cfe53271709c9f698ba644c3f018bb149848866896e7c43f

31 hours agofix(chain): reject an occupied index in insert_spk
hassan [Fri, 2 Oct 2026 00:18:20 +0000 (05:48 +0530)]
fix(chain): reject an occupied index in insert_spk

Reject an occupied index before changing either lookup map. Preserve existing script, scanned-output and used-status mappings on a rejected insertion, matching the existing behavior for an occupied script.

Signed-off-by: hassan <144530465+h55n@users.noreply.github.com>
36 hours agoMerge bitcoindevkit/bdk#2376: docs(chain): document that `apply_block` trusts the...
merge-script [Sun, 18 Oct 2026 04:08:23 +0000 (15:08 +1100)]
Merge bitcoindevkit/bdk#2376: docs(chain): document that `apply_block` trusts the given block

2cce69780e6ca215c450cb9d8ebd1c752339c99b docs(chain): document that `apply_block` trusts the given block (志宇)

Pull request description:

  Fixes #2278. Supersedes #2373.

  Documents that `apply_block` and `apply_block_relevant` don't validate the block (chain membership or merkle root), so the caller must supply it from a trusted source. No `debug_assert!`: a bad block comes from a bad source at runtime, where the assert is compiled out.

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  oleonardolima:
    utACK 2cce69780e6ca215c450cb9d8ebd1c752339c99b

Tree-SHA512: bf155a1b7c94a5e23078b23449085f028a76852e72c0d6c2a52867337398c3f9331eb8072a52fb57d5b093d9afd685d3b901d540513f0557656910e110f312a3

36 hours agoMerge bitcoindevkit/bdk#2265: fix(chain): sanitize and bound-check derivation indices...
merge-script [Sun, 18 Oct 2026 03:34:48 +0000 (14:34 +1100)]
Merge bitcoindevkit/bdk#2265: fix(chain): sanitize and bound-check derivation indices near BIP32_MAX_INDEX

1ca8d29854d41269982a1f47b03603a98a221d99 fix(chain): fix integer overflow in lookahead_to_target (busayo-OD)
de43ac6941bd7196f1421bac3646e5dafea83290 fix(chain): fix off-by-one in replenish_inner_index stop bound (busayo-OD)
803fd968d7999bda911eb2b968cd54a585fac8b4 fix(chain): clamp derivation index before apply_changeset (busayo-OD)

Pull request description:

  Fixes [bdk_wallet/issues/60](https://github.com/bitcoindevkit/bdk_wallet/issues/60)

  ### Description

  `KeychainTxOutIndex::apply_changeset` accepted `ChangeSet::last_revealed` values with no upper bound, allowing an index greater than `BIP32_MAX_INDEX` (2^31 - 1) to be stored, violating an invariant relied on by `next_index()`.

  The changes in this PR address boundary issues around derivation indices:

  1. **`apply_changeset`**: clamps `last_revealed` to `BIP32_MAX_INDEX` before storing it, instead of storing the raw value.
  2. **`replenish_inner_index`**: fixes an off-by-one in the `stop_index` calculation that caused a panic (`"we just inserted it"`) when a keychain's derivation index was revealed exactly up to `BIP32_MAX_INDEX`. The exclusive upper bound was clamped to `BIP32_MAX_INDEX` instead of `BIP32_MAX_INDEX + 1`, silently excluding the boundary value itself.
  3. **`lookahead_to_target`**: fixes an integer overflow panic when called with `target_index == u32::MAX`, and changes out of range targets to clamp and replenish up to `BIP32_MAX_INDEX` instead of silently doing nothing.

  ### Notes to the reviewers

  This revisits the earlier implementation attempt (#1792) and implements the clamp-in-`apply_changeset` approach from that review discussion.

  The second and third fixes were found while adding the boundary tests requested in review of an earlier version of this PR.

  Also added a test documenting current saturation behavior: `reveal_next_spk`/`next_unused_spk` return the last revealed script with an empty `ChangeSet`, not `None`, once the index is saturated. This matches `reveal_next_spk`'s existing doc comment.

  Went through the other index-taking methods (`spk_at_index`, `is_used`, `mark_used`, `unmark_used`) as well. These are plain lookups with no boundary-sensitive logic, so no dedicated tests were added there.

  ### Changelog notice

  - Clamped `last_revealed` to `BIP32_MAX_INDEX` in `KeychainTxOutIndex::apply_changeset` to prevent it accepting an out-of-range derivation index.
  - Fixed an off-by-one in `replenish_inner_index` that could panic when revealing up to `BIP32_MAX_INDEX`.
  - Fixed an integer overflow in `lookahead_to_target` when called with `target_index == u32::MAX`, and changed out of range targets to clamp to `BIP32_MAX_INDEX` instead of silently doing nothing.

  ### Checklists

  **All Submissions**

  - [x] I've signed all my commits
  - [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  - [x]  I ran `just p` before committing

  **Bugfixes**

  - [ ] This pull request breaks the existing API
  - [x] I've added tests to cover the issue which are now passing
  - [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 1ca8d29854d41269982a1f47b03603a98a221d99
  notmandatory:
    ACK 1ca8d29854d41269982a1f47b03603a98a221d99

Tree-SHA512: 2e2f86b24772424fe716453f76b5628d55121a70f7fe493f2bed7ca42a662ee705e94d79488f6e71c1d442570f81d863b4f59081bda1cddfbef59435483167e1

2 days agoMerge bitcoindevkit/bdk#2381: docs(core): require `CheckPoint` data to be determined...
merge-script [Fri, 16 Oct 2026 23:50:08 +0000 (23:50 +0000)]
Merge bitcoindevkit/bdk#2381: docs(core): require `CheckPoint` data to be determined by its block hash

fbe1aa6335276b86f6d35fa409f473f8c797e02f docs(core): require `CheckPoint` data to be determined by its block hash (志宇)

Pull request description:

  ### Description

  The `ToBlockHash` docs said extra fields the block hash doesn't commit to "must be ignored", implying they're allowed. They aren't: `CheckPoint` and `LocalChain` treat data with equal hashes as interchangeable. The docs now require checkpoint data to be fully determined by its block hash, with anything else stored outside the checkpoint.

  The `BlockWithTime` test helper now commits to its time field.

  ### Changelog notice

  ```
  ### Changed
  - Documented that `CheckPoint` data must be fully determined by its block hash.
  ```

  ### Checklists

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  oleonardolima:
    utACK fbe1aa6335276b86f6d35fa409f473f8c797e02f

Tree-SHA512: b9082833ce10149ac8be4a082537e542b59dd3748c65b6bed6912486d1ecc664bbc88966775c78db3d5cd9050e48b0a56078b775505a147cd90c0d351dd6aa27

3 days agoMerge bitcoindevkit/bdk#2372: fix(chain): skip coinbase txs with a `last_seen` in...
merge-script [Fri, 16 Oct 2026 03:57:26 +0000 (03:57 +0000)]
Merge bitcoindevkit/bdk#2372: fix(chain): skip coinbase txs with a `last_seen` in canonicalization

a921c78d7aaad2c231dc5c9919e8b9b04b105954 fix(chain): skip coinbase txs with a `last_seen` in canonicalization (志宇)

Pull request description:

  ### Description

  Alternative to #2322, taking the approach outlined in https://github.com/bitcoindevkit/bdk/pull/2322#issuecomment-6136246396. Addresses part of #2269.

  - **`SeenTxs` now skips coinbase txs.** A coinbase cannot be in the mempool, so a `last_seen` value on one is bad data and must not make it canonical. This was only a `debug_assert!`, so release builds canonicalized it at `ChainPosition::Unconfirmed`. The check now mirrors the one in `LeftOverTxs`.
  - **`CanonicalTxOut::is_mature` no longer asserts on a coinbase without a confirmation height.** It returns `false` (maturity cannot be proven), which is what release builds already did.

  Unlike #2322, a coinbase that is an ancestor of a canonical tx, or is in `assume_canonical`, stays canonical even when no anchor is held for it. A canonical spend of a coinbase implies the coinbase is in the chain, and excluding it leaves the view with a canonical tx whose parent is missing.

  ### Notes to the reviewers

  This does not change what `ChainPosition` such a coinbase resolves to: it is still `Unconfirmed`. How (or whether) to represent "canonical coinbase, no anchor held" is a breaking design question and is left for a separate issue.

  The `is_mature` assert was reachable through `balance()` on master: an unanchored coinbase with an unspent output, pulled in by an unconfirmed spend of another of its outputs. The second new test scenario covers this.

  ### Changelog notice

  - Fixed: a coinbase tx with a `last_seen` value is no longer canonicalized in release builds.
  - Fixed: `CanonicalTxOut::is_mature` no longer panics in debug builds for a canonical coinbase with no known anchor; it returns `false`.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  noahjoeris:
    ACK a921c78d7aaad2c231dc5c9919e8b9b04b105954

Tree-SHA512: 495acb3f5c4c1c04db7d2162853cb926705b31a2b88cd4be6c68d558465869354d579bbb6826ef0e2bd904696ce4bed67875bddb488f0f41bef6f3bc2fc6f0bc

4 days agodocs(core): require `CheckPoint` data to be determined by its block hash
志宇 [Thu, 15 Oct 2026 10:11:16 +0000 (10:11 +0000)]
docs(core): require `CheckPoint` data to be determined by its block hash

The `ToBlockHash` docs allowed data types with extra fields that the
block hash does not commit to. `CheckPoint` and `LocalChain` treat data
with equal hashes as interchangeable, so such fields could be silently
kept or dropped. State that the data must be fully determined by its
block hash and that anything else belongs outside the checkpoint.

Make the `BlockWithTime` test helper commit to its time field so it
follows the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days agoMerge bitcoindevkit/bdk#2233: test(core): cover SyncRequest inspect progress and...
merge-script [Thu, 15 Oct 2026 09:19:37 +0000 (09:19 +0000)]
Merge bitcoindevkit/bdk#2233: test(core): cover SyncRequest inspect progress and expected txid merging

6c363335cf0462afdb8b467fd5385139cd30d1fa test(core): cover SyncRequest inspect progress and expected txid merging (haoshengzhen)

Pull request description:

  ### Description

  Adds unit tests for `SyncRequest` in `bdk_core::spk_client`:
  - the `inspect` callback sees progress *after* each spk/txid/outpoint is consumed.
  - `expected_spk_txids` merges txids across calls for the same spk, and other spks get none.

  ### Changelog notice

  None, test-only.

ACKs for top commit:
  evanlinjin:
    ACK 6c363335cf0462afdb8b467fd5385139cd30d1fa

Tree-SHA512: 532754e960ea14a665f1d59e2349bf0ecd270d1beb4ded8636d0c569bff0e60c3224ba28263b5187689c993e34180d9585cc6abe2024209ce2c20faf443124dd

4 days agotest(core): cover SyncRequest inspect progress and expected txid merging
haoshengzhen [Fri, 19 Jun 2026 02:30:23 +0000 (10:30 +0800)]
test(core): cover SyncRequest inspect progress and expected txid merging

Signed-off-by: haoshengzhen <haoshengzhen@outlook.com>
Co-Authored-By: 志宇 <hello@evanlinjin.me>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days agoMerge bitcoindevkit/bdk#2231: refactor(testenv)!: make `wait_until_electrum_sees_bloc...
merge-script [Thu, 15 Oct 2026 06:38:04 +0000 (06:38 +0000)]
Merge bitcoindevkit/bdk#2231: refactor(testenv)!: make `wait_until_electrum_sees_block` concrete

a98cf7c48664c37a717c3160883ece6ceaed1684 refactor(testenv)!: make `wait_until_electrum_sees_block` concrete (Claude)

Pull request description:

  ### Description

  A from-scratch reimplementation of bitcoindevkit/bdk#1640 on top of the current `master`.

  `TestEnv::wait_until_electrum_sees_block` previously only took a `timeout` and returned as soon as Electrum emitted *any* new-block notification. That made it ambiguous which block was actually being waited for, and it could return before Electrum had caught up to the block the test cared about.

  This PR makes the wait concrete:

  - `wait_until_electrum_sees_block` now takes `block_height: usize` and `block_hash: Option<BlockHash>`, so callers state exactly which block Electrum must be aware of. If the notified tip has already advanced past `block_height`, the header at that height is fetched explicitly so the expected `block_hash` can still be verified.
  - A new convenience wrapper `wait_until_electrum_tip_syncs_with_bitcoind` waits until Electrum's tip matches bitcoind's chain tip (both height and hash). All existing call sites are migrated to it, since in every case the intent was "wait until Electrum catches up to the freshly mined/reorged tip".

  ### Notes to the reviewers

  - **Why subscribe instead of polling:** polling Electrs for the header at `block_height` and checking its hash does not guarantee the spk histories are current. Receiving a block-tip notification does, because of how Electrs indexes. This is captured in a `NOTE` in the code.
  - **Tip advanced past target:** the original PR's reviewers asked what happens when the method is called after the chain tip has moved past the target height. This is handled explicitly: when the notified height is greater than `block_height`, the header at `block_height` is fetched directly to verify the hash, rather than relying on the notification header.
  - **`trigger()` placement:** `electrsd.trigger()` is kept inside the polling loop (matching the previous `master` behaviour) so Electrs keeps getting nudged to re-index — this is important for reorgs, where the tip changes without a height change.
  - The previous WIP artefacts from #1640 (a `// TODO: Fails here.` marker and a struck-through doc comment) are intentionally left out.

  ### Changelog notice

  - Changed: `TestEnv::wait_until_electrum_sees_block` now takes a `block_height` and an optional `block_hash` (breaking change to `bdk_testenv`).
  - Added: `TestEnv::wait_until_electrum_tip_syncs_with_bitcoind`.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [x] This pull request breaks the existing API

  https://claude.ai/code/session_016gJvi98Q65QnanBFxe2UUn

  ---
  _Generated by [Claude Code](https://claude.ai/code/session_016gJvi98Q65QnanBFxe2UUn)_

ACKs for top commit:
  evanlinjin:
    ACK a98cf7c48664c37a717c3160883ece6ceaed1684

Tree-SHA512: 563934e0e1af7261facdb2b52fad15a45b1c364e143f99dee72d185b273ad716754313b069b8369cf09bfc22dab24fa187c7ffd0986285e546358b5ecd3e60ae

4 days agorefactor(testenv)!: make `wait_until_electrum_sees_block` concrete
Claude [Mon, 15 Jun 2026 21:36:50 +0000 (21:36 +0000)]
refactor(testenv)!: make `wait_until_electrum_sees_block` concrete

Previously `TestEnv::wait_until_electrum_sees_block` only took a `timeout`
and returned as soon as Electrum emitted any new-block notification, so it
was not clear which block was actually being waited for.

Add `block_height` and an optional `block_hash` so callers state exactly
which block Electrum must be aware of. When the notified tip has already
advanced past `block_height`, the header at that height is fetched
explicitly so the expected `block_hash` can still be verified.

Introduce `TestEnv::wait_until_electrum_tip_syncs_with_bitcoind`, a
convenience wrapper that waits until Electrum's tip matches bitcoind's
chain tip (both height and hash), and migrate all existing call sites to
it.

https://claude.ai/code/session_016gJvi98Q65QnanBFxe2UUn

4 days agoMerge bitcoindevkit/bdk#2316: fix(rpc): verify fetched mempool txids before caching...
merge-script [Thu, 15 Oct 2026 03:42:01 +0000 (03:42 +0000)]
Merge bitcoindevkit/bdk#2316: fix(rpc): verify fetched mempool txids before caching and document trust assumption

0d6fb41ac4a268675193beee31050343331996f8 fix(rpc): verify fetched mempool tx txids (Vihiga Tyonum)

Pull request description:

  ### Description

  This PR adds a check in `Emitter::mempool_at`: when a mempool transaction is fetched with `get_raw_transaction`, its computed txid must match the txid that was requested before it goes into `mempool_snapshot`. This stops an RPC response with the wrong transaction body from being emitted or cached under the requested txid. It also adds module-level documentation stating that `bdk_bitcoind_rpc` assumes a connection to a trusted `bitcoind` node.

  This addresses only the missing txid-validation part of #2282, following discussions on #2283.

  ### Notes to the reviewers

  - On a mismatch the check returns the existing `Error::UnexpectedStructure`, so it is non-breaking. A single mismatch fails the whole poll; callers retry on the next call.
  - Only freshly fetched transactions are checked. Entries already in `mempool_snapshot` were checked when they were inserted.
  - A txid does not commit to witness data, so a transaction served with altered witnesses is not detected. This is documented on `mempool_at`.
  - Out of scope / possible follow-up: `next_block` does not verify that the returned block matches the requested hash, merkle root, or witness commitment.
  - The test uses a mock `RpcApi` that implements only `call`. `serde`/`serde_json` come from the `bitcoincore_rpc::jsonrpc` re-exports, so no new dependencies are added.

  ### Changelog notice

  - `Emitter::mempool`/`mempool_at`: when a mempool transaction is fetched from the node, check that its computed txid matches the requested txid. A mismatch is rejected with `Error::UnexpectedStructure`.
  - Document that `bdk_bitcoind_rpc` assumes a trusted `bitcoind` RPC connection.

  <!-- Notice the release manager should include in the release tag message changelog -->
  <!-- See https://keepachangelog.com/en/1.0.0/ for examples -->

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [x] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 0d6fb41ac4a268675193beee31050343331996f8

Tree-SHA512: 2910133931cb8e504c54e5465957e4674ee5ba0b98ee303db1409b643e34413ec17d9f2213883fe504632eb09c7114a2ebf7571b3f07d1a36daae6cb4f82909a

4 days agofix(rpc): verify fetched mempool tx txids
Vihiga Tyonum [Wed, 16 Sep 2026 06:22:13 +0000 (07:22 +0100)]
fix(rpc): verify fetched mempool tx txids

- Add `tx.compute_txid() == txid` check to validate that fetched raw
mempool transaction hashes against their requested txid before caching
in Emitter::mempool_at. Returns `Error::UnexpectedStructure` for a
mismatch.

- Add a regression test for the above check.

- Document that the crate assumes a trusted `bitcoind` connection, and
that the txid check does not cover witness data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days agoMerge bitcoindevkit/bdk#2299: refactor(chain)!: remove `Anchor::confirmation_height_u...
merge-script [Thu, 15 Oct 2026 03:09:08 +0000 (03:09 +0000)]
Merge bitcoindevkit/bdk#2299: refactor(chain)!: remove `Anchor::confirmation_height_upper_bound`

74de3a0d656448e6851771952e90b060e890b260 refactor(chain)!: remove `Anchor::confirmation_height` (志宇)
e6a63e450b8e8e56c9daeb3e2341a88b9f90a26e docs(chain): note false-pending in CanonicalView::balance (志宇)
dadeb37fd94ca7ded7adee2d2bf69047135f6d23 refactor(chain)!: rename Anchor::confirmation_height_upper_bound (志宇)
306e052be996ed8ed62ce3e6613dadffe8e04235 docs(chain): state that an anchor names the confirming block (志宇)

Pull request description:

  ### Description

  Fixes #2298.

  The `Anchor` trait said an anchor block *might* be a descendant of the confirming block. Nothing produces such anchors: Electrum validates a merkle proof, Esplora uses the confirming block from `TxStatus`, and `TxPosInBlock` carries the tx index inside that block.

  So an anchor's height is exact. This PR drops that framing from the `Anchor` docs and removes `Anchor::confirmation_height_upper_bound`. Callers use `anchor_block().height` directly. Every implementation already returned that, so there is no behaviour change.

  `ChainPosition::confirmation_height_upper_bound` and `confirmations_lower_bound` are kept. A transitively confirmed tx (`ChainPosition::Confirmed { transitively: Some(_), .. }`) carries a descendant's anchor, so for it the height really is an upper bound. Their docs, along with those of `CanonicalTxOut::is_mature` / `is_confirmed_and_spendable` and `CanonicalView::balance`, now attribute the bound to transitive confirmation. `transitively_confirmed_tx_reports_descendant_anchor_height` covers this case.

  ### Changelog notice

  **`bdk_chain`**

  - Removed `Anchor::confirmation_height_upper_bound`. An anchor names the confirming block, so use `anchor_block().height`.
  - Updated `Anchor` docs: an anchor block is no longer described as possibly a descendant of the confirming block.
  - Updated `ChainPosition::confirmation_height_upper_bound` and `confirmations_lower_bound` docs: the values are exact unless the position is confirmed transitively.
  - Updated `CanonicalView::balance` docs: a transitively confirmed output may be counted as pending by a confirmation-depth `is_settled`.

  ### Checklists

  #### All Submissions:
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:
  * [x] This pull request breaks the existing API
  * [x] I'm linking the issue being fixed by this PR

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  evanlinjin:
    self-ACK 74de3a0d656448e6851771952e90b060e890b260

Tree-SHA512: e1623c08e04da24fe2abfbfa8fd80ecef01bab1e242a0a208eb89134862e9524f7458c3f645ad1218848352dadf06578426a273d5030be5c00f2e950b488fd6a

4 days agorefactor(chain)!: remove `Anchor::confirmation_height`
志宇 [Thu, 15 Oct 2026 02:45:25 +0000 (02:45 +0000)]
refactor(chain)!: remove `Anchor::confirmation_height`

An anchor names the confirming block, so the confirmation height is
always `anchor_block().height`. An overridable method could only
diverge from that, so callers use `anchor_block().height` directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 days agofix(chain): don't panic on overlapping descriptors in release
志宇 [Thu, 15 Oct 2026 02:10:45 +0000 (02:10 +0000)]
fix(chain): don't panic on overlapping descriptors in release

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 days agodocs(chain): document that `apply_block` trusts the given block
志宇 [Wed, 14 Oct 2026 04:48:48 +0000 (04:48 +0000)]
docs(chain): document that `apply_block` trusts the given block

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days agofix(chain): skip coinbase txs with a `last_seen` in canonicalization
志宇 [Tue, 13 Oct 2026 01:45:43 +0000 (01:45 +0000)]
fix(chain): skip coinbase txs with a `last_seen` in canonicalization

A coinbase tx cannot be in the mempool, so a `last_seen` value on one is
bad data and must not make it canonical. Previously this was only a
`debug_assert!`, so release builds canonicalized it as unconfirmed.

A coinbase that is an ancestor of a canonical tx, or is assumed
canonical, stays canonical even if no anchor is held for it. As that is
a reachable state, `CanonicalTxOut::is_mature` no longer asserts on it
and returns `false` as maturity cannot be proven.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days agodocs(chain): note false-pending in CanonicalView::balance
志宇 [Mon, 12 Oct 2026 08:49:54 +0000 (08:49 +0000)]
docs(chain): note false-pending in CanonicalView::balance

An output confirmed transitively carries a descendant's anchor, so its
confirmation height may be an upper bound. An `is_settled` predicate
that requires a minimum number of confirmations may therefore make
`balance` count it as pending even though it has enough.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days agorefactor(chain)!: rename Anchor::confirmation_height_upper_bound
志宇 [Fri, 11 Sep 2026 01:51:10 +0000 (01:51 +0000)]
refactor(chain)!: rename Anchor::confirmation_height_upper_bound

An anchor names the block that confirmed the transaction, so its height
is exact, not an upper bound. Rename
`Anchor::confirmation_height_upper_bound` to
`Anchor::confirmation_height`.

This is a hard rename with no deprecated alias. The next `bdk_chain`
release is already breaking, so a downstream `Anchor` impl overriding
the old name should fail to compile rather than be silently ignored.
`ConfirmationBlockTime`'s override is removed as it was identical to the
default.

`ChainPosition::confirmation_height_upper_bound` and
`ChainPosition::confirmations_lower_bound` keep their names. A
transaction without its own anchor can be confirmed transitively during
canonicalization, in which case the position carries a descendant's
anchor and the height really is an upper bound. Their docs, and the
false-negative caveats on `CanonicalTxOut::is_mature` and
`is_confirmed_and_spendable`, now attribute the bound to transitive
confirmation instead of to the `Anchor` implementation. A test covers
the transitive case.

No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NND8BhEmYTYdLRv4dbUUro
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 days agodocs(chain): state that an anchor names the confirming block
志宇 [Fri, 11 Sep 2026 01:46:53 +0000 (01:46 +0000)]
docs(chain): state that an anchor names the confirming block

The `Anchor` trait doc claimed an anchored transaction "could also mean
transaction A is confirmed in a parent block of B". No chain source in
this repo produces such an anchor: electrum only builds one after
`validate_merkle_proof` succeeds, esplora uses `TxStatus`'s confirming
block, and `TxPosInBlock` carries the transaction's index within that
block. The framing only leaves ambiguity for a case that does not occur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NND8BhEmYTYdLRv4dbUUro

6 days agoMerge bitcoindevkit/bdk#2368: ci: pin proptest to 1.11.0 for MSRV
merge-script [Tue, 13 Oct 2026 01:32:36 +0000 (01:32 +0000)]
Merge bitcoindevkit/bdk#2368: ci: pin proptest to 1.11.0 for MSRV

1a457c8253d588d927e1be866654cbebaccf15ed ci: pin proptest to 1.11.0 for MSRV (志宇)

Pull request description:

  ### Description

  The MSRV (1.85.0) CI job is failing on `master` and on every open PR. `proptest` 1.12.0 declares `rust-version = "1.88"`, and since there is no committed lockfile, CI resolves to it:

  ```
  error: rustc 1.85.0 is not supported by the following package:
    proptest@1.12.0 requires rustc 1.88
  ```

  `proptest` is a dev-dependency of `bdk_core` and `bdk_chain` (`proptest = "1.2.0"`). This PR pins it to 1.11.0 in `ci/pin-msrv.sh`, the latest release that declares `rust-version = "1.85"`.

  Failing run on `master`: https://github.com/bitcoindevkit/bdk/actions/runs/38205608516/job/114668304248

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  notmandatory:
    ACK 1a457c8253d588d927e1be866654cbebaccf15ed
  noahjoeris:
    tACK 1a457c8253d588d927e1be866654cbebaccf15ed

Tree-SHA512: a8361dbc5604935a409a280c324e9fae495d30d6a8699a83932d48b938ae5eb3f3b1bff2d2945b145cc91aeb9062bc49230fc8c8fa652c30e513686a8c0ce616

7 days agoci: pin proptest to 1.11.0 for MSRV
志宇 [Mon, 12 Oct 2026 09:30:59 +0000 (09:30 +0000)]
ci: pin proptest to 1.11.0 for MSRV

`proptest` 1.12.0 requires rustc 1.88, which breaks the 1.85.0 MSRV
job. 1.11.0 is the latest release that supports 1.85.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 days agoMerge bitcoindevkit/bdk#2320: fix(electrum): refresh header cache with headers fetche...
merge-script [Mon, 12 Oct 2026 04:53:14 +0000 (04:53 +0000)]
Merge bitcoindevkit/bdk#2320: fix(electrum): refresh header cache with headers fetched for the chain update

6c5e9771814fe07fe13b1a401d06b7cc7b420cd7 test(electrum): cover tx re-mined at the same height after a reorg (志宇)
b4343702e7da3f3d634559b5cbdb12dbe419c94f fix(electrum): refresh header cache with headers fetched for the chain update (志宇)

Pull request description:

  ### Description

  Forward-port of #2319 to `master`.

  `block_header_cache` was only written by `batch_fetch_anchors`, so after a reorg it kept the pre-reorg header for that height. A tx re-mined at the same height then hit the anchor cache under the stale block hash, skipped proof validation, and was never re-anchored to the replacement block. This happens even for a 1-block reorg.

  `fetch_tip_and_latest_blocks` now writes the headers it fetches (the chain suffix and the agreement-walk checkpoints) into the header cache.

  ### Notes to the reviewers

  Only differences from #2319: the `fetch_tip_and_latest_blocks` signature keeps master's `CheckPoint<BlockHash>`, and the test uses master's `TestEnv` / `LocalChain` APIs. The test fails on `master` without the fix.

  Related: #2312.

  ### Changelog notice

  - Fixed `bdk_electrum` returning a stale anchor for a transaction re-mined at the same height after a reorg.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [ ] I'm linking the issue being fixed by this PR

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  noahjoeris:
    tACK 6c5e9771814fe07fe13b1a401d06b7cc7b420cd7

Tree-SHA512: 9e9aa3c654a9898dfefa27acf99de13a7563802d7d22577437cf8ac064542074e8ee893646b1f22580da9cccfd444be7bdd348bcc4b760f5991d00cca1d41eee

10 days agochore: update CODEOWNERS
Steve Myers [Thu, 8 Oct 2026 17:15:45 +0000 (12:15 -0500)]
chore: update CODEOWNERS

12 days agoMerge bitcoindevkit/bdk#2330: docs(electrum): drop stale MSRV section from README
merge-script [Wed, 7 Oct 2026 14:52:19 +0000 (17:52 +0300)]
Merge bitcoindevkit/bdk#2330: docs(electrum): drop stale MSRV section from README

04853e31e63ab75c11395ecc4d9d248d265a797b docs(electrum): drop stale MSRV section from README (artofbitcoin)

Pull request description:

  ### Description

  Remove the stale MSRV section from `crates/electrum/README.md`, which still claims Rust 1.75.0 and includes an obsolete dependency pinning command.

  The root README already documents the current MSRV and pinning instructions. Removing the duplicate section reduces maintenance and makes the Electrum README consistent with the other crate READMEs.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

ACKs for top commit:
  noahjoeris:
    ACK 04853e31e63ab75c11395ecc4d9d248d265a797b

Tree-SHA512: 45c82a13dd0a03540e6e719a1a9caff18cf64ff22ab7bb3601a5d8eb64ca489dd64f2cad0a1dec90aaad3304f3620b6599e24d48a462f677924551429baa3963

12 days agodocs(electrum): drop stale MSRV section from README
artofbitcoin [Sat, 3 Oct 2026 15:34:45 +0000 (17:34 +0200)]
docs(electrum): drop stale MSRV section from README

The root README already documents the current MSRV and dependency
pinning instructions. Remove the outdated duplicate section to reduce
maintenance and follow the other crate READMEs.

2 weeks agofix(chain,core): prevent serde from implicitly leaking std into no_std environments
Rishit Modi [Fri, 12 Jun 2026 23:11:57 +0000 (04:41 +0530)]
fix(chain,core): prevent serde from implicitly leaking std into no_std environments

2 weeks agodocs: add maintainers section to CONTRIBUTING.md
Steve Myers [Mon, 28 Sep 2026 21:30:54 +0000 (16:30 -0500)]
docs: add maintainers section to CONTRIBUTING.md

3 weeks agotest(electrum): cover transport errors when syncing outpoints
chaitika [Sat, 26 Sep 2026 15:17:41 +0000 (20:47 +0530)]
test(electrum): cover transport errors when syncing outpoints

Add a regression test: syncing an outpoint against a server that drops
the connection must return an error instead of an empty successful
update.

3 weeks agofix(electrum): propagate transport errors when syncing outpoints
chaitika [Sat, 26 Sep 2026 15:17:00 +0000 (20:47 +0530)]
fix(electrum): propagate transport errors when syncing outpoints

`populate_with_outpoints` discarded every error returned by `fetch_tx`,
so a connection, TLS or timeout failure made `sync` report success
without any data for the requested outpoint. A spend of that outpoint
was then missed and the UTXO kept being treated as unspent.

Skip the outpoint only on `Error::Protocol` and propagate all other
errors, matching `populate_with_txids`.

4 weeks agotest(electrum): cover tx re-mined at the same height after a reorg
志宇 [Fri, 18 Sep 2026 09:36:19 +0000 (09:36 +0000)]
test(electrum): cover tx re-mined at the same height after a reorg

Checks the tx is re-anchored to the replacement block both when the
reorged height is still in the synced chain suffix and when 20 blocks are
mined before the next sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4 weeks agofix(electrum): refresh header cache with headers fetched for the chain update
志宇 [Fri, 18 Sep 2026 09:36:19 +0000 (09:36 +0000)]
fix(electrum): refresh header cache with headers fetched for the chain update

`block_header_cache` was only written by `batch_fetch_anchors`, so after a
reorg it kept serving the pre-reorg header for that height. The anchor
cache is keyed by `(txid, hash)` from that header, so a tx re-mined at the
same height hit the anchor cache with the stale hash, skipped proof
validation, and was never re-anchored to the replacement block.

`fetch_tip_and_latest_blocks` now writes the headers it fetches (the
chain suffix and the agreement-walk checkpoints) into the header cache.

4 weeks agoMerge bitcoindevkit/bdk#2317: ci: pin yoke-derive to 0.8.2 for MSRV
merge-script [Fri, 18 Sep 2026 10:13:40 +0000 (10:13 +0000)]
Merge bitcoindevkit/bdk#2317: ci: pin yoke-derive to 0.8.2 for MSRV

4dc5e5a4fc1fe6d14f6b5457a48596197f45de24 ci: pin yoke-derive to 0.8.2 for MSRV (志宇)

Pull request description:

  ### Description

  The MSRV (1.85.0) CI job is failing. `yoke-derive` 0.8.3 (released 2026-09-15) calls `str::from_utf8` as a method on the `str` type. That form isn't available on Rust 1.85.0, and the crate doesn't declare a `rust-version`, so cargo doesn't avoid it:

  ```
  error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope
     --> yoke-derive-0.8.3/src/lib.rs:202:32
      |
  202 |         let underscores = str::from_utf8(&underscores).expect("_ is ASCII and thus UTF-8");
      |                                ^^^^^^^^^ function or associated item not found in `str`
  ```

  `yoke-derive` gets pulled in through `url` -> `idna` -> the `icu_*` crates -> `zerovec` -> `yoke`. This PR pins it to 0.8.2 in `ci/pin-msrv.sh`. Version 0.8.2 calls `core::str::from_utf8` instead, and `yoke` 0.8.3 accepts `yoke-derive ^0.8.2`.

  Failing run: https://github.com/bitcoindevkit/bdk/actions/runs/35182209052/job/105076597730

  ### Notes to the reviewers

  I couldn't build with 1.85.0 locally, so the MSRV CI run on this PR is the real test. I did check that `ci/pin-msrv.sh` runs cleanly from a fresh lockfile and ends up with `yoke-derive` 0.8.2. The last green `master` run (2026-09-12) was before 0.8.3 came out.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

ACKs for top commit:
  tvpeter:
    tACK  4dc5e5a4fc1fe6d14f6b5457a48596197f45de24
  noahjoeris:
    tACK 4dc5e5a4fc1fe6d14f6b5457a48596197f45de24

Tree-SHA512: 812f4cf719c9ed4fda07fc90d81149e1d05169d11cd1d785667801cb88479dbf4628c94ba5477048415e718a4fb09ce7060c35850bde1fc0673649c5e4f5ff59

4 weeks agoci: pin yoke-derive to 0.8.2 for MSRV
志宇 [Thu, 17 Sep 2026 04:38:47 +0000 (04:38 +0000)]
ci: pin yoke-derive to 0.8.2 for MSRV

yoke-derive 0.8.3 calls the inherent `str::from_utf8`, which is not
available on Rust 1.85.0, and it does not declare a `rust-version`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
5 weeks agoMerge bitcoindevkit/bdk#2246: feat(chain)!: taint-aware `CanonicalView::balance`
merge-script [Sat, 12 Sep 2026 11:34:56 +0000 (11:34 +0000)]
Merge bitcoindevkit/bdk#2246: feat(chain)!: taint-aware `CanonicalView::balance`

e2dad373b0e2023a58c67afb073406eeaf028374 bench(chain): benchmark classify_outpoints across canonical topologies (Dmenec)
01b466018c98af9aa3b7335b31c1ea23de0e3f42 test(chain): cover taint propagation and eligibility classification (Dmenec)
3f7c1bde852826cd3df0a12dd0ae8023a6f0bb07 feat(chain)!: derive balance trust from output ancestry (志宇)
98a7d5b88e975d529a4c8e6446ef8c3a4314281e feat(chain): classify outpoints by chain-level spend eligibility (Dmenec)

Pull request description:

  Solves #2267

  ### Description

  Takes over #2235 (thanks @evanlinjin for the go-ahead). It reworks `CanonicalView::balance` to derive trust from an output's unconfirmed ancestry, and adds `classify_outpoints`, a per-output spend-eligibility classifier that `balance` becomes a thin fold over.

  The old `balance` decided trust using a per-output `trust_predicate`, which cannot express transitive trust. As a result, owned outputs whose unconfirmed ancestry included foreign coins were counted as trusted. That is the root cause of the wallet trust-classification bugs (bitcoindevkit/bdk_wallet#16, bitcoindevkit/bdk_wallet#273).

  The API now takes two separate predicates, one per concern:

  - `does_taint(&tx)` - should this transaction be considered tainted? (e.g., because it spends a foreign unconfirmed output)
  - `is_settled(&pos)` - do we consider this chain position settled / final? (generalizes `min_confirmations`)

  For each unspent output, `classify_outpoints` reports its chain-level spend eligibility:

  - `Settled` if considered settled according to `is_settled`
  - `Immature` for a coinbase output that has not yet matured
  - `Unsettled(Trust)` otherwise, where `Trust` is:
    - `Trusted` if the whole unconfirmed ancestry only spends owned coins
    - `Untrusted` if the output itself, or any unconfirmed ancestor, is tainted
    - `Unknown` if part of the ancestry is missing from the view, so trust can't be determined

  `balance` then sums each output's value into the bucket corresponding to its `Eligibility`.

  ### Notes to the reviewers

  - Trust is resolved through a self-contained ancestry walk. The traversal is memoized: each visited transaction is cached so that already-classified transactions (and their ancestors) do not need to be walked again.
  - Kept `Balance::confirmed` and did not rename it to `settled`. That rename is out of scope here. The folding logic is slated to move into `bdk_wallet`, and the current `balance` function in chain will eventually be deprecated.
  - `balance` also drops the `O` generic and now takes plain `OutPoint`s, since the taint predicate operates on transactions rather than per-outpoint associated data.
  - `does_taint` is evaluated at most once per transaction.
  - More sophisticated classification rules (e.g., for coin control or locked funds) can be built on top of `classify_outpoints`. Left for a follow-up.

  ### Changelog notice

  - **Breaking**: `CanonicalView::balance` now takes `does_taint: impl FnMut(&CanonicalTx) -> bool` and `is_settled: impl Fn(&ChainPosition) -> bool` instead of a per-output trust predicate and `min_confirmations`, and plain `OutPoint`s instead of `(identifier, outpoint)` pairs (this drops the `O` generic). Trust is now derived from an output's unconfirmed ancestry.
  - **Breaking**: added `Balance::unknown_pending`, where outputs whose trust can't be determined are counted instead of being lumped into `untrusted_pending`.
  - Added `CanonicalView::classify_outpoints` and the `Eligibility` enum.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `just p` before pushing

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [x] This pull request breaks the existing API

ACKs for top commit:
  evanlinjin:
    ACK e2dad373b0e2023a58c67afb073406eeaf028374
  nymius:
    ACK e2dad373b0e2023a58c67afb073406eeaf028374

Tree-SHA512: 30db16f27b3068f7ed2e0ac43b199438437622a66ff1a1a8d9fd854d22895384979a3e2305139ca7955137087bde832c64d31807069f60f08293c5e194234693

5 weeks agofix(chain): fix integer overflow in lookahead_to_target
busayo-OD [Thu, 10 Sep 2026 22:42:56 +0000 (23:42 +0100)]
fix(chain): fix integer overflow in lookahead_to_target

lookahead_to_target treated an out-of-range target_index as a
silent no-op, which is wrong — it should clamp to BIP32_MAX_INDEX
and replenish up to that point, matching reveal_to_target's
best-effort semantics.

Use saturating_add/saturating_sub, which no longer needs checked
arithmetic since replenish_inner_index's stop_index now clamps
correctly on its own.

5 weeks agofix(chain): fix off-by-one in replenish_inner_index stop bound
busayo-OD [Thu, 10 Sep 2026 22:41:23 +0000 (23:41 +0100)]
fix(chain): fix off-by-one in replenish_inner_index stop bound

replenish_inner_index clamped stop_index (an exclusive bound) to
BIP32_MAX_INDEX, excluding the boundary itself and causing a panic
when a keychain's index was revealed exactly up to BIP32_MAX_INDEX.
The addition feeding into that clamp was also unchecked, risking
overflow if lookahead was large relative to an already-high
last_revealed.

Use saturating_add throughout, clamping to BIP32_MAX_INDEX + 1 so
the boundary value is included as documented.

5 weeks agobench(chain): benchmark classify_outpoints across canonical topologies
Dmenec [Wed, 22 Jul 2026 17:05:11 +0000 (19:05 +0200)]
bench(chain): benchmark classify_outpoints across canonical topologies

Times the memoized classification over fan-in, disjoint, untrusted, diamond
and deep-taint-mid-chain graphs at a range of widths and depths.

5 weeks agotest(chain): cover taint propagation and eligibility classification
Dmenec [Wed, 22 Jul 2026 16:43:47 +0000 (18:43 +0200)]
test(chain): cover taint propagation and eligibility classification

- taint propagates through a pending output's unsettled ancestry
- is_settled alone decides the settled boundary, even for unconfirmed outputs
- taint never crosses a settled ancestor
- an immature coinbase is classified apart from a settled output
- two UTXOs sharing a tainting ancestor are both untrusted (shared taint cache)
- classify_outpoints skips spent and out-of-view outpoints

Co-authored-by: 志宇 <hello@evanlinjin.me>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 weeks agofeat(chain)!: derive balance trust from output ancestry
志宇 [Wed, 22 Jul 2026 16:35:37 +0000 (18:35 +0200)]
feat(chain)!: derive balance trust from output ancestry

balance becomes a fold over classify_outpoints. A pending output's trust is now
taken from its unsettled ancestry (untrusted if an unsettled ancestor taints or
is missing from the view), not from a per-output flag.

BREAKING CHANGE: balance takes does_taint and is_settled instead of trust_predicate
and min_confirmations, and plain OutPoints instead of (identifier, outpoint)
pairs.

Co-authored-by: Dmenec <dmenec@proton.me>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5 weeks agoMerge bitcoindevkit/bdk#2259: chore(ci): add stale issue triage workflow
merge-script [Thu, 10 Sep 2026 07:51:58 +0000 (07:51 +0000)]
Merge bitcoindevkit/bdk#2259: chore(ci): add stale issue triage workflow

5edb32302e5fd6a7e3e258980ca4d4cfd684e5d9 chore(ci): add stale issue triage workflow (Rishit Modi)

Pull request description:

  ### Description

  Follow-up to bitcoindevkit/bdk_wallet#497.

  Adds `.github/workflows/stale.yml`, based on the workflow introduced in the referenced PR, to automatically label and close inactive issues and abandoned pull requests in this repository.

  The workflow uses `actions/stale@v11`

  - Issues inactive for 180 days -> marked stale -> closed after 30 additional days
  - Pull requests inactive for 180 days -> marked stale -> closed after 30 additional days
  - Issue exemptions: `release`, `security`, `audit`, `do-not-close`, and `good first issue`
  - Pull request exemptions: `blocked`, `security`, and `do-not-close`
  - Issues and pull requests associated with milestones are exempt
  - Runs weekly on Mondays at 09:00 UTC
  - Pull request runs are automatically forced into dry-run mode for safe validation
  - Stale labels are removed when new activity occurs

  The timing and exemption policy are initially kept consistent with bitcoindevkit/bdk_wallet#497. They can be adjusted based on feedback.

  ### Notes to the reviewers

  The workflow runs with `debug-only: true` on pull request events. I also recommend running it manually with `debug_only=true` after merging and reviewing the results before the first scheduled Monday run.

  ### Changelog notice

  ci: add automated stale issue and pull request triage

  ### Checklists

  #### All Submissions:

  - [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

ACKs for top commit:
  notmandatory:
    ACK 5edb32302e5fd6a7e3e258980ca4d4cfd684e5d9

Tree-SHA512: 99bc6eb275feaf0ee4732e7570d5b46a46d96f482fa9b24406a3688b4824f9489bda65c51efdbc4b6ec34a72febe93219cd803b53c275bc3b273a4c4182bbcca

5 weeks agoMerge bitcoindevkit/bdk#2263: feat(chain): add ChainPosition::confirmations_lower_bound
merge-script [Thu, 10 Sep 2026 01:54:22 +0000 (01:54 +0000)]
Merge bitcoindevkit/bdk#2263: feat(chain): add ChainPosition::confirmations_lower_bound

7ebc9d3ca216eafb11fd1d976955f9e3500108c6 feat(chain): add ChainPosition::confirmations_lower_bound (Dmenec)

Pull request description:

  Suggested by @nymius in https://github.com/bitcoindevkit/bdk/pull/2246#discussion_r3796478212.

  ### Description

  Adds `ChainPosition::confirmations_lower_bound(tip)`, which returns the number of blocks mined on top of a confirmed position's block, given the chain `tip`. Returns `None` if the position is unconfirmed, or if the confirmation height is above `tip`.

  ### Changelog notice

  - Added `ChainPosition::confirmations_lower_bound`, returning the number of blocks mined on top of a confirmed position's block given the chain tip.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `just p` before committing

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

ACKs for top commit:
  evanlinjin:
    ACK 7ebc9d3ca216eafb11fd1d976955f9e3500108c6

Tree-SHA512: 32bc2ff794f9aa7a502589b6d8feca215c92dda1075b0dae25c7d5bb48d9c62f2af0c138bf47367bc75e5c0ee176e7290e0eeea152681b2dc172375cba6ebb37

5 weeks agofeat(chain): add ChainPosition::confirmations_lower_bound
Dmenec [Thu, 20 Aug 2026 10:37:29 +0000 (12:37 +0200)]
feat(chain): add ChainPosition::confirmations_lower_bound

5 weeks agofeat(chain): classify outpoints by chain-level spend eligibility
Dmenec [Wed, 22 Jul 2026 15:30:35 +0000 (17:30 +0200)]
feat(chain): classify outpoints by chain-level spend eligibility

Adds classify_outpoints, which decides per-output whether it's settled,
immature, or pending (trusted, untrusted, or unknown) based on its unsettled
ancestry. Trust is resolved with a memoized ancestry walk: a tainting ancestor
makes it untrusted, one missing from the view makes it unknown, and ancestors
shared by several outputs are only walked once.

Co-authored-by: 志宇 <hello@evanlinjin.me>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6 weeks agofix(chain): clamp derivation index before apply_changeset
busayo-OD [Sun, 6 Sep 2026 15:20:22 +0000 (16:20 +0100)]
fix(chain): clamp derivation index before apply_changeset

KeychainTxOutIndex::apply_changeset accepts ChangeSet::last_revealed
values above BIP32_MAX_INDEX, violating an invariant relied on by
the indexer.

Clamp each last_revealed value to BIP32_MAX_INDEX before storing
it, keeping changeset application infallible and monotone.

6 weeks agochore(deps): bump codecov/codecov-action from 6.0.0 to 7.0.0 github/dependabot/github_actions/codecov/codecov-action-7.0.0
dependabot[bot] [Sat, 5 Sep 2026 04:02:53 +0000 (04:02 +0000)]
chore(deps): bump codecov/codecov-action from 6.0.0 to 7.0.0

Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/57e3a136b779b570ffcdbf80b3bdc90e7fab3de2...fb8b3582c8e4def4969c97caa2f19720cb33a72f)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
6 weeks agoMerge bitcoindevkit/bdk#2262: fix(chain): let the indexer decide how far to rescan
merge-script [Wed, 2 Sep 2026 00:52:43 +0000 (00:52 +0000)]
Merge bitcoindevkit/bdk#2262: fix(chain): let the indexer decide how far to rescan

f08eeac1547e4a4585933989573ede4db4dcb9ba fix(chain): let the indexer decide how far to rescan (LLFourn)

Pull request description:

  ### Description

  `IndexedTxGraph::reindex` was a single pass over `TxGraph::full_txs`, which iterates a `HashMap`. That made it **nondeterministic**: the same graph reindexed twice could produce two different `last_revealed` results.

  Each match inside `KeychainTxOutIndex::_index_txout` bumps `last_revealed` and then calls `replenish_inner_index`, whose stop index is `last_revealed + 1 + lookahead`. So a match *widens the derived window the remaining outputs are judged against*. An output far enough out to need that widening was skipped if it happened to be visited first, and no pass revisited it.

  Concretely, with `lookahead` 1000 and an empty frontier the derived set is `0..1001`. Given one tx paying index 900 and another paying 1850: visited 900-first, the window grows to 1901 and both are found; visited 1850-first, 1850 is missed and stays missed. Same inputs, different index, decided by `HashMap` iteration order.

  The usual objection — that callers should reveal before reindexing — does not hold. The lookahead exists precisely to catch indices the persisted frontier does not know about: a restored wallet, another signer on the same descriptor, an externally built PSBT paying one of our far indices. Whenever the lookahead does its job the frontier moves mid-walk, so the order dependence is present in the intended use, not just in misuse.

  ### The change

  Add `Indexer::rescan`, handed the whole `TxGraph` and returning the indexing it produced:

  ```rust
  fn rescan<A>(&mut self, graph: &TxGraph<A>) -> Self::ChangeSet
  where
      Self::ChangeSet: Merge,
  ```

  The default implementation offers every full transaction and floating output to `index_tx` / `index_txout` exactly once — all an indexer needs when what it recognizes is fixed up front — and `IndexedTxGraph::reindex` becomes a call to it. `KeychainTxOutIndex` overrides it and looks repeatedly, stopping when a pass leaves its revealed frontier unmoved.

  Whether to look more than once belongs to the indexer, because the indexer is the only thing that knows whether its recognition set can still grow. Two things follow from putting it there rather than in `IndexedTxGraph`:

  - **An indexer that does not widen what it matches is walked exactly once**, as today. `SpkTxOutIndex` is unaffected. There is no convergence requirement imposed on implementors, and no way for a third-party indexer to be spun forever by a loop it never asked for.
  - **`KeychainTxOutIndex` can key the loop on its own frontier** rather than on whether a changeset came back empty. That matters: the changeset also carries staged spk cache entries, which move *without* the frontier moving. A `changeset.is_empty()` loop therefore spends an extra full walk on the ordinary restore path. I measured this — with `persist_spks = true`, restoring via `from_changeset` with a correct frontier takes **1 pass** keyed on the frontier versus **2** keyed on changeset emptiness.

  ### Notes to the reviewers

  **On the test shape.** The regression test uses **one** transaction with two of our outputs rather than two transactions. A two-transaction test would depend on graph walk order — the very thing that is unreliable — and so would pass a single-pass implementation about half the time, which is a test that fails to fail. `index_tx` walks `tx.output`, a `Vec`, in vout order, so a single tx paying a far index at vout 0 and a near one at vout 1, against an empty frontier, misses the far output on every pass on every run. It is deterministic by construction; I verified it fails against the old implementation on five consecutive runs (`left: Some(9)`, `right: Some(15)`).

  **On cost.** `KeychainTxOutIndex::rescan` re-walks the whole graph per look, so it is O(looks × txs), with looks bounded by the number of distinct frontier advances plus one. Measured: the settled/restore case is 1 look; the recovery case in the test (empty frontier, far output only reachable after the near one lands) is 3. Making a later look re-examine only the outputs that did not already match would need the indexer to say *what* to re-offer rather than just *whether*, which is a bigger change than this fix.

  **Trait change.** `rescan` is a defaulted method, so existing `Indexer` implementations keep compiling unchanged. It is generic over the anchor `A`, which makes `Indexer` no longer object-safe — nothing in the workspace uses `dyn Indexer`.

  **Known adjacent gap, not addressed here.** `index_tx_graph_changeset` — used by `insert_tx`, `insert_txout`, `apply_update` and `apply_block_relevant` — is still a single pass and has the same order dependence. Feeding the one-transaction case above through `insert_tx` yields `last_revealed = Some(9)` and only one of the two outpoints, so a live wallet can drop the far UTXO until the next restart re-runs `reindex`. It needs the incremental paths to go through the same mechanism rather than their own copy; I kept this PR to `reindex` to stay reviewable, and am happy to follow up.

  ### Changelog notice

  - Added: `Indexer::rescan`, which indexes an entire `TxGraph` and lets an implementation decide how many looks that takes. Defaulted, so existing implementations are unaffected.
  - Fixed: `IndexedTxGraph::reindex` no longer depends on `HashMap` iteration order. It previously made a single pass and could miss outputs at derivation indices that only came into range after another output in the same walk advanced the lookahead; `KeychainTxOutIndex` now looks until its revealed frontier stops moving.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing

ACKs for top commit:
  evanlinjin:
    ACK f08eeac1547e4a4585933989573ede4db4dcb9ba

Tree-SHA512: 0c5496805655a2c0f060e26bacd05a48adb2866961a9b785b53b444572a5b2e7f78ad0ddbcb737a308be5e1958bb0c49c09ed1e56fb30d029e06661f9e10a953

6 weeks agofix(chain): let the indexer decide how far to rescan
LLFourn [Wed, 19 Aug 2026 05:15:39 +0000 (15:15 +1000)]
fix(chain): let the indexer decide how far to rescan

`IndexedTxGraph::reindex` was a single pass over `TxGraph::full_txs`,
which iterates a `HashMap`, so the same graph reindexed twice could
produce two different `last_revealed` results. Each match inside
`KeychainTxOutIndex::_index_txout` bumps `last_revealed` and replenishes
the lookahead, widening the derived window the remaining outputs are
judged against. An output far enough out to need that widening was
skipped whenever it happened to be visited first, and no pass revisited
it.

This is not merely misuse of the API. The lookahead exists precisely to
catch indices the persisted frontier does not know about -- a restored
wallet, another signer on the same descriptor, an externally built PSBT
paying one of our far indices -- so whenever it does its job the frontier
moves mid-walk, and the order dependence is present in the intended use.

Add `Indexer::rescan`, which is handed the whole graph and returns what
indexing it produced. The default implementation offers every full
transaction and floating output once, which is all an indexer needs when
what it recognizes is fixed up front; `reindex` becomes a call to it.
`KeychainTxOutIndex` overrides it and looks repeatedly, stopping when a
pass leaves its revealed frontier unmoved.

Putting the loop behind the trait rather than in `IndexedTxGraph` keeps
the decision where the knowledge is. An indexer that does not widen
what it matches is walked exactly once, as before, so the looping
cannot leak into implementations that have no use for it. And
`KeychainTxOutIndex` can key the loop on its own frontier instead of on
whether a changeset came back empty: the changeset also carries staged
spk cache entries, which move without the frontier moving, so an
emptiness test spends an extra full walk on the ordinary restore path.

The regression test uses a single transaction with both outputs rather
than two transactions, because the graph's walk order is a `HashMap`
order: a two-transaction test would pass a single-pass implementation
about half the time. `index_tx` walks `tx.output` in vout order, so the
far index at vout 0 is always judged against the initial window and
always missed until the near index at vout 1 lifts the frontier.

2 months agoMerge bitcoindevkit/bdk#2257: fix: fix command order for importing GPG key
merge-script [Mon, 17 Aug 2026 07:49:39 +0000 (07:49 +0000)]
Merge bitcoindevkit/bdk#2257: fix: fix command order for importing GPG key

3474e0c6d02e527c1f8a38660191e243026dac0d fix: fix command order for importing GPG key (Carlos Marques)

Pull request description:

  ### Fix

  Options order wrong. `--recv-keys` eat everything after as key IDs.
  Put `--keyserver` first.

ACKs for top commit:
  thunderbiscuit:
    ACK 3474e0c6d02e527c1f8a38660191e243026dac0d. Tested and our current line is indeed incorrect; proposed fix works.
  luisschwab:
    ACK 3474e0c6d02e527c1f8a38660191e243026dac0d
  evanlinjin:
    ACK 3474e0c6d02e527c1f8a38660191e243026dac0d

Tree-SHA512: 6f31c18a21507260fd207757bf06fb4660459a35002f3020ac1b1651c4441389495564a84317a25046d49f5af8458a56e9874a0b1d32b729b791c18ab138b4ce

2 months agochore(ci): add stale issue triage workflow
Rishit Modi [Fri, 14 Aug 2026 22:28:02 +0000 (03:58 +0530)]
chore(ci): add stale issue triage workflow

2 months agofix: fix command order for importing GPG key
Carlos Marques [Tue, 11 Aug 2026 13:54:19 +0000 (14:54 +0100)]
fix: fix command order for importing GPG key

2 months agoMerge bitcoindevkit/bdk#2255: Add GH security advisory link on SECURITY.md
merge-script [Wed, 5 Aug 2026 22:02:30 +0000 (19:02 -0300)]
Merge bitcoindevkit/bdk#2255: Add GH security advisory link on SECURITY.md

a98ab189340c6ed2a2fbc91ee6d8178950e09b6b chore(doc): add GH security advisory link on SECURITY.md (Luis Schwab)

Pull request description:

  Link to GitHub's security advisory page on `SECURITY.md`

ACKs for top commit:
  oleonardolima:
    ACK a98ab189340c6ed2a2fbc91ee6d8178950e09b6b

Tree-SHA512: 2e25062934d717f05c6a567d33ca27445594eafbb6aeefc648c5108a0d7631f4cac30861e857f28d88fb501ac72ef24f4fef6814144e659c60edbde7657108ec

2 months agochore(doc): add GH security advisory link on SECURITY.md
Luis Schwab [Wed, 5 Aug 2026 20:03:54 +0000 (17:03 -0300)]
chore(doc): add GH security advisory link on SECURITY.md

2 months agoMerge bitcoindevkit/bdk#2240: fix(chain): skip evicted txs in leftover canonicalization
merge-script [Fri, 24 Jul 2026 10:10:25 +0000 (10:10 +0000)]
Merge bitcoindevkit/bdk#2240: fix(chain): skip evicted txs in leftover canonicalization

2a8512b15706e3f05a4a506af0293c2d33d12581 fix(chain): skip evicted leftover transactions (Noah Joeris)
9e404720bbc0ba462a661141c01325815aa6c180 feat(chain): add TxNode::is_evicted (Noah Joeris)

Pull request description:

  ### Description

  `CanonicalStage::LeftOverTxs` doesn't filter evicted txs and can mark them canonical. So a stale-anchored tx that was evicted/replaced could still show as unconfirmed.

  Example: a tx confirms, then a reorg leaves only a stale anchor. Leftover treats it as unconfirmed. If it's later RBF'd or missing from the mempool, leftover still marks it unconfirmed.

  ### Changelog notice
  - Add `TxNode::is_evicted()` for determining mempool eviction state.
  - Skip evicted transactions during leftover canonicalization.

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [x] I've added tests to reproduce the issue which are now passing
  * [ ] I'm linking the issue being fixed by this PR

ACKs for top commit:
  evanlinjin:
    ACK 2a8512b15706e3f05a4a506af0293c2d33d12581

Tree-SHA512: 615a46406600115210a272668b01e9a85a6d6fbbcc9d055cf83617fcc5c6e742f845b57e34277e26ecdffe41f39b01576ae038cd7eed5ce916fcb505b58fc8e8

2 months agofix(chain): skip evicted leftover transactions
Noah Joeris [Wed, 22 Jul 2026 19:23:11 +0000 (22:23 +0300)]
fix(chain): skip evicted leftover transactions

2 months agofeat(chain): add TxNode::is_evicted
Noah Joeris [Wed, 22 Jul 2026 19:22:54 +0000 (22:22 +0300)]
feat(chain): add TxNode::is_evicted

3 months agoMerge bitcoindevkit/bdk#2209: ci: individual package tests
merge-script [Mon, 13 Jul 2026 21:08:22 +0000 (17:08 -0400)]
Merge bitcoindevkit/bdk#2209: ci: individual package tests

2185dcbb31d512d708e8c117c5b1a429ff52ce81 ci: Update cont_integration.yml (valued mammal)

Pull request description:

  Close #1944

  ### Description

  Replaces the monolithic `build-test` matrix job (which ran a 2-toolchain × 2-feature-set matrix across the whole workspace) with dedicated per-crate build jobs. Each crate uses named steps for every meaningful feature combination, making it easy to identify which specific crate and feature set broke in CI.

  **Changes:**

  - **Per-crate jobs**: `build-core`, `build-chain`, `build-bitcoind-rpc`, `build-electrum`, `build-esplora`, `build-file-store`, `build-testenv` — each with per-feature `cargo build` steps and a `cargo test --all-features` step
  - **`msrv` job**: Simplified from a matrix to a single flat job targeting 1.85.0; runs `cargo build --workspace --all-targets` and `cargo test --workspace --lib --tests --all-features`
  - **`check-no-std`**: Updated to check `bdk_core` and `bdk_chain` with no default features + with `hashbrown` and `miniscript`; cross-compilation against `thumbv6m-none-eabi` is stubbed out as usual to be addressed in a separate PR

Top commit has no ACKs.

Tree-SHA512: d912a967ded7eb23bb25dbe6c59f11361b34e2cd77d1c6f3971b02261b4169ec96ab69bf68e6b667530120d8f75610bd85bfbc7da47d9d8cdf79d54ba4b1cb01

3 months agoci: Update cont_integration.yml
valued mammal [Fri, 15 May 2026 21:47:34 +0000 (17:47 -0400)]
ci: Update cont_integration.yml

Added workspace level MSRV job.

Added per-crate jobs to build meaningful feature
combinations and test all features.

Removed build-test job.

3 months agoMerge bitcoindevkit/bdk#2210: docs(rpc): add Emitter::next_block and Emitter::mempool...
merge-script [Sat, 11 Jul 2026 22:50:43 +0000 (18:50 -0400)]
Merge bitcoindevkit/bdk#2210: docs(rpc): add Emitter::next_block and Emitter::mempool examples

30fa17955b16d6a5a435bea8e2b9728e9e0b3701 docs(rpc): add Emitter::next_block and Emitter::mempool examples (Kyle 🐆)

Pull request description:

  Adds `# Example` rustdoc to `Emitter::next_block` and `Emitter::mempool`, which were flagged as missing in the #2006 review.

  Originally this PR also removed `example_bitcoind_rpc_polling`, but #2006 has since merged and removed all example crates, so that part is dropped after rebase. Only the doc additions remain.

  Part of the #2006 follow-up to document the components previously covered by the `examples/` crates.

ACKs for top commit:
  ValuedMammal:
    ACK 30fa17955b16d6a5a435bea8e2b9728e9e0b3701

Tree-SHA512: 28ade24c33f9714f94282a4594024bff9ed304c461b477b7ba5e95de64fbc5951c23a6a93ee623cc764e1e31971aaccac3aaebe4f615637d5b2de6d56bc32b56

3 months agodocs(rpc): add Emitter::next_block and Emitter::mempool examples
Kyle 🐆 [Sat, 23 May 2026 18:59:47 +0000 (14:59 -0400)]
docs(rpc): add Emitter::next_block and Emitter::mempool examples

4 months agoMerge bitcoindevkit/bdk#2203: ci: automated update to rustc 1.96.0
merge-script [Mon, 15 Jun 2026 17:49:37 +0000 (14:49 -0300)]
Merge bitcoindevkit/bdk#2203: ci: automated update to rustc 1.96.0

c1cffa3fd6b55f2fc0c862fd059ca1fa427a025c ci: automated update to rustc 1.96.0 (Github Action)

Pull request description:

  Automated update to Github CI workflow `cont_integration.yml` by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action

ACKs for top commit:
  oleonardolima:
    ACK c1cffa3fd6b55f2fc0c862fd059ca1fa427a025c

Tree-SHA512: 28f681fe56bfee891c06c8d324cc89b6af9fb40af976ad5eebfe89e8ee0c2ea7d5ad0c9ad1d2849d77c4fc202474b8032285bf4b42a5f8f544a86e5e6a115c71

4 months agoci: automated update to rustc 1.96.0
Github Action [Mon, 15 Jun 2026 03:43:59 +0000 (03:43 +0000)]
ci: automated update to rustc 1.96.0

4 months agoMerge bitcoindevkit/bdk#2038: refactor(chain,core)!: replace `CanonicalIter` with...
merge-script [Mon, 15 Jun 2026 12:42:14 +0000 (09:42 -0300)]
Merge bitcoindevkit/bdk#2038: refactor(chain,core)!: replace `CanonicalIter` with sans-IO `CanonicalTask` + `ChainQuery` trait

555f774169ea934dbb2934f2fbe6763908e86670 fix(chain): position resolution for assumed txs (Leonardo Lima)
4f10cdef4d55139ee7f608d9cf90ff8d9c0b3037 chore(chain,example)!: remove `ChainOracle`, update docs (志宇)
a3c73e4685f3381b5b1ae799c2279009b6f7d1ea refactor(chain)!: split `canonical_view` into `canonical`,`canonical_view_task` (志宇)
22b04c33dc0b75e5a247f99087ad36f41e99a0ed refactor(chain)!: split canonicalization into two tasks with generic `Canonical<A, P>` (志宇)
a9f5ca4415af36b42cca43551414ad0644d627b8 refactor(chain)!: remove `CanonicalIter` APIs (Leonardo Lima)
bb83da8260fb167fa548f31c12710153095e9064 chore(workspace): use new `LocalChain::canonical_view` API (Leonardo Lima)
987da73fce7b7f4e015aa05cc8fa4e36deebf4df feat(core,chain): introduce `CanonicalizationTask` and `ChainQuery` (Leonardo Lima)

Pull request description:

  fixes #1816

  ### Description

  Replaces the iterator-based `CanonicalIter` with a two-phase sans-IO canonicalization pipeline, and introduces a generic `ChainQuery` trait in `bdk_core` to decouple canonicalization from chain sources.

  **Old API:**
  ```rust
  // Direct coupling between canonicalization logic and ChainOracle
  let view = tx_graph.canonical_view(&chain, chain_tip, params)?;
  ```

  **New API:**
  ```rust
  // Option A: Two-phase (full control)
  let canonical_txs = chain.canonicalize(tx_graph.canonical_task(tip, params));
  let view = chain.canonicalize(canonical_txs.view_task(&tx_graph));

  // Option B: Convenience method
  let view = chain.canonical_view(&tx_graph, tip, params);
  ```

  #### Phase 1: `CanonicalTask`
  Determines which transactions are canonical by processing them in stages:
  1. **Assumed txs** — transactions assumed canonical via `CanonicalParams`
  2. **Anchored txs** — transactions anchored in the best chain (descending height)
  3. **Seen txs** — unconfirmed transactions by descending last-seen time
  4. **Remaining txs** — leftover anchored transactions not in the best chain

  Produces a `CanonicalTxs<A>` containing each canonical transaction with its `CanonicalReason`.

  #### Phase 2: `CanonicalViewTask`
  Resolves `CanonicalReason`s into concrete `ChainPosition`s (confirmed height or unconfirmed with last-seen), producing the final `CanonicalView<A>`.

  Both phases implement the `ChainQuery` trait, so any chain source can drive them via the same `next_query`/`resolve_query` loop.

  #### Key structural changes

  - **`ChainQuery` trait** added to `bdk_core` — a generic sans-IO interface (`next_query` → `resolve_query` → `finish`) for any algorithm that needs to verify blocks against a chain source.
  - **`ChainOracle` trait removed** — replaced by `ChainQuery`. `LocalChain::canonicalize()` now drives any `ChainQuery` implementor.
  - **`Canonical<A, P>` generic container** — `CanonicalTxs<A>` (phase 1 output) and `CanonicalView<A>` (phase 2 output) are type aliases over `Canonical<A, P>`.
  - **Module split** — `canonical_view.rs` split into `canonical.rs` (types: `Canonical`, `CanonicalTx`, `CanonicalTxOut`) and `canonical_view_task.rs` (phase 2 task). `canonical_iter.rs` replaced by `canonical_task.rs`.

  ### Notes to the reviewers

  The changes are split into multiple commits for easier review. Also depends on #2029.

  ### Changelog notice

  ```
    ### Added
    - `bdk_core::ChainQuery` trait — generic sans-IO interface for chain verification queries
    - `bdk_core::ChainRequest` / `ChainResponse` type aliases
    - `CanonicalTask` — phase 1 sans-IO canonicalization (determines canonical txs)
    - `CanonicalViewTask` — phase 2 sans-IO canonicalization (resolves chain positions)
    - `Canonical<A, P>` generic container with `CanonicalTxs<A>` and `CanonicalView<A>` aliases
    - `LocalChain::canonicalize()` — drives any `ChainQuery` implementor
    - `LocalChain::canonical_view()` — convenience method for full two-phase canonicalization

    ### Changed
    - **Breaking:** Replace `TxGraph::canonical_iter()` / `TxGraph::canonical_view()` with `TxGraph::canonical_task()`
    - **Breaking:** Canonicalization now uses a two-phase sans-IO process via `ChainQuery`
    - **Breaking:** `ChainQuery`, `ChainRequest`, `ChainResponse` have no generics (use `BlockId` directly)
    - **Breaking:** Chain tip moved from `ChainRequest` to `ChainQuery::tip()`

    ### Removed
    - **Breaking:** `ChainOracle` trait and all implementations
    - **Breaking:** `CanonicalIter` type and `canonical_iter` module
    - **Breaking:** `TxGraph::try_canonical_view()` and `TxGraph::canonical_view()` methods
    - **Breaking:** `CanonicalView::new()` public constructor
  ```

  ### Checklists

  #### All Submissions:

  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

ACKs for top commit:
  evanlinjin:
    ACK 555f774169ea934dbb2934f2fbe6763908e86670

Tree-SHA512: ce466a623a1f8df20dedd3e4e68460892bd369567db44e6ba391c3d9ae1d10bca6204ebdcdb7b1376a9c97f3155b89e991846af4122e200d1fa15d998deb7830

4 months agofix(chain): position resolution for assumed txs
Leonardo Lima [Mon, 16 Mar 2026 23:01:22 +0000 (20:01 -0300)]
fix(chain): position resolution for assumed txs

- add new `test_canonical_view_task.rs` to handle different scenarios
  of chain position resolution.
- fixes the assumed canonical txs chain position resolution, especially for transitively
  assumed canonical transactions, where there's an anchored/confirmed descendant.

4 months agochore(chain,example)!: remove `ChainOracle`, update docs
志宇 [Sun, 8 Mar 2026 23:50:34 +0000 (23:50 +0000)]
chore(chain,example)!: remove `ChainOracle`, update docs

4 months agorefactor(chain)!: split `canonical_view` into `canonical`,`canonical_view_task`
志宇 [Fri, 13 Feb 2026 14:43:13 +0000 (14:43 +0000)]
refactor(chain)!: split `canonical_view` into `canonical`,`canonical_view_task`

It moves the shared types: `CanonicalTx`, `Canonical`, `CanonicalView`,
`CanonicalTxs` and other convenience methods into `canonical.rs`.

Keep the phase-2 task (`CanonicalViewTask` in `canonical_view_task.rs`.

Also, rename `FullTxOut` to `CanonicalTxOut`, and move it to
`canonical.rs`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 months agorefactor(chain)!: split canonicalization into two tasks with generic `Canonical<A...
志宇 [Fri, 13 Feb 2026 12:57:39 +0000 (12:57 +0000)]
refactor(chain)!: split canonicalization into two tasks with generic `Canonical<A, P>`

Separate concerns by splitting `CanonicalizationTask` into two phases:

1. `CanonicalTask` determines which transactions are canonical and why
   (`CanonicalReason`), outputting `CanonicalTxs<A>`.
2. `CanonicalViewTask` resolves reasons into `ChainPosition`s (confirmed
   vs unconfirmed), outputting `CanonicalView<A>`.

Make `Canonical<A, P>`, `CanonicalTx<P>`, and `FullTxOut<P>` generic over
the position type so the same structs serve both phases. Add
`LocalChain::canonical_view()` convenience method for the common two-step
pipeline.

Renames:

- `CanonicalizationTask` -> `CanonicalTask`
- `CanonicalizationParams` -> `CanonicalParams`
- `canonicalization_task()` -> `canonical_task()`
- `FullTxOut::chain_position` -> `FullTxOut::pos`

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>