]> Untitled Git - bdk/commitdiff
chore: remove bdk_wallet and update readme and ci workflow
authorSteve Myers <steve@notmandatory.org>
Sat, 5 Apr 2025 01:22:52 +0000 (20:22 -0500)
committerSteve Myers <steve@notmandatory.org>
Sat, 5 Apr 2025 01:22:52 +0000 (20:22 -0500)
42 files changed:
.github/workflows/cont_integration.yml
Cargo.toml
README.md
crates/wallet/CHANGELOG.md [deleted file]
crates/wallet/Cargo.toml [deleted file]
crates/wallet/README.md [deleted file]
crates/wallet/examples/compiler.rs [deleted file]
crates/wallet/examples/mnemonic_to_descriptors.rs [deleted file]
crates/wallet/examples/policy.rs [deleted file]
crates/wallet/src/descriptor/checksum.rs [deleted file]
crates/wallet/src/descriptor/dsl.rs [deleted file]
crates/wallet/src/descriptor/error.rs [deleted file]
crates/wallet/src/descriptor/mod.rs [deleted file]
crates/wallet/src/descriptor/policy.rs [deleted file]
crates/wallet/src/descriptor/template.rs [deleted file]
crates/wallet/src/keys/bip39.rs [deleted file]
crates/wallet/src/keys/mod.rs [deleted file]
crates/wallet/src/lib.rs [deleted file]
crates/wallet/src/psbt/mod.rs [deleted file]
crates/wallet/src/test_utils.rs [deleted file]
crates/wallet/src/types.rs [deleted file]
crates/wallet/src/wallet/changeset.rs [deleted file]
crates/wallet/src/wallet/coin_selection.rs [deleted file]
crates/wallet/src/wallet/error.rs [deleted file]
crates/wallet/src/wallet/export.rs [deleted file]
crates/wallet/src/wallet/mod.rs [deleted file]
crates/wallet/src/wallet/params.rs [deleted file]
crates/wallet/src/wallet/persisted.rs [deleted file]
crates/wallet/src/wallet/signer.rs [deleted file]
crates/wallet/src/wallet/tx_builder.rs [deleted file]
crates/wallet/src/wallet/utils.rs [deleted file]
crates/wallet/tests/psbt.rs [deleted file]
crates/wallet/tests/wallet.rs [deleted file]
example-crates/example_wallet_electrum/Cargo.toml [deleted file]
example-crates/example_wallet_electrum/src/main.rs [deleted file]
example-crates/example_wallet_esplora_async/Cargo.toml [deleted file]
example-crates/example_wallet_esplora_async/src/main.rs [deleted file]
example-crates/example_wallet_esplora_blocking/Cargo.toml [deleted file]
example-crates/example_wallet_esplora_blocking/src/main.rs [deleted file]
example-crates/example_wallet_rpc/Cargo.toml [deleted file]
example-crates/example_wallet_rpc/README.md [deleted file]
example-crates/example_wallet_rpc/src/main.rs [deleted file]

index f43a3d5967ecb05d5b22b6a193477f625d8635bb..8769ca1f37a8f2a55c331ccab079695a5acc0eb7 100644 (file)
@@ -89,10 +89,6 @@ jobs:
         working-directory: ./crates/chain
         # TODO "--target thumbv6m-none-eabi" should work but currently does not
         run: cargo check --no-default-features --features miniscript/no-std,hashbrown
-      - name: Check bdk wallet
-        working-directory: ./crates/wallet
-        # TODO "--target thumbv6m-none-eabi" should work but currently does not
-        run: cargo check --no-default-features --features miniscript/no-std,bdk_chain/hashbrown
       - name: Check esplora
         working-directory: ./crates/esplora
         # TODO "--target thumbv6m-none-eabi" should work but currently does not
@@ -123,9 +119,6 @@ jobs:
             target: "wasm32-unknown-unknown"
       - name: Rust Cache
         uses: Swatinem/rust-cache@v2.7.7
-      - name: Check bdk wallet
-        working-directory: ./crates/wallet
-        run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown
       - name: Check esplora
         working-directory: ./crates/esplora
         run: cargo check --target wasm32-unknown-unknown --no-default-features --features bdk_core/hashbrown,async
@@ -183,10 +176,6 @@ jobs:
           - example_bitcoind_rpc_polling
           - example_electrum
           - example_esplora
-          - example_wallet_electrum
-          - example_wallet_esplora_async
-          - example_wallet_esplora_blocking
-          - example_wallet_rpc
     steps:
       - name: checkout
         uses: actions/checkout@v4
index 2abc16bd8136af4074cb2cac9af7b230f784d18a..3f96ad1243ce1fab12ca9233364fb448c1fb9a82 100644 (file)
@@ -1,7 +1,6 @@
 [workspace]
 resolver = "2"
 members = [
-    "crates/wallet",
     "crates/chain",
     "crates/core",
     "crates/file_store",
@@ -13,10 +12,6 @@ members = [
     "example-crates/example_electrum",
     "example-crates/example_esplora",
     "example-crates/example_bitcoind_rpc_polling",
-    "example-crates/example_wallet_electrum",
-    "example-crates/example_wallet_esplora_blocking",
-    "example-crates/example_wallet_esplora_async",
-    "example-crates/example_wallet_rpc",
 ]
 
 [workspace.package]
index dc9ae77a94d6f7ee9a3843dc4946739967ac452a..e3fc7480f42982714578fd29068e3d3d3a495442 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,73 +1,72 @@
 # The Bitcoin Dev Kit
 
 <div align="center">
-  <h1>BDK</h1>
 
   <img src="./static/bdk.png" width="220" />
 
   <p>
-    <strong>A modern, lightweight, descriptor-based wallet library written in Rust!</strong>
+    <strong>A suite of libraries for building modern, lightweight, descriptor-based wallets written in Rust!</strong>
   </p>
 
   <p>
-    <a href="https://crates.io/crates/bdk_wallet"><img alt="Crate Info" src="https://img.shields.io/crates/v/bdk_wallet.svg"/></a>
     <a href="https://github.com/bitcoindevkit/bdk/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
     <a href="https://github.com/bitcoindevkit/bdk/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk/workflows/CI/badge.svg"></a>
     <a href="https://coveralls.io/github/bitcoindevkit/bdk?branch=master"><img src="https://coveralls.io/repos/github/bitcoindevkit/bdk/badge.svg?branch=master"/></a>
-    <a href="https://docs.rs/bdk_wallet"><img alt="Wallet API Docs" src="https://img.shields.io/badge/docs.rs-bdk_wallet-green"/></a>
     <a href="https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html"><img alt="Rustc Version 1.63.0+" src="https://img.shields.io/badge/rustc-1.63.0%2B-lightgrey.svg"/></a>
     <a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
   </p>
 
   <h4>
     <a href="https://bitcoindevkit.org">Project Homepage</a>
-    <span> | </span>
-    <a href="https://docs.rs/bdk_wallet">Documentation</a>
   </h4>
 </div>
 
 ## About
 
-The `bdk` libraries aims to provide well engineered and reviewed components for Bitcoin based applications.
-It is built upon the excellent [`rust-bitcoin`] and [`rust-miniscript`] crates.
+The `bdk` libraries aim to provide well engineered and reviewed components for Bitcoin wallets and other applications.
+They are built upon the excellent [`rust-bitcoin`] and [`rust-miniscript`] crates.
 
 ## Architecture
 
-The project is split up into several crates in the `/crates` directory:
+The workspace in this repository contains several crates in the `/crates` directory:
 
-- [`wallet`](./crates/wallet): Contains the central high level `Wallet` type that is built from the low-level mechanisms provided by the other components
-- [`chain`](./crates/chain): Tools for storing and indexing chain data
-- [`file_store`](./crates/file_store): Persistence backend for storing chain data in a single file. Intended for testing and development purposes, not for production.
-- [`esplora`](./crates/esplora): Extends the [`esplora-client`] crate with methods to fetch chain data from an esplora HTTP server in the form that [`bdk_chain`] and `Wallet` can consume.
-- [`electrum`](./crates/electrum): Extends the [`electrum-client`] crate with methods to fetch chain data from an electrum server in the form that [`bdk_chain`] and `Wallet` can consume.
+| Sub-Directory | Description | Badges |
+|---------------|-------------|--------|
+| [`chain`](./crates/chain) | Tools for storing and indexing chain data. | ![Chain Crate Info](https://img.shields.io/crates/v/bdk_chain.svg) ![Chain API Docs](https://img.shields.io/badge/docs.rs-bdk_chain-green) |
+| [`core`](./crates/core) | A collection of core structures used by the [`bdk_chain`], [`bdk_wallet`], and bdk chain data source crates. | ![Core Crate Info](https://img.shields.io/crates/v/bdk_core.svg) ![Core API Docs](https://img.shields.io/badge/docs.rs-bdk_core-green) |
+| [`esplora`](./crates/esplora) | Extends the [`esplora-client`] crate with methods to fetch chain data from an esplora HTTP server in the form that [`bdk_chain`] and `Wallet` can consume. | ![Esplora Crate Info](https://img.shields.io/crates/v/bdk_esplora.svg) ![Esplora API Docs](https://img.shields.io/badge/docs.rs-bdk_esplora-green) |
+| [`electrum`](./crates/electrum) | Extends the [`electrum-client`] crate with methods to fetch chain data from an electrum server in the form that [`bdk_chain`] and `Wallet` can consume. | ![Electrum Crate Info](https://img.shields.io/crates/v/bdk_electrum.svg) ![Electrum API Docs](https://img.shields.io/badge/docs.rs-bdk_electrum-green) |
+| [`bitcoind_rpc`](./crates/bitcoind_rpc) | Extends [`bitcoincore-rpc`] for emitting blockchain data from the `bitcoind` RPC interface. | ![BitcoinD RPC Crate Info](https://img.shields.io/crates/v/bdk_bitcoind_rpc.svg) ![BitcoinD RPC API Docs](https://img.shields.io/badge/docs.rs-bdk_bitcoind_rpc-green) |
+| [`file_store`](./crates/file_store) | Persistence backend for storing chain data in a single file. Intended for testing and development purposes, not for production. | ![File Store Crate Info](https://img.shields.io/crates/v/bdk_file_store.svg) ![File Store API Docs](https://img.shields.io/badge/docs.rs-bdk_file_store-green) |
+
+The [`bdk_wallet`] repository and crate contains a higher level `Wallet` type that depends on the above lower-level mechanism crates. 
 
 Fully working examples of how to use these components are in `/example-crates`:
+
 - [`example_cli`](./example-crates/example_cli): Library used by the `example_*` crates. Provides utilities for syncing, showing the balance, generating addresses and creating transactions without using the bdk_wallet `Wallet`.
 - [`example_electrum`](./example-crates/example_electrum): A command line Bitcoin wallet application built on top of `example_cli` and the `electrum` crate. It shows the power of the bdk tools (`chain` + `file_store` + `electrum`), without depending on the main `bdk_wallet` library.
 - [`example_esplora`](./example-crates/example_esplora): A command line Bitcoin wallet application built on top of `example_cli` and the `esplora` crate. It shows the power of the bdk tools (`chain` + `file_store` + `esplora`), without depending on the main `bdk_wallet` library.
 - [`example_bitcoind_rpc_polling`](./example-crates/example_bitcoind_rpc_polling): A command line Bitcoin wallet application built on top of `example_cli` and the `bitcoind_rpc` crate. It shows the power of the bdk tools (`chain` + `file_store` + `bitcoind_rpc`), without depending on the main `bdk_wallet` library.
-- [`example_wallet_esplora_blocking`](./example-crates/example_wallet_esplora_blocking): Uses the `Wallet` to sync and spend using the Esplora blocking interface.
-- [`example_wallet_esplora_async`](./example-crates/example_wallet_esplora_async): Uses the `Wallet` to sync and spend using the Esplora asynchronous interface.
-- [`example_wallet_electrum`](./example-crates/example_wallet_electrum): Uses the `Wallet` to sync and spend using Electrum.
 
 [`rust-miniscript`]: https://github.com/rust-bitcoin/rust-miniscript
 [`rust-bitcoin`]: https://github.com/rust-bitcoin/rust-bitcoin
 [`esplora-client`]: https://docs.rs/esplora-client/
 [`electrum-client`]: https://docs.rs/electrum-client/
+[`bitcoincore-rpc`]: https://docs.rs/bitcoincore-rpc/
 [`bdk_chain`]: https://docs.rs/bdk-chain/
+[`bdk_wallet`]: https://github.com/bitcoindevkit/bdk_wallet
 
 ## Minimum Supported Rust Version (MSRV)
-The BDK library maintains a MSRV of 1.63.0. This includes the following crates:
+
+The following BDK crates maintains a MSRV of 1.63.0. To build these crates with the MSRV of 1.63.0 you will need to pin dependencies by running the [`pin-msrv.sh`](./ci/pin-msrv.sh) script.
 
 - `bdk_core`
 - `bdk_chain`
-- `bdk_bitcoind_rpc`.
-- `bdk_esplora`.
-- `bdk_wallet`.
-
-The MSRV of `bdk_electrum` is 1.75.0.
+- `bdk_bitcoind_rpc`
+- `bdk_esplora`
+- `bdk_file_store`
 
-To build with the MSRV of 1.63.0 you will need to pin dependencies by running the [`pin-msrv.sh`](./ci/pin-msrv.sh) script.
+The MSRV of the `bdk_electrum` crate is 1.75.0.
 
 ## License
 
diff --git a/crates/wallet/CHANGELOG.md b/crates/wallet/CHANGELOG.md
deleted file mode 100644 (file)
index 2a86c18..0000000
+++ /dev/null
@@ -1,1274 +0,0 @@
-# Changelog
-
-All notable changes to this project can be found here and in each release's git tag and can be viewed with `git tag -ln100 "v*"`. See also [DEVELOPMENT_CYCLE.md](../../DEVELOPMENT_CYCLE.md) for more details.
-
-Contributors do not need to change this file but do need to add changelog details in their PR descriptions. The person making the next release will collect changelog details from included PRs and edit this file prior to each release.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
-and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-
-## [wallet-1.2.0]
-
-### Changed
-
-- Accept any type that is convertible to a `ScriptBuf` in `TxBuilder::add_recipient` #1841
-- Refactor/use iterators to preselect utxos #1798
-- Bump bitcoin dependency to v0.32.4 #1853
-- Pin bdk_chain version to latest release #1860
-- chore: bump `miniscript` to `12.3.1` #1924
-
-### Fixed
-
-- Fix off-by-one error checking coinbase maturity in optional UTxOs #1830
-- Fix PersistedWallet to be Send + Sync, even when used with a !Sync persister type such as rusqlite::Connection. #1874
-
-
-## [wallet-1.1.0]
-
-### Added
-
-- `Wallet` can now be constructed using `Network::Testnet4` #1805
-- test: create tx locktime cltv for a specific time #1682
-- docs: add architectural decision records (ADR) #1592
-
-### Changed
-
-- Changed the default transaction version number to version 2 for TxBuilder. #1789
-
-### Fixed
-
-- Improve safety on finalize psbt #1790
-- Fixed an issue preventing `build_fee_bump` from re-targeting the drain value for some wallets #1812
-
-## [wallet-1.0.0]
-
-### Summary
-
-This is the final bdk_wallet 1.0.0 release. It contains small improvements to the wallet `transactions` function and 
-`next_unused_address` API docs. Please thank all the [contributors] who made this first major release possible and
-who's continued effort make the BDK project so awesome! 
-
-[contributors]: https://github.com/bitcoindevkit/bdk/graphs/contributors
-
-### Changed
-
-- `Wallet::transactions` should only return relevant transactions. #1779
-- Minor updates to fix new rustc 1.83.0 clippy warnings. #1776
-
-### Documentation
-
-- Reword the `next_unused_address` API docs. #1680
-
-## [v1.0.0-beta.6]
-
-### Summary
-
-This is the final "beta" test release before a final `bdk_wallet` 1.0.0 version. Changes include small bug fixes and API improvements plus an improved algorithm for determining which transactions are in the current best "canonical" block chain. The new canonicalization algorithm processes the transaction graph in linear time versus the prior quadratic time algorithm.
-
-### Changed
-
-- Move transaction testing utilities from `crates/chain/tests/common` to `testenv` crate #1612
-- Remove bdk_chain::ConfirmationTime. Use ChainPosition<ConfirmationBlockTime> in its place. #1643
-- Fix building change signers in `load_with_params` #1662
-- Remove bdk_chain method KeychainTxOutIndex::inner #1652
-- Document bdk_file_store is a development/testing database #1661
-- Fix incorrect links in docs to wallet examples #1668
-- Add bdk_wallet "test-utils" feature flag that exposes common helpers for testing and development #1658
-- Removed methods Wallet::insert_tx, Wallet::insert_checkpoint, Wallet::unbroadcast_transactions #1658
-- Fix type constraint on list canonical tx #1724
-- Fix testenv docs.rs docs  #1722
-- Use `bitcoin::constants::COINBASE_MATURITY` #1727
-- Rename bdk_core::spk_client's SyncResult to SyncResponse #1732
-- Fix core checkpoint Drop stack overflow #1731
-- Change Utxo::Foreign::sequence type to not be optional #1681
-- Check time when persisting in `rusqlite` impl #1730
-- Bump hashbrown dependency version to v0.14.5 #1721
-- Add usage of debug_assert!() to LocalChain::apply_update #1734
-- Allow Sqlite to persist anchor without tx #1736
-- Change ChainPosition to represent transitive anchors and unconfirmed-without-last-seen values #1733
-- Updated electrum-client dependency to 0.22.0 #1751
-- Change TxBuilder to be Send safe and not implement the Clone trait #1737
-- Update esplora-client dependency to 0.11.0 #1746
-- Fix fetch_prev_txout to no longer queries coinbase transactions #1756
-- Remove serde json dependency from chain crate #1752
-- Introduce `O(n)` canonicalization algorithm #1670
-- Add chain O(n) canonicalization algorithm see: /crates/chain/src/canonical_iter.rs #1670
-- Add chain indexing fields in TxGraph; txs_by_anchor_height and txs_by_last_seen #1670
-- Removed chain TxGraph methods: try_get_chain_position and get_chain_position #1670
-- Change coin_selection and DescriptorExt::dust_value to use Amount type #1763
-
-## [v1.0.0-beta.5]
-
-### Summary
-
-This release changes bdk_wallet transaction creation to enable RBF by default, it also updates the bdk_esplora client to retry server requests that fail due to rate limiting. The bdk_electrum crate now also offers a use-openssl feature.
-
-### Changed
-
-- ci: automated update to rustc 1.81.0 by @create-pr-actions in https://github.com/bitcoindevkit/bdk/pull/1613
-- doc(wallet): Add docs to explain the lookahead by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1596
-- chore: fix RUSTSEC-2024-0370 proc-macro-error is unmaintained by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1603
-- fix(bdk_esplora, bdk_electrum): build and test with `--no-default-features` by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1615
-- fix!(wallet): `ChangeSet` should not be non-exhaustive by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1623
-- feat(wallet)!: enable RBF by default on TxBuilder by @luisschwab in https://github.com/bitcoindevkit/bdk/pull/1616
-- deps(esplora): bump esplora-client to 0.10.0 by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1626
-- feat(chain,core)!: move `Merge` to `bdk_core` by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1625
-- Replace trait `AnchorFromBlockPosition` with new struct by @jirijakes in https://github.com/bitcoindevkit/bdk/pull/1594
-- ci: fix build-test job with --no-default-features, add miniscript/no-std by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1636
-- feat(bdk_electrum): add `use-openssl` as a feature by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1620
-- Bump bdk_wallet version to 1.0.0-beta.5 by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1639
-
-## [v1.0.0-beta.4]
-
-### Summary
-
-BDK Wallet 1.0.0-beta.4 replaces 1.0.0-beta.3 and fixes a versioning mistake with two of our dependency crates. The bdk-wallet 1.0.0-beta.3 version and related dependency patch releases have been yanked from crates.io.
-
-### Changed
-
-See [v1.0.0-beta.3]
-
-## [v1.0.0-beta.3] **YANKED**
-
-RELEASE YANKED FROM CRATES.IO
-
-### Summary
-
-BDK Wallet 1.0.0-beta.3 is out! ðŸš€ Fixed transaction creation to not skip unused addresses, added function for sorting wallet transactions and option to change default BNB fallback coin selection. We moved the bdk_hwi crate functionality to the rust-hwi repo.
-
-NOTE: The `bdk_wallet` BETA releases are meant for early user testing to find bugs, get feedback on APIs and identify any missing functionality. A final `bdk_wallet` 1.0.0 release will be available once known bugs are fixed, and tutorial docs and language bindings projects are updated.
-
-### Changed
-- chore: add `print_stdout`/`print_stderr` lints to workspace level by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1425
-- ci: add token for cron-update-rust.yml by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1580
-- feat(core): add `TxUpdate::map_anchors` by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1587
-- ci: pin `tokio-util` dependency version to build with rust 1.63 by @LagginTimes in https://github.com/bitcoindevkit/bdk/pull/1590
-- feat(wallet): add transactions_sort_by function by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1477
-- docs: update CONTRIBUTING.md by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1584
-- fix(wallet): only mark change address used if `create_tx` succeeds by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1579
-- refactor(wallet): use `Amount` everywhere by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1595
-- Change methods of `IndexedTxGraph`/`TxGraph`/`Wallet` that insert txs to be more generic by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1586
-- fix: typos by @storopoli in https://github.com/bitcoindevkit/bdk/pull/1599
-- fix(wallet): do `check_wallet_descriptor` when creating and loading by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1597
-- refactor(bdk_hwi): remove `bdk_hwi`, as `HWISigner`'s being moved to `rust-hwi` by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1561
-- Allow custom fallback algorithm for bnb by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1581
-- fix(core): calling `CheckPoint::insert` with existing block must succeed by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1601
-- fix(wallet): fix SingleRandomDraw to error if insufficient funds by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1605
-- Bump bdk_wallet version to 1.0.0-beta.3 by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1608
-
-## [v1.0.0-beta.2]
-
-### Summary
-
-The primary user facing changes are re-enabling single descriptor wallets and renaming LoadParams methods to be more explict. Wallet persistence was also simplified and blockchain clients no longer depend on bdk_chain.
-
-### Changed
-
-- ci: pin tokio to 1.38.1 to support MSRV 1.63 by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1524
-- fix: typos by @storopoli in https://github.com/bitcoindevkit/bdk/pull/1529
-- chore: fix clippy lints by @storopoli in https://github.com/bitcoindevkit/bdk/pull/1530
-- bdk_wallet: Don't reimplement descriptor checksum, use the one from rust-miniscript by @darosior in https://github.com/bitcoindevkit/bdk/pull/1523
-- Remove crate-renaming by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1544
-- example: Update `example_cli` and retire old nursery crates by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1495
-- feat(testenv): Add method `new_with_config` by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1545
-- test(electrum): Test sync in reorg and no-reorg situations by @LagginTimes in https://github.com/bitcoindevkit/bdk/pull/1535
-- Add documentation for Electrum's full_scan/sync by @thunderbiscuit in https://github.com/bitcoindevkit/bdk/pull/1494
-- Enable selecting use-rustls-ring feature on electrum client by @thunderbiscuit in https://github.com/bitcoindevkit/bdk/pull/1491
-- [wallet] Enable support for single descriptor wallets by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1533
-- Allow opting out of getting `LocalChain` updates with `FullScanRequest`/`SyncRequest` structures by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1478
-- Use explicit names for wallet builder methods that validate rather than set by @thunderbiscuit in https://github.com/bitcoindevkit/bdk/pull/1537
-- fix(example_cli): add bitcoin and rand dependencies by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1549
-- Simplify wallet persistence by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1547
-- Derive `Clone` on `AddressInfo` by @praveenperera in https://github.com/bitcoindevkit/bdk/pull/1560
-- fix(wallet)!: make `LoadParams` implicitly satisfy `Send` by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1562
-- ci: add cron-update-rust.yml by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1564
-- Introduce `tx_graph::Update` and simplify `TxGraph` update logic by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1568
-- Introduce `bdk_core` by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1569
-- Bump bdk version to 1.0.0-beta.2 by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1572
-- chore: add missing bdk_core README.md and remove specific bdk_chain dev version by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1573
-
-## [v1.0.0-beta.1]
-
-### Summary
-
-This release includes the first beta version of `bdk_wallet` with a stable 1.0.0 API. The changes in this version include reworked wallet persistence, changeset, and construction, optional user provided RNG, custom tx sorting, and use of merkle proofs in bdk_electrum.
-
-### Changed
-
-- fix(wallet)!: Simplify `SignOptions` and improve finalization logic by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1476
-- feat(wallet): Allow user provided RNG, make rand an optional dependency by @rustaceanrob in https://github.com/bitcoindevkit/bdk/pull/1395
-- refactor(wallet): Use Psbt::sighash_ecdsa for computing sighashes by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1424
-- refactor(wallet)!: Use `Weight` type instead of `usize` by @oleonardolima in https://github.com/bitcoindevkit/bdk/pull/1468
-- refactor(wallet): Remove usage of `blockdata::` from bitcoin paths by @tcharding in https://github.com/bitcoindevkit/bdk/pull/1490
-- refactor(chain): calculate DescriptorId as the sha256 hash of spk at index 0 by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1486
-- refactor(chain)!: Change tx_last_seen to `Option<u64>` by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1416
-- refactor(wallet)!: Add support for custom sorting and deprecate BIP69 by @nymius in https://github.com/bitcoindevkit/bdk/pull/1487
-- refactor(chain)!: Create module `indexer` by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1493
-- chore(chain)!: Rename `Append` to `Merge` by @LagginTimes in https://github.com/bitcoindevkit/bdk/pull/1502
-- refactor(wallet)!: Simplify public_descriptor(), remove redundant function by @gnapoli23 in https://github.com/bitcoindevkit/bdk/pull/1503
-- ci: pin cc dependency version to build with rust 1.63 by @LagginTimes in https://github.com/bitcoindevkit/bdk/pull/1505
-- feat(electrum)!: Update `bdk_electrum` to use merkle proofs by @LagginTimes in https://github.com/bitcoindevkit/bdk/pull/1489
-- refactor(wallet)!: rework persistence, changeset, and construction  by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/1514
-- refactor(chain)!: Update KeychainTxOutIndex methods to use owned K and ScriptBuf by @rustaceanrob in https://github.com/bitcoindevkit/bdk/pull/1506
-- Bump bdk version to 1.0.0-beta.1 by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1522
-
-## [v1.0.0-alpha.13]
-
-### Summary
-
-This release includes major changes required to finalize the bdk_wallet 1.0.0 APIs, including: upgrading to rust-bitcoin 0.32 and rust-miniscript 0.12.0, constructing a Wallet now requires two descriptors (external and internal), the db field was removed from Wallet, staged changes can be persisted via a blocking or async data store.
-
-### Fixed
-
-*  Fix KeychainTxOutIndex range-based methods. #1463
-
-## [v1.0.0-alpha.12]
-
-**NOTE: The wallet API library up to tag `1.0.0-alpha.11` uses the crate name `bdk`. The `1.0.0-alpha.12` tag and later use `bdk_wallet` as the crate name.**
-
-### Summary
-
-This bi-weekly release fixes an electrum syncing bug when calculating fees and adds the bdk_sqlite crate for storing wallet data in a SQLite tables. The Wallet::allow_shrinking function was also remove because it was too easy to misuse. Also the `bdk` crate was renamed to `bdk_wallet`. See the changelog for all the details.
-
-### Fixed
-
-- Fixed `calculate_fee` result when syncing with electrum.  #1443
-
-### Changed
-
-- Removed `TxBuilder::allow_shrinking()` function. #1386
-- Renamed `bdk` crate to `bdk_wallet`. #1326
-- Update Wallet to use `CombinedChangeSet` for persistence. #1128
-
-### Added
-
-- Add `CombinedChangeSet` in bdk_persist crate. #1128
-- Add `bdk_sqlite` crate implementing SQLite based wallet data storage. #1128
-- Update `bdk_wallet` export feature to support taproot descriptors. #1393
-
-## [v1.0.0-alpha.11]
-
-### Summary
-
-This incremental bi-weekly release includes three big improvements. New electrum full_scan and sync APIs were added for more efficiently querying blockchain data. And the keychain::Changeset now includes public key descriptors and keychain::Balance uses bitcoin::Amount instead of u32 sats amounts. See the changelog for all the details.
-
-### Changed
-
-- Include the descriptor in keychain::Changeset #1203
-- Update bdk_electrum crate to use sync/full-scan structs #1403
-- Update keychain::Balance to use bitcoin::Amount #1411
-- Change `bdk_testenv` to re-export internally used crates. #1414
-- Updated documentation for `full_scan` and `sync` in `bdk_esplora`. #1427
-
-## [v1.0.0-alpha.10]
-
-### Summary
-
-This incremental bi-weekly release improves the address API, simplifies the Esplora API, and introduced new structures for spk-based scanning that will enable easier syncing with electrum/esplora. It also introduces a new bdk-persist crate, removes the generic T from the Wallet, and makes KeychainTxOutIndex more range based.
-
-### Fixed
-- Enable blocking-https-rustls feature on esplora client https://github.com/bitcoindevkit/bdk/pull/1408
-
-### Changed
-
-- KeychainTxOutIndex methods modified to take ranges of keychains instead. https://github.com/bitcoindevkit/bdk/pull/1324
-- Remove the generic from wallet https://github.com/bitcoindevkit/bdk/pull/1387
-  - Changed PersistenceBackend errors to depend on the anyhow crate
-  - Remove the generic T from Wallet
-- Improve address API https://github.com/bitcoindevkit/bdk/pull/1402
-  - Added Wallet methods:
-    - peek_address
-    - reveal_next_address
-    - next_unused_address
-    - reveal_addresses_to
-    - list_unused_addresses
-    - mark_used
-    - unmark_used
-  - Removed Wallet methods:
-    - get_address
-    - get_internal_address
-    - try_get_address
-    - try_get_internal_address
-- Simplified EsploraExt API https://github.com/bitcoindevkit/bdk/pull/1380
-  - Changed EsploraExt API so that sync only requires one round of fetching data. The local_chain_update method is removed and the local_tip parameter is added to the full_scan and sync methods.
-  - Removed TxGraph::missing_heights and tx_graph::ChangeSet::missing_heights_from methods.
-  - Introduced CheckPoint::insert which allows convenient checkpoint-insertion. This is intended for use by chain-sources when crafting an update.
-  - Refactored merge_chains to also return the resultant CheckPoint tip.
-  - Optimized the update LocalChain logic - use the update CheckPoint as the new CheckPoint tip when possible.
-- Introduce `bdk-persist` crate https://github.com/bitcoindevkit/bdk/pull/1412
-- Introduce universal sync/full-scan structures for spk-based syncing #1413
-  - Add universal structures for initiating/receiving sync/full-scan requests/results for spk-based syncing.
-  - Updated bdk_esplora chain-source to make use of new universal sync/full-scan structures.
-
-## [v1.0.0-alpha.9]
-
-### Summary
-
-This regular bi-weekly alpha release updates dependencies rust-bitcoin to v0.31.0 and rust-miniscript to v11.0.0 plus replaces the deprecated rust-miniscript function max_satisfaction_weight with max_weight_to_satisfy. It also adds chain module improvements needed to simplify syncing with electrum and esplora blockchain clients.
-
-### Fixed
-
-* Replace the deprecated max_satisfaction_weight from rust-miniscript to max_weight_to_satisfy. #1345
-
-### Changed
-
-* Update dependencies: rust-bitcoin to v0.31.0 and rust-miniscript to v11.0.0. #1177
-* Changed TxGraph to store transactions as Arc<Transaction>. This allows chain-sources to cheaply keep a copy of already-fetched transactions.  #1373
-* Add get and range methods to CheckPoint #1369
-  * Added get and range methods to CheckPoint (and in turn, LocalChain). This simulates an API where we have implemented a skip list of checkpoints (to implement in the future). This is a better API because we can query for any height or height range with just a checkpoint tip instead of relying on a separate checkpoint index (which needs to live in LocalChain).
-  * Changed LocalChain to have a faster Eq implementation. We now maintain an xor value of all checkpoint block hashes. We compare this xor value to determine whether two chains are equal.
-  * Added PartialEq implementation for CheckPoint and local_chain::Update.
-* Methods into_tx_graph and into_confirmation_time_tx_graph for RelevantTxids are changed to no longer accept a seen_at parameter. #1385
-  * Added method update_last_seen_unconfirmed for TxGraph.
-* Added proptest for CheckPoint::range. #1397
-
-## [v1.0.0-alpha.8]
-
-### Summary
-
-This incremental bi-weekly release migrates API to use the rust-bitcoin FeeRate type, fixes PSBT finalization to remove extra taproot fields, and fixes blockchain scanning stop_gap definition and documentation. We recommend all 1.0.0-alpha users upgrade to this release.
-
-### Fixed
-
-- Remove extra taproot fields when finalizing PSBT #1310
-- Define and document stop_gap #1351
-
-### Changed
-
-- Migrate to bitcoin::FeeRate #1216
-- chore: extract TestEnv into separate crate #1171
-
-## [v1.0.0-alpha.7]
-
-### Summary
-
-This incremental bi-weekly release includes an API change to relax the generic requirements on the wallet transaction builder and a small fix when manually looking ahead to unrevealed scripts. Unless you need one of these changes there's no need to upgrade to this release.
-
-### Fixed
-
-- Fix `KeychainTxOutIndex::lookahead_to_target` to look ahead to correct index.  #1349
-
-### Changed
-
-- Relax the generic requirements on `TxBuilder`. #1312
-
-## [v1.0.0-alpha.6]
-
-### Summary
-
-This small bi-weekly release fixes TxBuilder to support setting explicit nSequence for foreign inputs and a bug in tx_graph::ChangeSet::is_empty where is returns true even when it wasn't empty ðŸ™ˆ.  We also added a new option for Esplora APIs to include floating TxOuts in pudates for fee calculations.
-
-### Fixed
-
-- TxBuilder now supports setting explicit nSequence for foreign inputs. #1316
-- Fix bug in tx_graph::ChangeSet::is_empty where is returns true even when it wasn't empty. #1335
-
-### Added
-
-- New Option for Esplora APIs to include floating TxOuts in updates for fee calculation. #1308
-
-## [v1.0.0-alpha.5]
-
-### Summary
-
-This release introduces a block-by-block API to bdk::Wallet and adds a RPC wallet example, improves performance of bdk_file_store::EntryIter, and simplifies Esplora::update_local_chain with additional tests. See release notes for all the details.
-
-### Fixed
-
-- `InsertTxError` now implements `std::error::Error`. #1172
-- Simplified `EsploraExt::update_local_chain` logic. #1267
-
-### Changed
-
-- `EntryIter` performance is improved by reducing syscalls. #1270
-- Changed to implement `ElectrumExt` for all that implements `ElectrumApi`. #1306
-
-### Added
-
-- `Wallet` methods to apply full blocks (`apply_block` and `apply_block_connected_to`) and a method to apply a batch of unconfirmed transactions (`apply_unconfirmed_txs`). #1172
-- `CheckPoint::from_block_ids` convenience method. #1172
-- `LocalChain` methods to apply a block header (`apply_header` and `apply_header_connected_to`). #1172
-- Test to show that `LocalChain` can apply updates that are shorter than original. This will happen during reorgs if we sync wallet with `bdk_bitcoind_rpc::Emitter`. #1172
-
-## [v1.0.0-alpha.4]
-
-### Summary
-
-This release improves the `KeychainTxOutIndex` API and contains a few bug fixes and performance improvements.
-
-### Fixed
-
-- Avoid using `BTreeMap::append` due to performance issues (refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420). #1274
-
-### Changed
-
-- The old `hardwaresigner` module has been moved out of `bdk` and inside a new `bdk_hwi` crate. #1161
-- Wallet's peek-address logic is optimized by making use of `<SpkIterator as Iterator>::nth`. #1269
-- `KeychainTxOutIndex` API is refactored to better differentiate between methods that return unbounded vs stored spks. #1269
-- `KeychainTxOutIndex` no longer directly exposes `SpkTxOutIndex` methods via `DeRef`. This was problematic because `SpkTxOutIndex` also contains lookahead spks which we want to hide. #1269
-
-### Added
-
-- LocalChain::disconnect_from method to evict a chain of blocks starting from a given BlockId. #1276
-- `SpkIterator::descriptor` method which gets a reference to the internal descriptor. #1269
-
-## [v1.0.0-alpha.3]
-
-### Summary
-
-This release changes LocalChain to have a hard-wired genesis block, adds context specific Wallet TxBuilder errors, and bumps the projects MSRV to 1.63. It also includes other API and docs improvements and bug fixes, see the changelog for all the details.
-
-### Fixed
-
-- Further improve unconfirmed tx conflict resolution. #1109
-- Stuck Electrum chain sync issue. #1145
-- Bug related to taproot signing with internal keys. We would previously sign with the first private key we had, without checking if it was the correct internal key or not. #1200
-- Coinbase transactions cannot exist in the mempool and be unconfirmed. TxGraph::try_get_chain_position should always return None for coinbase transactions not anchored in best chain. #1202
-- Esplora incorrect gap limit check in blocking client. #1225
-- Loading a wallet from persistence now restores keychain indices. #1246
-
-### Changed
-
-- Rename ConfirmationTimeAnchor to ConfirmationTimeHeightAnchor. #1206
-- New LocalChain now have a hardwired genesis block: #1178
-  - Changed ChainOracle::get_chain_tip method to return a BlockId instead of an Option of a BlockId.
-  - Refactored LocalChain so that the genesis BlockId is hardwired. This way, the ChainOracle::get_chain_tip implementation can always return a tip.
-  - Add is_empty method to PersistBackend. This returns true when there is no data in the persistence.
-  - Changed Wallet::new to initialize a fresh wallet only.
-  - Added Wallet::load to restore an instance of a wallet.
-  - Replaced Store::new with separate methods to create/open the database file.
-- Updated the bdk module to use new context specific error types: #1028
-  - wallet: MiniscriptPsbtError, CreateTxError, BuildFeeBumpError error enums.
-  - coin_selection: module Error enum.
-- Renamed fallible Wallet address functions to try_get_address() and try_get_internal_address(). #1028
-- Rename LocalUtxo to LocalOutput. #1190
-- MSRV is now 1.63.0 for bdk, chain, and bitcoind_rpc crates. #1183
-- Use a universal lookahead value for KeychainTxOutIndex and have a reasonable default. #1229
-- Return NonEmptyDatabase error when constructing a wallet with Wallet::new if the file already contains data (in which case, the caller should use load or new_or_load). #1256
-- In electrum_ext rename functions scan_without_keychain to sync and scan to full_scan. #1235
-- In esplora_ext rename functions scan_txs to sync and scan_txs_with_keychains to full_scan. #1235
-- Increase rust-bip39 dependency version to 2.0 #1259
-
-### Removed
-
-- Removed catch-all top level bdk::Error enum. #1028
-- Removed impl_error macro. #1028
-
-### Added
-
-- Add infallible Wallet get_address and get_internal_address functions. #1028
-- Add Wallet::list_output method. #1190
-- New async-https-rustls feature flag for the bdk_esplora crate, allowing to compile rust-esplora-client using rustls-tls instead of the default native-tls. #1179
-
-## [v1.0.0-alpha.2]
-
-### Summary
-
-Notable changes include a new bitcoind RPC based blockchain client module for quick syncing to bitcoind,  a new linked-list LocalChain, and an upgrade to rust-bitcoin 0.30.
-
-### Fixed
-
-- wallet_esplora: missing_heights uses the graph update #1152
-- bump electrum version to 0.18 #1132
-- Correct the coin type in the derivation path for wallet examples #1089
-
-### Added
-
-- Add bitcoind_rpc chain-source module. #1041
-- Add example_bitcoind_rpc example module. #1041
-- Add AnchorFromBlockPosition trait which are for anchors that can be constructed from a given block, height and position in block. #1041
-- Add helper methods to IndexedTxGraph and TxGraph for batch operations and applying blocks directly. #1041
-- Add helper methods to CheckPoint for easier construction from a block Header. #1041
-- Add cli-example for esplora. #1040
-- Introduced tx_template module. #1064
-- Introduced TxGraph::TxAncestors iterator. #1064
-- Added walk_ancestors to TxGraph. #1064
-- Implement Anchor for BlockId. #1069
-
-### Changed
-
-- Move WalletUpdate to the wallet module. #1084
-- Remove ForEachTxout trait completely. #1084
-- Refactor ElectrumExt to not use WalletUpdate. #1084
-- Rename indexed_tx_graph::IndexedAdditions to indexed_tx_graph::ChangeSet. #1065
-- Rename indexed_tx_graph::IndexedAdditions::graph_additions to indexed_tx_graph::ChangeSet::graph. #1065
-- Rename indexed_tx_graph::IndexedAdditions::index_additions to indexed_tx_graph::ChangeSet::indexer. #1065
-- Rename tx_graph::Additions to tx_graph::ChangeSet. #1065
-- Rename keychain::DerivationAdditions to keychain::ChangeSet. #1065
-- Rename CanonicalTx::node to CanonicalTx::tx_node. #1065
-- Rename CanonicalTx::observed_as to CanonicalTx::chain_position. #1065
-- Rename LocalChangeSet to WalletChangeSet. #1065
-- Rename LocalChangeSet::chain_changeset to WalletChangeSet::chain. #1065
-- Rename LocalChangeSet::indexed_additions to WalletChangeSet::indexed_tx_graph. #1065
-- Rename LocalUpdate to WalletUpdate. #1065
-- Make TxGraph::determine_changeset pub(crate). #1065
-- Add TxGraph::initial_changeset. #1065
-- Add IndexedTxGraph::initial_changeset. #1065
-- Remove TxGraph::insert_txout_preview. #1065
-- Remove TxGraph::insert_tx_preview. #1065
-- Remove insert_anchor_preview. #1065
-- Remove insert_seen_at_preview. #1065
-- Refactored TxGraph::walk_conflicts to use TxGraph::TxAncestors. #1064
-- Update to rust-bitcoin 0.30.0 and miniscript 10.0.0. #1023
-- Use apply_update instead of determine_changeset + apply_changeset around the code. #1092
-- Rename TxGraph::direct_conflicts_of_tx to TxGraph::direct_conflicts. #1164
-- Rename methods of esplora ext. #1070
-
-## [v1.0.0-alpha.1]
-
-### Summary
-
-The BDK 1.0.0-alpha release should be used for **experimentation only**, APIs are still unstable and the code is not fully tested. This alpha.1 release introduces the new `ChainOracle` struct for more efficient chain syncing. A new `std` default feature was added for `bdk`, `bdk_chain` and `bdk_esplora` crates; when disabled these crates can be used in `no-std` projects. BDK 1.0.0-alpha.x docs are now published to docs.rs.
-
-### Fixed
-
-- Fixed a bug in the policy condition calculation. #932
-- Pin base64 to 0.21.0 #990
-- Fix docsrs publishing for bdk crate. #1011
-
-### Changed
-
-- Refactor `bdk_chain` to use new `ChainOracle` structure. #926 #963 #965 #975 #976
-- Better no std support. #894
-  - Set `default-features = false` for `rust-bitcoin` and `rust-miniscript`.
-  - Introduce `std` default feature for `bdk`, `bdk_chain` and `bdk_esplora`.
-
-### Added
-
-- Add a simple conversion tool for going to kilo weight units. #953
-- Add Custom spk iterator. #927
-- Add taproot descriptor template (BIP-86). #840
-
-## [v0.30.0]
-
-### Summary
-
-This release bumps the MSRV to 1.63.0 and updates the `rusqlite` dependency version to 0.31 to be aligned with the upcoming 1.0.0 release. These changes will prepare projects for migrating wallet data to the 1.0.0 version of BDK, see #1648.
-
-### Changed
-
-- ci (maintenance): Pin byteorder, webpki to keep the MSRV by @danielabrozzoni in https://github.com/bitcoindevkit/bdk/pull/1160
-- Bump MSRV 1.63.0 AND add deprecate TxBuilder::allow_shrinking() by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1366
-- (maintenance) doc(bdk): Clarify the absolute_fee docs by @danielabrozzoni in https://github.com/bitcoindevkit/bdk/pull/1159
-- release/0.29, deps: Update `rusqlite` to 0.31 by @ValuedMammal in https://github.com/bitcoindevkit/bdk/pull/1651
-- test: update electrsd to fix RUSTSEC-2024-0384 warning by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/1744
-
-## [v0.29.0]
-
-### Summary
-
-This maintenance release updates our `rust-bitcoin` dependency to 0.30.x and fixes a wallet balance bug when a wallet has more than one coinbase transaction.
-
-### Changed
-
-- Update rust-bitcoin to 0.30 #1071
-
-### Fixed
-
-- Fix a bug when syncing coinbase utxos on electrum #1090
-
-## [v0.28.2]
-
-### Summary
-
-Reverts the 0.28.1 esplora-client version update from 0.5.0 back to 0.4.0.
-
-### Changed
-
-- Reverts the 0.28.1 esplora-client version update from 0.5.0 back to 0.4.0.
-
-## [v0.28.1]
-
-### Summary
-
-This patch release backports (from the BDK 1.0 dev branch) a fix for a bug in the policy condition calculation and adds a new taproot single key descriptor template (BIP-86). The policy condition calculation bug can cause issues when a policy subtree fails due to missing info even if it's not selected when creating a new transaction, errors on unused policy paths are now ignored.
-
-### Fixed
-
-- Backported #932 fix for policy condition calculation #1008
-
-### Added
-
--  Backported #840 taproot descriptor template (BIP-86) #1033
-
-## [v0.28.0]
-
-### Summary
-
-This is a maintenance release and includes changes from yanked version 0.27.2 including to disable default-features for rust-bitcoin and rust-miniscript dependencies, and for rust-esplora-client optional dependency. New default std feature must now be enabled unless building for wasm.
-
-### Changed
-
-- Bump bip39 crate to v2.0.0  #875
-- Set default-features = false for rust-bitcoin and rust-miniscript #882
-- Update esplora client dependency to version 0.4 #884
-- Added new std feature as part of default features #930
-
-## [v0.27.2] **YANKED**
-
-RELEASE YANKED FROM CRATES.IO
-
-See: [#897](https://github.com/bitcoindevkit/bdk/pull/897)
-
-## [v0.27.1]
-
-### Summary
-
-Fixes [RUSTSEC-2022-0090], this issue is only applicable if you are using the optional sqlite database feature.
-
-[RUSTSEC-2022-0090]: https://rustsec.org/advisories/RUSTSEC-2022-0090
-
-### Changed
-
-- Update optional sqlite dependency from 0.27.0 to 0.28.0. #867
-
-## [v0.27.0]
-
-### Summary
-
-A maintenance release with a bump in project MSRV to 1.57.0, updated dependence and a few developer oriented improvements. Improvements include  better error formatting, don't default to async/await for wasm32 and adding derived PartialEq and Eq on SyncTime.
-
-### Changed
-
-- Improve display error formatting #814
-- Don't default to use async/await on wasm32 #831
-- Project MSRV changed from 1.56.1 to 1.57.0 #842
-- Update rust-miniscript dependency to latest bug fix release 9.0 #844
-
-### Added
-
-- Derive PartialEq, Eq on SyncTime #837
-
-## [v0.26.0]
-
-### Summary
-
-This release improves Fulcrum electrum server compatibility and fixes public descriptor template key origin paths. We also snuck in small enhancements to configure the electrum client to validate the domain using SSL and sort TransactionDetails by block height and timestamp.
-  
-### Fixed
-  
-- Make electrum blockchain client `save_tx` function order independent to work with Fulcrum servers. #808
-- Fix wrong testnet key origin path in public descriptor templates. #818
-- Make README.md code examples compile without errors. #820
-
-### Changed
-
-- Bump `hwi` dependency to `0.4.0`. #825
-- Bump `esplora-client` dependency to `0.3` #830
-
-### Added
-  
-- For electrum blockchain client, allow user to configure whether to validate the domain using SSL. #805
-- Implement ordering for `TransactionDetails`. #812
-
-## [v0.25.0]
-
-### Summary
-
-This release fixes slow sync time and big script_pubkeys table with SQLite, the wallet rescan height for the FullyNodedExport and setting the network for keys in the KeyMap when using descriptor templates. Also added are new blockchain and mnemonic examples.
-  
-### Fixed
-  
-- Slow sync time and big script_pubkeys table with SQLite.
-- Wallet rescan height for the FullyNodedExport.
-- Setting the network for keys in the KeyMap when using descriptor templates.
-  
-### Added
-  
-- Examples for connecting to Esplora, Electrum Server, Neutrino and Bitcoin Core.
-- Example for using a mnemonic in a descriptors.
-
-## [v0.24.0]
-
-### Summary
-
-This release contains important dependency updates for `rust-bitcoin` to `0.29` and `rust-miniscript` to `8.0`, plus related crates that also depend on the latest version of `rust-bitcoin`. The release also includes a breaking change to the BDK signer which now produces low-R signatures by default, saving one byte. A bug was found in the `get_checksum` and `get_checksum_bytes` functions, which are now deprecated in favor of fixed versions called `calc_checksum` and `calc_checksum_bytes`. And finally a new `hardware-signer` features was added that re-exports the `hwi` crate, along with a new `hardware_signers.rs` example file.
-   
-### Changed
-
-- Updated dependency versions for `rust-bitcoin` to `0.29` and `rust-miniscript` to `8.0`, plus all related crates. @afilini #770
-- BDK Signer now produces low-R signatures by default, saving one byte. If you want to preserve the original behavior, set allow_grinding in the SignOptions to false. @vladimirfomene #779
-- Deprecated `get_checksum`and `get_checksum_bytes` due to bug where they calculates the checksum of a descriptor that already has a checksum.  Use `calc_checksum` and `calc_checksum_bytes` instead. @evanlinjin #765
-- Remove deprecated "address validators". @afilini #770
-  
-### Added
-
-- New `calc_checksum` and `calc_checksum_bytes`, replace deprecated `get_checksum` and `get_checksum_bytes`. @evanlinjin #765
-- Re-export the hwi crate when the feature hardware-signer is on.  @danielabrozzoni #758
-- New examples/hardware_signer.rs. @danielabrozzoni #758
-- Make psbt module public to expose PsbtUtils trait to downstream projects. @notmandatory #782
-
-## [v0.23.0]
-
-### Summary
-
-This release brings new utilities functions on PSBTs like `fee_amount()` and `fee_rate()` and migrates BDK to use our new external esplora client library.
-As always many bug fixes, docs and tests improvement are also included.
-
-### Changed
-
-- Update electrum-client to 0.11.0 by @afilini in https://github.com/bitcoindevkit/bdk/pull/737
-- Change configs for source-base code coverage by @wszdexdrf in https://github.com/bitcoindevkit/bdk/pull/708
-- Improve docs regarding PSBT finalization by @tnull in https://github.com/bitcoindevkit/bdk/pull/753
-- Update compiler example to a Policy example by @rajarshimaitra in https://github.com/bitcoindevkit/bdk/pull/730
-- Fix the release process by @afilini in https://github.com/bitcoindevkit/bdk/pull/754
-- Remove redundant duplicated keys check by @afilini in https://github.com/bitcoindevkit/bdk/pull/761
-- Remove genesis_block lazy initialization by @shobitb in https://github.com/bitcoindevkit/bdk/pull/756
-- Fix `Wallet::descriptor_checksum` to actually return the checksum by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/763
-- Use the esplora client crate by @afilini in https://github.com/bitcoindevkit/bdk/pull/764
-
-### Added
-
-- Run code coverage on every PR by @danielabrozzoni in https://github.com/bitcoindevkit/bdk/pull/747
-- Add psbt_signer.rs example by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/744
-- Add fee_amount() and fee_rate() functions to PsbtUtils trait by @notmandatory in https://github.com/bitcoindevkit/bdk/pull/728
-- Add tests to improve coverage by @vladimirfomene in https://github.com/bitcoindevkit/bdk/pull/745
-- Enable signing taproot transactions with only `non_witness_utxos` by @afilini in https://github.com/bitcoindevkit/bdk/pull/757
-- Add datatype for is_spent sqlite column by @vladimirfomene in https://github.com/bitcoindevkit/bdk/pull/713
-- Add vscode filter to gitignore by @evanlinjin in https://github.com/bitcoindevkit/bdk/pull/762
-
-## [v0.22.0]
-
-### Summary
-
-This release brings support for hardware signers on desktop through the HWI library.
-It also includes fixes and improvements which are part of our ongoing effort of integrating
-BDK and LDK together.
-
-### Changed
-
-- FeeRate function name as_sat_vb to as_sat_per_vb. #678
-- Verify signatures after signing. #718
-- Dependency electrum-client to 0.11.0. #737
-
-### Added
-  
-- Functions to create FeeRate from sats/kvbytes and sats/kwu. #678
-- Custom hardware wallet signer HwiSigner in wallet::hardwaresigner module. #682
-- Function allow_dust on TxBuilder. #689
-- Implementation of Deref<Target=UrlClient> for EsploraBlockchain. #722
-- Implementation of Deref<Target=Client> for ElectrumBlockchain #705
-- Implementation of Deref<Target=Client> for RpcBlockchain. #731
-
-## [v0.21.0]
-
-- Add `descriptor::checksum::get_checksum_bytes` method.
-- Add `Excess` enum to handle remaining amount after coin selection.
-- Move change creation from `Wallet::create_tx` to `CoinSelectionAlgorithm::coin_select`.
-- Change the interface of `SqliteDatabase::new` to accept any type that implement AsRef<Path>
-- Add the ability to specify which leaves to sign in a taproot transaction through `TapLeavesOptions` in `SignOptions`
-- Add the ability to specify whether a taproot transaction should be signed using the internal key or not, using `sign_with_tap_internal_key` in `SignOptions`
-- Consolidate params `fee_amount` and `amount_needed` in `target_amount` in `CoinSelectionAlgorithm::coin_select` signature.
-- Change the meaning of the `fee_amount` field inside `CoinSelectionResult`: from now on the `fee_amount` will represent only the fees associated with the utxos in the `selected` field of `CoinSelectionResult`.
-- New `RpcBlockchain` implementation with various fixes.
-- Return balance in separate categories, namely `confirmed`, `trusted_pending`, `untrusted_pending` & `immature`.
-
-## [v0.20.0]
-
-- New MSRV set to `1.56.1`
-- Fee sniping discouraging through nLockTime - if the user specifies a `current_height`, we use that as a nlocktime, otherwise we use the last sync height (or 0 if we never synced)
-- Fix hang when `ElectrumBlockchainConfig::stop_gap` is zero.
-- Set coin type in BIP44, BIP49, and BIP84 templates
-- Get block hash given a block height - A `get_block_hash` method is now defined on the `GetBlockHash` trait and implemented on every blockchain backend. This method expects a block height and returns the corresponding block hash.
-- Add `remove_partial_sigs` and `try_finalize` to `SignOptions`
-- Deprecate `AddressValidator`
-- Fix Electrum wallet sync potentially causing address index decrement - compare proposed index and current index before applying batch operations during sync.
-
-## [v0.19.0]
-
-- added `OldestFirstCoinSelection` impl to `CoinSelectionAlgorithm`
-- New MSRV set to `1.56`
-- Unpinned tokio to `1`
-- Add traits to reuse `Blockchain`s across multiple wallets (`BlockchainFactory` and `StatelessBlockchain`).
-- Upgrade to rust-bitcoin `0.28`
-- If using the `sqlite-db` feature all cached wallet data is deleted due to a possible UTXO inconsistency, a wallet.sync will recreate it
-- Update `PkOrF` in the policy module to become an enum
-- Add experimental support for Taproot, including:
-  - Support for `tr()` descriptors with complex tapscript trees
-  - Creation of Taproot PSBTs (BIP-371)
-  - Signing Taproot PSBTs (key spend and script spend)
-  - Support for `tr()` descriptors in the `descriptor!()` macro
-- Add support for Bitcoin Core 23.0 when using the `rpc` blockchain
-
-## [v0.18.0]
-
-- Add `sqlite-bundled` feature for deployments that need a bundled version of sqlite, i.e. for mobile platforms.
-- Added `Wallet::get_signers()`, `Wallet::descriptor_checksum()` and `Wallet::get_address_validators()`, exposed the `AsDerived` trait.
-- Deprecate `database::Database::flush()`, the function is only needed for the sled database on mobile, instead for mobile use the sqlite database.
-- Add `keychain: KeychainKind` to `wallet::AddressInfo`.
-- Improve key generation traits
-- Rename `WalletExport` to `FullyNodedExport`, deprecate the former.
-- Bump `miniscript` dependency version to `^6.1`.
-
-## [v0.17.0]
-
-- Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
-- `verify` flag removed from `TransactionDetails`.
-- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
-- added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database
-- Add `is_spent` field to `LocalUtxo`; when we notice that a utxo has been spent we set `is_spent` field to true instead of deleting it from the db.
-
-### Sync API change
-
-To decouple the `Wallet` from the `Blockchain` we've made major changes:
-
-- Removed `Blockchain` from Wallet.
-- Removed `Wallet::broadcast` (just use `Blockchain::broadcast`)
-- Deprecated `Wallet::new_offline` (all wallets are offline now)
-- Changed `Wallet::sync` to take a `Blockchain`.
-- Stop making a request for the block height when calling `Wallet:new`.
-- Added `SyncOptions` to capture extra (future) arguments to `Wallet::sync`.
-- Removed `max_addresses` sync parameter which determined how many addresses to cache before syncing since this can just be done with `ensure_addresses_cached`.
-- remove `flush` method from the `Database` trait.
-
-## [v0.16.1]
-
-- Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
-
-## [v0.16.0]
-
-- Disable `reqwest` default features.
-- Added `reqwest-default-tls` feature: Use this to restore the TLS defaults of reqwest if you don't want to add a dependency to it in your own manifest.
-- Use dust_value from rust-bitcoin
-- Fixed generating WIF in the correct network format.
-
-## [v0.15.0]
-
-- Overhauled sync logic for electrum and esplora.
-- Unify ureq and reqwest esplora backends to have the same configuration parameters. This means reqwest now has a timeout parameter and ureq has a concurrency parameter.
-- Fixed esplora fee estimation.
-
-## [v0.14.0]
-
-- BIP39 implementation dependency, in `keys::bip39` changed from tiny-bip39 to rust-bip39.
-- Add new method on the `TxBuilder` to embed data in the transaction via `OP_RETURN`. To allow that a fix to check the dust only on spendable output has been introduced.
-- Update the `Database` trait to store the last sync timestamp and block height
-- Rename `ConfirmationTime` to `BlockTime`
-
-## [v0.13.0]
-
-- Exposed `get_tx()` method from `Database` to `Wallet`.
-
-## [v0.12.0]
-
-- Activate `miniscript/use-serde` feature to allow consumers of the library to access it via the re-exported `miniscript` crate.
-- Add support for proxies in `EsploraBlockchain`
-- Added `SqliteDatabase` that implements `Database` backed by a sqlite database using `rusqlite` crate.
-
-## [v0.11.0]
-
-- Added `flush` method to the `Database` trait to explicitly flush to disk latest changes on the db.
-
-## [v0.10.0]
-
-- Added `RpcBlockchain` in the `AnyBlockchain` struct to allow using Rpc backend where `AnyBlockchain` is used (eg `bdk-cli`)
-- Removed hard dependency on `tokio`.
-
-### Wallet
-
-- Removed and replaced `set_single_recipient` with more general `drain_to` and replaced `maintain_single_recipient` with `allow_shrinking`.
-
-### Blockchain
-
-- Removed `stop_gap` from `Blockchain` trait and added it to only `ElectrumBlockchain` and `EsploraBlockchain` structs.
-- Added a `ureq` backend for use when not using feature `async-interface` or target WASM. `ureq` is a blocking HTTP client.
-
-## [v0.9.0]
-
-### Wallet
-
-- Added Bitcoin core RPC added as blockchain backend
-- Added a `verify` feature that can be enable to verify the unconfirmed txs we download against the consensus rules
-
-## [v0.8.0]
-
-### Wallet
-- Added an option that must be explicitly enabled to allow signing using non-`SIGHASH_ALL` sighashes (#350)
-#### Changed
-`get_address` now returns an `AddressInfo` struct that includes the index and derefs to `Address`.
-
-## [v0.7.0]
-
-### Policy
-#### Changed
-Removed `fill_satisfaction` method in favor of enum parameter in `extract_policy` method
-
-#### Added
-Timelocks are considered (optionally) in building the `satisfaction` field
-
-### Wallet
-
-- Changed `Wallet::{sign, finalize_psbt}` now take a `&mut psbt` rather than consuming it.
-- Require and validate `non_witness_utxo` for SegWit signatures by default, can be adjusted with `SignOptions`
-- Replace the opt-in builder option `force_non_witness_utxo` with the opposite `only_witness_utxo`. From now on we will provide the `non_witness_utxo`, unless explicitly asked not to.
-
-## [v0.6.0]
-
-### Misc
-#### Changed
-- New minimum supported rust version is 1.46.0
-- Changed `AnyBlockchainConfig` to use serde tagged representation.
-
-### Descriptor
-#### Added
-- Added ability to analyze a `PSBT` to check which and how many signatures are already available
-
-### Wallet
-#### Changed
-- `get_new_address()` refactored to `get_address(AddressIndex::New)` to support different `get_address()` index selection strategies
-
-#### Added
-- Added `get_address(AddressIndex::LastUnused)` which returns the last derived address if it has not been used or if used in a received transaction returns a new address
-- Added `get_address(AddressIndex::Peek(u32))` which returns a derived address for a specified descriptor index but does not change the current index
-- Added `get_address(AddressIndex::Reset(u32))` which returns a derived address for a specified descriptor index and resets current index to the given value
-- Added `get_psbt_input` to create the corresponding psbt input for a local utxo.
-
-#### Fixed
-- Fixed `coin_select` calculation for UTXOs where `value < fee` that caused over-/underflow errors.
-
-## [v0.5.1]
-
-### Misc
-#### Changed
-- Pin `hyper` to `=0.14.4` to make it compile on Rust 1.45
-
-## [v0.5.0]
-
-### Misc
-#### Changed
-- Updated `electrum-client` to version `0.7`
-
-### Wallet
-#### Changed
-- `FeeRate` constructors `from_sat_per_vb` and `default_min_relay_fee` are now `const` functions
-
-## [v0.4.0]
-
-### Keys
-#### Changed
-- Renamed `DerivableKey::add_metadata()` to `DerivableKey::into_descriptor_key()`
-- Renamed `ToDescriptorKey::to_descriptor_key()` to `IntoDescriptorKey::into_descriptor_key()`
-#### Added
-- Added an `ExtendedKey` type that is an enum of `bip32::ExtendedPubKey` and `bip32::ExtendedPrivKey`
-- Added `DerivableKey::into_extended_key()` as the only method that needs to be implemented
-
-### Misc
-#### Removed
-- Removed the `parse_descriptor` example, since it wasn't demonstrating any bdk-specific API anymore.
-#### Changed
-- Updated `bitcoin` to `0.26`, `miniscript` to `5.1` and `electrum-client` to `0.6`
-#### Added
-- Added support for the `signet` network (issue #62)
-- Added a function to get the version of BDK at runtime
-
-### Wallet
-#### Changed
-- Removed the explicit `id` argument from `Wallet::add_signer()` since that's now part of `Signer` itself
-- Renamed `ToWalletDescriptor::to_wallet_descriptor()` to `IntoWalletDescriptor::into_wallet_descriptor()`
-
-### Policy
-#### Changed
-- Removed unneeded `Result<(), PolicyError>` return type for `Satisfaction::finalize()`
-- Removed the `TooManyItemsSelected` policy error (see commit message for more details)
-
-## [v0.3.0]
-
-### Descriptor
-#### Changed
-- Added an alias `DescriptorError` for `descriptor::error::Error`
-- Changed the error returned by `descriptor!()` and `fragment!()` to `DescriptorError`
-- Changed the error type in `ToWalletDescriptor` to `DescriptorError`
-- Improved checks on descriptors built using the macros
-
-### Blockchain
-#### Changed
-- Remove `BlockchainMarker`, `OfflineClient` and `OfflineWallet` in favor of just using the unit
-  type to mark for a missing client.
-- Upgrade `tokio` to `1.0`.
-
-### Transaction Creation Overhaul
-
-The `TxBuilder` is now created from the `build_tx` or `build_fee_bump` functions on wallet and the
-final transaction is created by calling `finish` on the builder.
-
-- Removed `TxBuilder::utxos` in favor of `TxBuilder::add_utxos`
-- Added `Wallet::build_tx` to replace `Wallet::create_tx`
-- Added `Wallet::build_fee_bump` to replace `Wallet::bump_fee`
-- Added `Wallet::get_utxo`
-- Added `Wallet::get_descriptor_for_keychain`
-
-### `add_foreign_utxo`
-
-- Renamed `UTXO` to `LocalUtxo`
-- Added `WeightedUtxo` to replace floating `(UTXO, usize)`.
-- Added `Utxo` enum to incorporate both local utxos and foreign utxos
-- Added `TxBuilder::add_foreign_utxo` which allows adding a utxo external to the wallet.
-
-### CLI
-#### Changed
-- Remove `cli.rs` module, `cli-utils` feature and `repl.rs` example; moved to new [`bdk-cli`](https://github.com/bitcoindevkit/bdk-cli) repository
-
-## [v0.2.0]
-
-### Project
-#### Added
-- Add CONTRIBUTING.md
-- Add a Discord badge to the README
-- Add code coverage github actions workflow
-- Add scheduled audit check in CI
-- Add CHANGELOG.md
-
-#### Changed
-- Rename the library to `bdk`
-- Rename `ScriptType` to `KeychainKind`
-- Prettify README examples on github
-- Change CI to github actions
-- Bump rust-bitcoin to 0.25, fix Cargo dependencies
-- Enable clippy for stable and tests by default
-- Switch to "mainline" rust-miniscript
-- Generate a different cache key for every CI job
-- Fix to at least bitcoin ^0.25.2
-
-#### Fixed
-- Fix or ignore clippy warnings for all optional features except compact_filters
-- Pin cc version because last breaks rocksdb build
-
-### Blockchain
-#### Added
-- Add a trait to create `Blockchain`s from a configuration
-- Add an `AnyBlockchain` enum to allow switching at runtime
-- Document `AnyBlockchain` and `ConfigurableBlockchain`
-- Use our Instant struct to be compatible with wasm
-- Make esplora call in parallel
-- Allow to set concurrency in Esplora config and optionally pass it in repl
-
-#### Fixed
-- Fix receiving a coinbase using Electrum/Esplora
-- Use proper type for EsploraHeader, make conversion to BlockHeader infallible
-- Eagerly unwrap height option, save one collect
-
-#### Changed
-- Simplify the architecture of blockchain traits
-- Improve sync
-- Remove unused variant `HeaderParseFail`
-
-### CLI
-#### Added
-- Conditionally remove cli args according to enabled feature
-
-#### Changed
-- Add max_addresses param in sync
-- Split the internal and external policy paths
-
-### Database
-#### Added
-- Add `AnyDatabase` and `ConfigurableDatabase` traits
-
-### Descriptor
-#### Added
-- Add a macro to write descriptors from code
-- Add descriptor templates, add `DerivableKey`
-- Add ToWalletDescriptor trait tests
-- Add support for `sortedmulti` in `descriptor!`
-- Add ExtractPolicy trait tests
-- Add get_checksum tests, cleanup tests
-- Add descriptor macro tests
-
-#### Changes
-- Improve the descriptor macro, add traits for key and descriptor types
-
-#### Fixes
-- Fix the recovery of a descriptor given a PSBT
-
-### Keys
-#### Added
-- Add BIP39 support
-- Take `ScriptContext` into account when converting keys
-- Add a way to restrict the networks in which keys are valid
-- Add a trait for keys that can be generated
-- Fix entropy generation
-- Less convoluted entropy generation
-- Re-export tiny-bip39
-- Implement `GeneratableKey` trait for `bitcoin::PrivateKey`
-- Implement `ToDescriptorKey` trait for `GeneratedKey`
-- Add a shortcut to generate keys with the default options
-
-#### Fixed
-- Fix all-keys and cli-utils tests
-
-### Wallet
-#### Added
-- Allow to define static fees for transactions Fixes #137
-- Merging two match expressions for fee calculation
-- Incorporate RBF rules into utxo selection function
-- Add Branch and Bound coin selection
-- Add tests for BranchAndBoundCoinSelection::coin_select
-- Add tests for BranchAndBoundCoinSelection::bnb
-- Add tests for BranchAndBoundCoinSelection::single_random_draw
-- Add test that shwpkh populates witness_utxo
-- Add witness and redeem scripts to PSBT outputs
-- Add an option to include `PSBT_GLOBAL_XPUB`s in PSBTs
-- Eagerly finalize inputs
-
-#### Changed
-- Use collect to avoid iter unwrapping Options
-- Make coin_select take may/must use utxo lists
-- Improve `CoinSelectionAlgorithm`
-- Refactor `Wallet::bump_fee()`
-- Default to SIGHASH_ALL if not specified
-- Replace ChangeSpendPolicy::filter_utxos with a predicate
-- Make 'unspendable' into a HashSet
-- Stop implicitly enforcing manual selection by .add_utxo
-- Rename DumbCS to LargestFirstCoinSelection
-- Rename must_use_utxos to required_utxos
-- Rename may_use_utxos to optional_uxtos
-- Rename get_must_may_use_utxos to preselect_utxos
-- Remove redundant Box around address validators
-- Remove redundant Box around signers
-- Make Signer and AddressValidator Send and Sync
-- Split `send_all` into `set_single_recipient` and `drain_wallet`
-- Use TXIN_DEFAULT_WEIGHT constant in coin selection
-- Replace `must_use` with `required` in coin selection
-- Take both spending policies into account in create_tx
-- Check last derivation in cache to avoid recomputing
-- Use the branch-and-bound cs by default
-- Make coin_select return UTXOs instead of TxIns
-- Build output lookup inside complete transaction
-- Don't wrap SignersContainer arguments in Arc
-- More consistent references with 'signers' variables
-
-#### Fixed
-- Fix signing for `ShWpkh` inputs
-- Fix the recovery of a descriptor given a PSBT
-
-### Examples
-#### Added
-- Support esplora blockchain source in repl
-
-#### Changed
-- Revert back the REPL example to use Electrum
-- Remove the `magic` alias for `repl`
-- Require esplora feature for repl example
-
-#### Security
-- Use dirs-next instead of dirs since the latter is unmaintained
-
-## [0.1.0-beta.1] - 2020-09-08
-
-### Blockchain
-#### Added
-- Lightweight Electrum client with SSL/SOCKS5 support
-- Add a generalized "Blockchain" interface
-- Add Error::OfflineClient
-- Add the Esplora backend
-- Use async I/O in the various blockchain impls
-- Compact Filters blockchain implementation
-- Add support for Tor
-- Impl OnlineBlockchain for types wrapped in Arc
-
-### Database
-#### Added
-- Add a generalized database trait and a Sled-based implementation
-- Add an in-memory database
-
-### Descriptor
-#### Added
-- Wrap Miniscript descriptors to support xpubs
-- Policy and contribution
-- Transform a descriptor into its "public" version
-- Use `miniscript::DescriptorPublicKey`
-
-### Macros
-#### Added
-- Add a feature to enable the async interface on non-wasm32 platforms
-
-### Wallet
-#### Added
-- Wallet logic
-- Add `assume_height_reached` in PSBTSatisfier
-- Add an option to change the assumed current height
-- Specify the policy branch with a map
-- Add a few commands to handle psbts
-- Add hd_keypaths to outputs
-- Add a `TxBuilder` struct to simplify `create_tx()`'s interface
-- Abstract coin selection in a separate trait
-- Refill the address pool whenever necessary
-- Implement the wallet import/export format from FullyNoded
-- Add a type convert fee units, add `Wallet::estimate_fee()`
-- TxOrdering, shuffle/bip69 support
-- Add RBF and custom versions in TxBuilder
-- Allow limiting the use of internal utxos in TxBuilder
-- Add `force_non_witness_utxo()` to TxBuilder
-- RBF and add a few tests
-- Add AddressValidators
-- Add explicit ordering for the signers
-- Support signing the whole tx instead of individual inputs
-- Create a PSBT signer from an ExtendedDescriptor
-
-### Examples
-#### Added
-- Add REPL broadcast command
-- Add a miniscript compiler CLI
-- Expose list_transactions() in the REPL
-- Use `MemoryDatabase` in the compiler example
-- Make the REPL return JSON
-
-[0.1.0-beta.1]: https://github.com/bitcoindevkit/bdk/compare/96c87ea5...0.1.0-beta.1
-[v0.2.0]: https://github.com/bitcoindevkit/bdk/compare/0.1.0-beta.1...v0.2.0
-[v0.3.0]: https://github.com/bitcoindevkit/bdk/compare/v0.2.0...v0.3.0
-[v0.4.0]: https://github.com/bitcoindevkit/bdk/compare/v0.3.0...v0.4.0
-[v0.5.0]: https://github.com/bitcoindevkit/bdk/compare/v0.4.0...v0.5.0
-[v0.5.1]: https://github.com/bitcoindevkit/bdk/compare/v0.5.0...v0.5.1
-[v0.6.0]: https://github.com/bitcoindevkit/bdk/compare/v0.5.1...v0.6.0
-[v0.7.0]: https://github.com/bitcoindevkit/bdk/compare/v0.6.0...v0.7.0
-[v0.8.0]: https://github.com/bitcoindevkit/bdk/compare/v0.7.0...v0.8.0
-[v0.9.0]: https://github.com/bitcoindevkit/bdk/compare/v0.8.0...v0.9.0
-[v0.10.0]: https://github.com/bitcoindevkit/bdk/compare/v0.9.0...v0.10.0
-[v0.11.0]: https://github.com/bitcoindevkit/bdk/compare/v0.10.0...v0.11.0
-[v0.12.0]: https://github.com/bitcoindevkit/bdk/compare/v0.11.0...v0.12.0
-[v0.13.0]: https://github.com/bitcoindevkit/bdk/compare/v0.12.0...v0.13.0
-[v0.14.0]: https://github.com/bitcoindevkit/bdk/compare/v0.13.0...v0.14.0
-[v0.15.0]: https://github.com/bitcoindevkit/bdk/compare/v0.14.0...v0.15.0
-[v0.16.0]: https://github.com/bitcoindevkit/bdk/compare/v0.15.0...v0.16.0
-[v0.16.1]: https://github.com/bitcoindevkit/bdk/compare/v0.16.0...v0.16.1
-[v0.17.0]: https://github.com/bitcoindevkit/bdk/compare/v0.16.1...v0.17.0
-[v0.18.0]: https://github.com/bitcoindevkit/bdk/compare/v0.17.0...v0.18.0
-[v0.19.0]: https://github.com/bitcoindevkit/bdk/compare/v0.18.0...v0.19.0
-[v0.20.0]: https://github.com/bitcoindevkit/bdk/compare/v0.19.0...v0.20.0
-[v0.21.0]: https://github.com/bitcoindevkit/bdk/compare/v0.20.0...v0.21.0
-[v0.22.0]: https://github.com/bitcoindevkit/bdk/compare/v0.21.0...v0.22.0
-[v0.23.0]: https://github.com/bitcoindevkit/bdk/compare/v0.22.0...v0.23.0
-[v0.24.0]: https://github.com/bitcoindevkit/bdk/compare/v0.23.0...v0.24.0
-[v0.25.0]: https://github.com/bitcoindevkit/bdk/compare/v0.24.0...v0.25.0
-[v0.26.0]: https://github.com/bitcoindevkit/bdk/compare/v0.25.0...v0.26.0
-[v0.27.0]: https://github.com/bitcoindevkit/bdk/compare/v0.26.0...v0.27.0
-[v0.27.1]: https://github.com/bitcoindevkit/bdk/compare/v0.27.0...v0.27.1
-[v0.27.2]: https://github.com/bitcoindevkit/bdk/compare/v0.27.1...v0.27.2
-[v0.28.0]: https://github.com/bitcoindevkit/bdk/compare/v0.27.2...v0.28.0
-[v0.28.1]: https://github.com/bitcoindevkit/bdk/compare/v0.28.0...v0.28.1
-[v0.28.2]: https://github.com/bitcoindevkit/bdk/compare/v0.28.1...v0.28.2
-[v0.29.0]: https://github.com/bitcoindevkit/bdk/compare/v0.28.2...v0.29.0
-[v0.30.0]: https://github.com/bitcoindevkit/bdk/compare/v0.29.0...v0.30.0
-[v1.0.0-alpha.1]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.1
-[v1.0.0-alpha.2]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.2
-[v1.0.0-alpha.3]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.3
-[v1.0.0-alpha.4]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.4
-[v1.0.0-alpha.5]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.5
-[v1.0.0-alpha.6]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.6
-[v1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.7
-[v1.0.0-alpha.8]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.8
-[v1.0.0-alpha.9]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.9
-[v1.0.0-alpha.10]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.10
-[v1.0.0-alpha.11]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.11
-[v1.0.0-alpha.12]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.12
-[v1.0.0-alpha.13]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-alpha.13
-[v1.0.0-beta.1]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.1
-[v1.0.0-beta.2]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.2
-[v1.0.0-beta.3]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.3
-[v1.0.0-beta.4]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.4
-[v1.0.0-beta.5]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.5
-[v1.0.0-beta.6]: https://github.com/bitcoindevkit/bdk/releases/tag/v1.0.0-beta.6
-[wallet-1.0.0]: https://github.com/bitcoindevkit/bdk/releases/tag/wallet-1.0.0
-[wallet-1.1.0]: https://github.com/bitcoindevkit/bdk/releases/tag/wallet-1.1.0
-[wallet-1.2.0]: https://github.com/bitcoindevkit/bdk/releases/tag/wallet-1.2.0
diff --git a/crates/wallet/Cargo.toml b/crates/wallet/Cargo.toml
deleted file mode 100644 (file)
index 6620208..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-[package]
-name = "bdk_wallet"
-homepage = "https://bitcoindevkit.org"
-version = "1.2.0"
-repository = "https://github.com/bitcoindevkit/bdk"
-documentation = "https://docs.rs/bdk"
-description = "A modern, lightweight, descriptor-based wallet library"
-keywords = ["bitcoin", "wallet", "descriptor", "psbt"]
-readme = "README.md"
-license = "MIT OR Apache-2.0"
-authors = ["Bitcoin Dev Kit Developers"]
-edition = "2021"
-rust-version = "1.63"
-
-[lints]
-workspace = true
-
-[dependencies]
-rand_core = { version = "0.6.0" }
-miniscript = { version = "12.3.1", features = [ "serde" ], default-features = false }
-bitcoin = { version = "0.32.4", features = [ "serde", "base64" ], default-features = false }
-serde = { version = "^1.0", features = ["derive"] }
-serde_json = { version = "^1.0" }
-bdk_chain = { version = "0.21.1", features = [ "miniscript", "serde" ], default-features = false }
-bdk_file_store = { version = "0.18.1", optional = true }
-
-# Optional dependencies
-bip39 = { version = "2.0", optional = true }
-
-[features]
-default = ["std"]
-std = ["bitcoin/std", "bitcoin/rand-std", "miniscript/std", "bdk_chain/std"]
-compiler = ["miniscript/compiler"]
-all-keys = ["keys-bip39"]
-keys-bip39 = ["bip39"]
-rusqlite = ["bdk_chain/rusqlite"]
-file_store = ["bdk_file_store"]
-test-utils = ["std"]
-
-[dev-dependencies]
-lazy_static = "1.4"
-assert_matches = "1.5.0"
-tempfile = "3"
-bdk_chain = { version = "0.21.1", features = ["rusqlite"] }
-bdk_wallet = { path = ".", features = ["rusqlite", "file_store", "test-utils"] }
-bdk_file_store = { version = "0.18.1" }
-anyhow = "1"
-rand = "^0.8"
-
-[package.metadata.docs.rs]
-all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
-
-[[example]]
-name = "mnemonic_to_descriptors"
-path = "examples/mnemonic_to_descriptors.rs"
-required-features = ["all-keys"]
-
-[[example]]
-name = "miniscriptc"
-path = "examples/compiler.rs"
-required-features = ["compiler"]
diff --git a/crates/wallet/README.md b/crates/wallet/README.md
deleted file mode 100644 (file)
index 0f4a1db..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-<div align="center">
-  <h1>BDK</h1>
-
-  <img src="https://raw.githubusercontent.com/bitcoindevkit/bdk/master/static/bdk.png" width="220" />
-
-  <p>
-    <strong>A modern, lightweight, descriptor-based wallet library written in Rust!</strong>
-  </p>
-
-  <p>
-    <a href="https://crates.io/crates/bdk_wallet"><img alt="Crate Info" src="https://img.shields.io/crates/v/bdk_wallet.svg"/></a>
-    <a href="https://github.com/bitcoindevkit/bdk/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
-    <a href="https://github.com/bitcoindevkit/bdk/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk/workflows/CI/badge.svg"></a>
-    <a href="https://coveralls.io/github/bitcoindevkit/bdk?branch=master"><img src="https://coveralls.io/repos/github/bitcoindevkit/bdk/badge.svg?branch=master"/></a>
-    <a href="https://docs.rs/bdk_wallet"><img alt="API Docs" src="https://img.shields.io/badge/docs.rs-bdk_wallet-green"/></a>
-    <a href="https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html"><img alt="Rustc Version 1.63.0+" src="https://img.shields.io/badge/rustc-1.63.0%2B-lightgrey.svg"/></a>
-    <a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
-  </p>
-
-  <h4>
-    <a href="https://bitcoindevkit.org">Project Homepage</a>
-    <span> | </span>
-    <a href="https://docs.rs/bdk_wallet">Documentation</a>
-  </h4>
-</div>
-
-# BDK Wallet
-
-The `bdk_wallet` crate provides the [`Wallet`] type which is a simple, high-level
-interface built from the low-level components of [`bdk_chain`]. `Wallet` is a good starting point
-for many simple applications as well as a good demonstration of how to use the other mechanisms to
-construct a wallet. It has two keychains (external and internal) which are defined by
-[miniscript descriptors][`rust-miniscript`] and uses them to generate addresses. When you give it
-chain data it also uses the descriptors to find transaction outputs owned by them. From there, you
-can create and sign transactions.
-
-For details about the API of `Wallet` see the [module-level documentation][`Wallet`].
-
-## Blockchain data
-
-In order to get blockchain data for `Wallet` to consume, you should configure a client from
-an available chain source. Typically you make a request to the chain source and get a response
-that the `Wallet` can use to update its view of the chain.
-
-**Blockchain Data Sources**
-
-* [`bdk_esplora`]: Grabs blockchain data from Esplora for updating BDK structures.
-* [`bdk_electrum`]: Grabs blockchain data from Electrum for updating BDK structures.
-* [`bdk_bitcoind_rpc`]: Grabs blockchain data from Bitcoin Core for updating BDK structures.
-
-**Examples**
-
-* [`example-crates/example_wallet_esplora_async`](https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_wallet_esplora_async)
-* [`example-crates/example_wallet_esplora_blocking`](https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_wallet_esplora_blocking)
-* [`example-crates/example_wallet_electrum`](https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_wallet_electrum)
-* [`example-crates/example_wallet_rpc`](https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_wallet_rpc)
-
-## Persistence
-
-To persist `Wallet` state data use a data store crate that reads and writes [`ChangeSet`].
-
-**Implementations**
-
-* [`bdk_file_store`]: Stores wallet changes in a simple flat file.
-
-**Example**
-
-<!-- compile_fail because outpoint and txout are fake variables -->
-```rust,no_run
-use bdk_wallet::{bitcoin::Network, KeychainKind, ChangeSet, Wallet};
-
-// Open or create a new file store for wallet data.
-let mut db =
-    bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "/tmp/my_wallet.db")
-        .expect("create store");
-
-// Create a wallet with initial wallet data read from the file store.
-let network = Network::Testnet;
-let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
-let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)";
-let wallet_opt = Wallet::load()
-    .descriptor(KeychainKind::External, Some(descriptor))
-    .descriptor(KeychainKind::Internal, Some(change_descriptor))
-    .extract_keys()
-    .check_network(network)
-    .load_wallet(&mut db)
-    .expect("wallet");
-let mut wallet = match wallet_opt {
-    Some(wallet) => wallet,
-    None => Wallet::create(descriptor, change_descriptor)
-        .network(network)
-        .create_wallet(&mut db)
-        .expect("wallet"),
-};
-
-// Get a new address to receive bitcoin.
-let receive_address = wallet.reveal_next_address(KeychainKind::External);
-// Persist staged wallet data changes to the file store.
-wallet.persist(&mut db).expect("persist");
-println!("Your new receive address is: {}", receive_address.address);
-```
-
-<!-- ### Sync the balance of a descriptor -->
-
-<!-- ```rust,no_run -->
-<!-- use bdk_wallet::Wallet; -->
-<!-- use bdk_wallet::blockchain::ElectrumBlockchain; -->
-<!-- use bdk_wallet::SyncOptions; -->
-<!-- use bdk_wallet::electrum_client::Client; -->
-<!-- use bdk_wallet::bitcoin::Network; -->
-
-<!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); -->
-<!--     let wallet = Wallet::new( -->
-<!--         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", -->
-<!--         Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), -->
-<!--         Network::Testnet, -->
-<!--     )?; -->
-
-<!--     wallet.sync(&blockchain, SyncOptions::default())?; -->
-
-<!--     println!("Descriptor balance: {} SAT", wallet.balance()?); -->
-
-<!--     Ok(()) -->
-<!-- } -->
-<!-- ``` -->
-<!-- ### Generate a few addresses -->
-
-<!-- ```rust -->
-<!-- use bdk_wallet::Wallet; -->
-<!-- use bdk_wallet::AddressIndex::New; -->
-<!-- use bdk_wallet::bitcoin::Network; -->
-
-<!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let wallet = Wallet::new( -->
-<!--         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", -->
-<!--         Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), -->
-<!--         Network::Testnet, -->
-<!--     )?; -->
-
-<!--     println!("Address #0: {}", wallet.get_address(New)); -->
-<!--     println!("Address #1: {}", wallet.get_address(New)); -->
-<!--     println!("Address #2: {}", wallet.get_address(New)); -->
-
-<!--     Ok(()) -->
-<!-- } -->
-<!-- ``` -->
-
-<!-- ### Create a transaction -->
-
-<!-- ```rust,no_run -->
-<!-- use bdk_wallet::{FeeRate, Wallet, SyncOptions}; -->
-<!-- use bdk_wallet::blockchain::ElectrumBlockchain; -->
-
-<!-- use bdk_wallet::electrum_client::Client; -->
-<!-- use bdk_wallet::AddressIndex::New; -->
-
-<!-- use bitcoin::base64; -->
-<!-- use bdk_wallet::bitcoin::consensus::serialize; -->
-<!-- use bdk_wallet::bitcoin::Network; -->
-
-<!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); -->
-<!--     let wallet = Wallet::new( -->
-<!--         "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", -->
-<!--         Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"), -->
-<!--         Network::Testnet, -->
-<!--     )?; -->
-
-<!--     wallet.sync(&blockchain, SyncOptions::default())?; -->
-
-<!--     let send_to = wallet.get_address(New); -->
-<!--     let (psbt, details) = { -->
-<!--         let mut builder = wallet.build_tx(); -->
-<!--         builder -->
-<!--             .add_recipient(send_to.script_pubkey(), 50_000) -->
-<!--             .do_not_spend_change() -->
-<!--             .fee_rate(FeeRate::from_sat_per_vb(5.0)); -->
-<!--         builder.finish()? -->
-<!--     }; -->
-
-<!--     println!("Transaction details: {:#?}", details); -->
-<!--     println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt))); -->
-
-<!--     Ok(()) -->
-<!-- } -->
-<!-- ``` -->
-
-<!-- ### Sign a transaction -->
-
-<!-- ```rust,no_run -->
-<!-- use bdk_wallet::{Wallet, SignOptions}; -->
-
-<!-- use bitcoin::base64; -->
-<!-- use bdk_wallet::bitcoin::consensus::deserialize; -->
-<!-- use bdk_wallet::bitcoin::Network; -->
-
-<!-- fn main() -> Result<(), bdk_wallet::Error> { -->
-<!--     let wallet = Wallet::new( -->
-<!--         "wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)", -->
-<!--         Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"), -->
-<!--         Network::Testnet, -->
-<!--     )?; -->
-
-<!--     let psbt = "..."; -->
-<!--     let mut psbt = deserialize(&base64::decode(psbt).unwrap())?; -->
-
-<!--     let _finalized = wallet.sign(&mut psbt, SignOptions::default())?; -->
-
-<!--     Ok(()) -->
-<!-- } -->
-<!-- ``` -->
-
-## Testing
-
-### Unit testing
-
-```bash
-cargo test
-```
-
-# License
-
-Licensed under either of
-
-* Apache License, Version 2.0, ([LICENSE-APACHE](../../LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
-* MIT license ([LICENSE-MIT](../../LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
-
-at your option.
-
-# Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally
-submitted for inclusion in the work by you, as defined in the Apache-2.0
-license, shall be dual licensed as above, without any additional terms or
-conditions.
-
-[`Wallet`]: https://docs.rs/bdk_wallet/latest/bdk_wallet/wallet/struct.Wallet.html
-[`bdk_chain`]: https://docs.rs/bdk_chain/latest
-[`bdk_file_store`]: https://docs.rs/bdk_file_store/latest
-[`bdk_electrum`]: https://docs.rs/bdk_electrum/latest
-[`bdk_esplora`]: https://docs.rs/bdk_esplora/latest
-[`bdk_bitcoind_rpc`]: https://docs.rs/bdk_bitcoind_rpc/latest
-[`rust-miniscript`]: https://docs.rs/miniscript/latest/miniscript/index.html
diff --git a/crates/wallet/examples/compiler.rs b/crates/wallet/examples/compiler.rs
deleted file mode 100644 (file)
index bf43f3d..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-extern crate bdk_wallet;
-extern crate bitcoin;
-extern crate miniscript;
-extern crate serde_json;
-
-use std::error::Error;
-use std::str::FromStr;
-
-use bitcoin::Network;
-use miniscript::policy::Concrete;
-use miniscript::Descriptor;
-
-use bdk_wallet::{KeychainKind, Wallet};
-
-/// Miniscript policy is a high level abstraction of spending conditions. Defined in the
-/// rust-miniscript library here  https://docs.rs/miniscript/7.0.0/miniscript/policy/index.html
-/// rust-miniscript provides a `compile()` function that can be used to compile any miniscript policy
-/// into a descriptor. This descriptor then in turn can be used in bdk a fully functioning wallet
-/// can be derived from the policy.
-///
-/// This example demonstrates the interaction between a bdk wallet and miniscript policy.
-#[allow(clippy::print_stdout)]
-fn main() -> Result<(), Box<dyn Error>> {
-    // We start with a miniscript policy string
-    let policy_str = "or(
-        10@thresh(4,
-            pk(029ffbe722b147f3035c87cb1c60b9a5947dd49c774cc31e94773478711a929ac0),pk(025f05815e3a1a8a83bfbb03ce016c9a2ee31066b98f567f6227df1d76ec4bd143),pk(025625f41e4a065efc06d5019cbbd56fe8c07595af1231e7cbc03fafb87ebb71ec),pk(02a27c8b850a00f67da3499b60562673dcf5fdfb82b7e17652a7ac54416812aefd),pk(03e618ec5f384d6e19ca9ebdb8e2119e5bef978285076828ce054e55c4daf473e2)
-        ),1@and(
-            older(4209713),
-            thresh(2,
-                pk(03deae92101c790b12653231439f27b8897264125ecb2f46f48278603102573165),pk(033841045a531e1adf9910a6ec279589a90b3b8a904ee64ffd692bd08a8996c1aa),pk(02aebf2d10b040eb936a6f02f44ee82f8b34f5c1ccb20ff3949c2b28206b7c1068)
-            )
-        )
-    )"
-    .replace(&[' ', '\n', '\t'][..], "");
-
-    println!("Compiling policy: \n{}", policy_str);
-
-    // Parse the string as a [`Concrete`] type miniscript policy.
-    let policy = Concrete::<String>::from_str(&policy_str)?;
-
-    // Create a `wsh` type descriptor from the policy.
-    // `policy.compile()` returns the resulting miniscript from the policy.
-    let descriptor = Descriptor::new_wsh(policy.compile()?)?.to_string();
-
-    println!("Compiled into Descriptor: \n{}", descriptor);
-
-    // Create a new wallet from descriptors
-    let mut wallet = Wallet::create_single(descriptor)
-        .network(Network::Regtest)
-        .create_wallet_no_persist()?;
-
-    println!(
-        "First derived address from the descriptor: \n{}",
-        wallet.next_unused_address(KeychainKind::External),
-    );
-
-    // BDK also has it's own `Policy` structure to represent the spending condition in a more
-    // human readable json format.
-    let spending_policy = wallet.policies(KeychainKind::External)?;
-    println!(
-        "The BDK spending policy: \n{}",
-        serde_json::to_string_pretty(&spending_policy)?
-    );
-
-    Ok(())
-}
diff --git a/crates/wallet/examples/mnemonic_to_descriptors.rs b/crates/wallet/examples/mnemonic_to_descriptors.rs
deleted file mode 100644 (file)
index 19154c2..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-use anyhow::anyhow;
-use bdk_wallet::bitcoin::bip32::DerivationPath;
-use bdk_wallet::bitcoin::secp256k1::Secp256k1;
-use bdk_wallet::bitcoin::Network;
-use bdk_wallet::descriptor;
-use bdk_wallet::descriptor::IntoWalletDescriptor;
-use bdk_wallet::keys::bip39::{Language, Mnemonic, WordCount};
-use bdk_wallet::keys::{GeneratableKey, GeneratedKey};
-use bdk_wallet::miniscript::Tap;
-use std::str::FromStr;
-
-/// This example demonstrates how to generate a mnemonic phrase
-/// using BDK and use that to generate a descriptor string.
-#[allow(clippy::print_stdout)]
-fn main() -> Result<(), anyhow::Error> {
-    let secp = Secp256k1::new();
-
-    // In this example we are generating a 12 words mnemonic phrase
-    // but it is also possible generate 15, 18, 21 and 24 words
-    // using their respective `WordCount` variant.
-    let mnemonic: GeneratedKey<_, Tap> =
-        Mnemonic::generate((WordCount::Words12, Language::English))
-            .map_err(|_| anyhow!("Mnemonic generation error"))?;
-
-    println!("Mnemonic phrase: {}", *mnemonic);
-    let mnemonic_with_passphrase = (mnemonic, None);
-
-    // define external and internal derivation key path
-    let external_path = DerivationPath::from_str("m/86h/1h/0h/0").unwrap();
-    let internal_path = DerivationPath::from_str("m/86h/1h/0h/1").unwrap();
-
-    // generate external and internal descriptor from mnemonic
-    let (external_descriptor, ext_keymap) =
-        descriptor!(tr((mnemonic_with_passphrase.clone(), external_path)))?
-            .into_wallet_descriptor(&secp, Network::Testnet)?;
-    let (internal_descriptor, int_keymap) =
-        descriptor!(tr((mnemonic_with_passphrase, internal_path)))?
-            .into_wallet_descriptor(&secp, Network::Testnet)?;
-
-    println!("tpub external descriptor: {}", external_descriptor);
-    println!("tpub internal descriptor: {}", internal_descriptor);
-    println!(
-        "tprv external descriptor: {}",
-        external_descriptor.to_string_with_secret(&ext_keymap)
-    );
-    println!(
-        "tprv internal descriptor: {}",
-        internal_descriptor.to_string_with_secret(&int_keymap)
-    );
-
-    Ok(())
-}
diff --git a/crates/wallet/examples/policy.rs b/crates/wallet/examples/policy.rs
deleted file mode 100644 (file)
index 2b55a2e..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-extern crate bdk_wallet;
-use std::error::Error;
-
-use bdk_wallet::bitcoin::Network;
-use bdk_wallet::descriptor::{policy::BuildSatisfaction, ExtractPolicy, IntoWalletDescriptor};
-use bdk_wallet::signer::SignersContainer;
-
-/// This example describes the use of the BDK's [`bdk_wallet::descriptor::policy`] module.
-///
-/// Policy is higher abstraction representation of the wallet descriptor spending condition.
-/// This is useful to express complex miniscript spending conditions into more human readable form.
-/// The resulting `Policy` structure  can be used to derive spending conditions the wallet is capable
-/// to spend from.
-///
-/// This example demos a Policy output for a 2of2 multisig between between 2 parties, where the wallet holds
-/// one of the Extend Private key.
-#[allow(clippy::print_stdout)]
-fn main() -> Result<(), Box<dyn Error>> {
-    let secp = bitcoin::secp256k1::Secp256k1::new();
-
-    // The descriptor used in the example
-    // The form is "wsh(multi(2, <privkey>, <pubkey>))"
-    let desc = "wsh(multi(2,tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/*))";
-
-    // Use the descriptor string to derive the full descriptor and a keymap.
-    // The wallet descriptor can be used to create a new bdk_wallet::wallet.
-    // While the `keymap` can be used to create a `SignerContainer`.
-    //
-    // The `SignerContainer` can sign for `PSBT`s.
-    // a `bdk_wallet::Wallet` internally uses these to handle transaction signing.
-    // But they can be used as independent tools also.
-    let (wallet_desc, keymap) = desc.into_wallet_descriptor(&secp, Network::Testnet)?;
-
-    println!("Example Descriptor for policy analysis : {}", wallet_desc);
-
-    // Create the signer with the keymap and descriptor.
-    let signers_container = SignersContainer::build(keymap, &wallet_desc, &secp);
-
-    // Extract the Policy from the given descriptor and signer.
-    // Note that Policy is a wallet specific structure. It depends on the the descriptor, and
-    // what the concerned wallet with a given signer can sign for.
-    let policy = wallet_desc
-        .extract_policy(&signers_container, BuildSatisfaction::None, &secp)?
-        .expect("We expect a policy");
-
-    println!("Derived Policy for the descriptor {:#?}", policy);
-
-    Ok(())
-}
diff --git a/crates/wallet/src/descriptor/checksum.rs b/crates/wallet/src/descriptor/checksum.rs
deleted file mode 100644 (file)
index 7b77091..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptor checksum
-//!
-//! This module contains a re-implementation of the function used by Bitcoin Core to calculate the
-//! checksum of a descriptor
-
-use crate::descriptor::DescriptorError;
-use alloc::string::String;
-
-use miniscript::descriptor::checksum::desc_checksum;
-
-/// Compute the checksum of a descriptor, excludes any existing checksum in the descriptor string from the calculation
-pub fn calc_checksum(desc: &str) -> Result<String, DescriptorError> {
-    if let Some(split) = desc.split_once('#') {
-        let og_checksum = split.1;
-        let checksum = desc_checksum(split.0)?;
-        if og_checksum != checksum {
-            return Err(DescriptorError::InvalidDescriptorChecksum);
-        }
-        Ok(checksum)
-    } else {
-        Ok(desc_checksum(desc)?)
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::descriptor::calc_checksum;
-    use assert_matches::assert_matches;
-
-    // test calc_checksum() function; it should return the same value as Bitcoin Core
-    #[test]
-    fn test_calc_checksum() {
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)";
-        assert_eq!(calc_checksum(desc).unwrap(), "tqz0nc62");
-
-        let desc = "pkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/44'/1'/0'/0/*)";
-        assert_eq!(calc_checksum(desc).unwrap(), "lasegmfs");
-    }
-
-    // test calc_checksum() function; it should return the same value as Bitcoin Core even if the
-    // descriptor string includes a checksum hash
-    #[test]
-    fn test_calc_checksum_with_checksum_hash() {
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc62";
-        assert_eq!(calc_checksum(desc).unwrap(), "tqz0nc62");
-
-        let desc = "pkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/44'/1'/0'/0/*)#lasegmfs";
-        assert_eq!(calc_checksum(desc).unwrap(), "lasegmfs");
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc26";
-        assert_matches!(
-            calc_checksum(desc),
-            Err(DescriptorError::InvalidDescriptorChecksum)
-        );
-
-        let desc = "pkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/44'/1'/0'/0/*)#lasegmsf";
-        assert_matches!(
-            calc_checksum(desc),
-            Err(DescriptorError::InvalidDescriptorChecksum)
-        );
-    }
-
-    #[test]
-    fn test_calc_checksum_invalid_character() {
-        let sparkle_heart = unsafe { core::str::from_utf8_unchecked(&[240, 159, 146, 150]) };
-        let invalid_desc = format!("wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcL{}fjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)", sparkle_heart);
-
-        assert_matches!(
-            calc_checksum(&invalid_desc),
-            Err(DescriptorError::Miniscript(miniscript::Error::BadDescriptor(e))) if e == format!("Invalid character in checksum: '{}'", sparkle_heart)
-        );
-    }
-}
diff --git a/crates/wallet/src/descriptor/dsl.rs b/crates/wallet/src/descriptor/dsl.rs
deleted file mode 100644 (file)
index caec8cd..0000000
+++ /dev/null
@@ -1,1231 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptors DSL
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_top_level_sh {
-    // disallow `sortedmulti` in `bare()`
-    ( Bare, new, new, Legacy, sortedmulti $( $inner:tt )* ) => {
-        compile_error!("`bare()` descriptors can't contain any `sortedmulti()` operands");
-    };
-    ( Bare, new, new, Legacy, sortedmulti_vec $( $inner:tt )* ) => {
-        compile_error!("`bare()` descriptors can't contain any `sortedmulti_vec()` operands");
-    };
-
-    ( $inner_struct:ident, $constructor:ident, $sortedmulti_constructor:ident, $ctx:ident, sortedmulti $( $inner:tt )* ) => {{
-        use core::marker::PhantomData;
-
-        use $crate::miniscript::descriptor::{$inner_struct, Descriptor, DescriptorPublicKey};
-        use $crate::miniscript::$ctx;
-
-        let build_desc = |k, pks| {
-            Ok((Descriptor::<DescriptorPublicKey>::$inner_struct($inner_struct::$sortedmulti_constructor(k, pks)?), PhantomData::<$ctx>))
-        };
-
-        $crate::impl_sortedmulti!(build_desc, sortedmulti $( $inner )*)
-    }};
-    ( $inner_struct:ident, $constructor:ident, $sortedmulti_constructor:ident, $ctx:ident, sortedmulti_vec $( $inner:tt )* ) => {{
-        use core::marker::PhantomData;
-
-        use $crate::miniscript::descriptor::{$inner_struct, Descriptor, DescriptorPublicKey};
-        use $crate::miniscript::$ctx;
-
-        let build_desc = |k, pks| {
-            Ok((Descriptor::<DescriptorPublicKey>::$inner_struct($inner_struct::$sortedmulti_constructor(k, pks)?), PhantomData::<$ctx>))
-        };
-
-        $crate::impl_sortedmulti!(build_desc, sortedmulti_vec $( $inner )*)
-    }};
-
-    ( $inner_struct:ident, $constructor:ident, $sortedmulti_constructor:ident, $ctx:ident, $( $minisc:tt )* ) => {{
-        use $crate::miniscript::descriptor::{$inner_struct, Descriptor, DescriptorPublicKey};
-
-        $crate::fragment!($( $minisc )*)
-            .and_then(|(minisc, keymap, networks)| Ok(($inner_struct::$constructor(minisc)?, keymap, networks)))
-            .and_then(|(inner, key_map, valid_networks)| Ok((Descriptor::<DescriptorPublicKey>::$inner_struct(inner), key_map, valid_networks)))
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_top_level_pk {
-    ( $inner_type:ident, $ctx:ty, $key:expr ) => {{
-        use $crate::miniscript::descriptor::$inner_type;
-
-        #[allow(unused_imports)]
-        use $crate::keys::{DescriptorKey, IntoDescriptorKey};
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-
-        $key.into_descriptor_key()
-            .and_then(|key: DescriptorKey<$ctx>| key.extract(&secp))
-            .map_err($crate::descriptor::DescriptorError::Key)
-            .map(|(pk, key_map, valid_networks)| ($inner_type::new(pk), key_map, valid_networks))
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_top_level_tr {
-    ( $internal_key:expr, $tap_tree:expr ) => {{
-        use $crate::miniscript::descriptor::{
-            Descriptor, DescriptorPublicKey, KeyMap, TapTree, Tr,
-        };
-        use $crate::miniscript::Tap;
-
-        #[allow(unused_imports)]
-        use $crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
-
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-
-        $internal_key
-            .into_descriptor_key()
-            .and_then(|key: DescriptorKey<Tap>| key.extract(&secp))
-            .map_err($crate::descriptor::DescriptorError::Key)
-            .and_then(|(pk, mut key_map, mut valid_networks)| {
-                let tap_tree = $tap_tree.map(
-                    |(tap_tree, tree_keymap, tree_networks): (
-                        TapTree<DescriptorPublicKey>,
-                        KeyMap,
-                        ValidNetworks,
-                    )| {
-                        key_map.extend(tree_keymap.into_iter());
-                        valid_networks =
-                            $crate::keys::merge_networks(&valid_networks, &tree_networks);
-
-                        tap_tree
-                    },
-                );
-
-                Ok((
-                    Descriptor::<DescriptorPublicKey>::Tr(Tr::new(pk, tap_tree)?),
-                    key_map,
-                    valid_networks,
-                ))
-            })
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_leaf_opcode {
-    ( $terminal_variant:ident ) => {{
-        use $crate::descriptor::CheckMiniscript;
-
-        $crate::miniscript::Miniscript::from_ast(
-            $crate::miniscript::miniscript::decode::Terminal::$terminal_variant,
-        )
-        .map_err($crate::descriptor::DescriptorError::Miniscript)
-        .and_then(|minisc| {
-            minisc.check_miniscript()?;
-            Ok(minisc)
-        })
-        .map(|minisc| {
-            (
-                minisc,
-                $crate::miniscript::descriptor::KeyMap::default(),
-                $crate::keys::any_network(),
-            )
-        })
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_leaf_opcode_value {
-    ( $terminal_variant:ident, $value:expr ) => {{
-        use $crate::descriptor::CheckMiniscript;
-
-        $crate::miniscript::Miniscript::from_ast(
-            $crate::miniscript::miniscript::decode::Terminal::$terminal_variant($value),
-        )
-        .map_err($crate::descriptor::DescriptorError::Miniscript)
-        .and_then(|minisc| {
-            minisc.check_miniscript()?;
-            Ok(minisc)
-        })
-        .map(|minisc| {
-            (
-                minisc,
-                $crate::miniscript::descriptor::KeyMap::default(),
-                $crate::keys::any_network(),
-            )
-        })
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_leaf_opcode_value_two {
-    ( $terminal_variant:ident, $one:expr, $two:expr ) => {{
-        use $crate::descriptor::CheckMiniscript;
-
-        $crate::miniscript::Miniscript::from_ast(
-            $crate::miniscript::miniscript::decode::Terminal::$terminal_variant($one, $two),
-        )
-        .map_err($crate::descriptor::DescriptorError::Miniscript)
-        .and_then(|minisc| {
-            minisc.check_miniscript()?;
-            Ok(minisc)
-        })
-        .map(|minisc| {
-            (
-                minisc,
-                $crate::miniscript::descriptor::KeyMap::default(),
-                $crate::keys::any_network(),
-            )
-        })
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_node_opcode_two {
-    ( $terminal_variant:ident, $( $inner:tt )* ) => ({
-        use $crate::descriptor::CheckMiniscript;
-
-        let inner = $crate::fragment_internal!( @t $( $inner )* );
-        let (a, b) = $crate::descriptor::dsl::TupleTwo::from(inner).flattened();
-
-        a
-            .and_then(|a| Ok((a, b?)))
-            .and_then(|((a_minisc, mut a_keymap, a_networks), (b_minisc, b_keymap, b_networks))| {
-                // join key_maps
-                a_keymap.extend(b_keymap.into_iter());
-
-                let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
-                    $crate::alloc::sync::Arc::new(a_minisc),
-                    $crate::alloc::sync::Arc::new(b_minisc),
-                ))?;
-
-                minisc.check_miniscript()?;
-
-                Ok((minisc, a_keymap, $crate::keys::merge_networks(&a_networks, &b_networks)))
-            })
-    });
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_node_opcode_three {
-    ( $terminal_variant:ident, $( $inner:tt )* ) => ({
-        use $crate::descriptor::CheckMiniscript;
-
-        let inner = $crate::fragment_internal!( @t $( $inner )* );
-        let (a, b, c) = $crate::descriptor::dsl::TupleThree::from(inner).flattened();
-
-        a
-            .and_then(|a| Ok((a, b?, c?)))
-            .and_then(|((a_minisc, mut a_keymap, a_networks), (b_minisc, b_keymap, b_networks), (c_minisc, c_keymap, c_networks))| {
-                // join key_maps
-                a_keymap.extend(b_keymap.into_iter());
-                a_keymap.extend(c_keymap.into_iter());
-
-                let networks = $crate::keys::merge_networks(&a_networks, &b_networks);
-                let networks = $crate::keys::merge_networks(&networks, &c_networks);
-
-                let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
-                    $crate::alloc::sync::Arc::new(a_minisc),
-                    $crate::alloc::sync::Arc::new(b_minisc),
-                    $crate::alloc::sync::Arc::new(c_minisc),
-                ))?;
-
-                minisc.check_miniscript()?;
-
-                Ok((minisc, a_keymap, networks))
-            })
-    });
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_sortedmulti {
-    ( $build_desc:expr, sortedmulti_vec ( $thresh:expr, $keys:expr ) ) => ({
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-        $crate::keys::make_sortedmulti($thresh, $keys, $build_desc, &secp)
-    });
-    ( $build_desc:expr, sortedmulti ( $thresh:expr $(, $key:expr )+ ) ) => ({
-        use $crate::keys::IntoDescriptorKey;
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-
-        let keys = vec![
-            $(
-                $key.into_descriptor_key(),
-            )*
-        ];
-
-        keys.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
-            .map_err($crate::descriptor::DescriptorError::Key)
-            .and_then(|keys| $crate::keys::make_sortedmulti($thresh, keys, $build_desc, &secp))
-    });
-
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! parse_tap_tree {
-    ( @merge $tree_a:expr, $tree_b:expr) => {{
-        use $crate::miniscript::descriptor::TapTree;
-
-        $tree_a
-            .and_then(|tree_a| Ok((tree_a, $tree_b?)))
-            .and_then(|((a_tree, mut a_keymap, a_networks), (b_tree, b_keymap, b_networks))| {
-                a_keymap.extend(b_keymap.into_iter());
-                Ok((TapTree::combine(a_tree, b_tree), a_keymap, $crate::keys::merge_networks(&a_networks, &b_networks)))
-            })
-
-    }};
-
-    // Two sub-trees
-    ( { { $( $tree_a:tt )* }, { $( $tree_b:tt )* } } ) => {{
-        let tree_a = $crate::parse_tap_tree!( { $( $tree_a )* } );
-        let tree_b = $crate::parse_tap_tree!( { $( $tree_b )* } );
-
-        $crate::parse_tap_tree!(@merge tree_a, tree_b)
-    }};
-
-    // One leaf and a sub-tree
-    ( { $op_a:ident ( $( $minisc_a:tt )* ), { $( $tree_b:tt )* } } ) => {{
-        let tree_a = $crate::parse_tap_tree!( $op_a ( $( $minisc_a )* ) );
-        let tree_b = $crate::parse_tap_tree!( { $( $tree_b )* } );
-
-        $crate::parse_tap_tree!(@merge tree_a, tree_b)
-    }};
-    ( { { $( $tree_a:tt )* }, $op_b:ident ( $( $minisc_b:tt )* ) } ) => {{
-        let tree_a = $crate::parse_tap_tree!( { $( $tree_a )* } );
-        let tree_b = $crate::parse_tap_tree!( $op_b ( $( $minisc_b )* ) );
-
-        $crate::parse_tap_tree!(@merge tree_a, tree_b)
-    }};
-
-    // Two leaves
-    ( { $op_a:ident ( $( $minisc_a:tt )* ), $op_b:ident ( $( $minisc_b:tt )* ) } ) => {{
-        let tree_a = $crate::parse_tap_tree!( $op_a ( $( $minisc_a )* ) );
-        let tree_b = $crate::parse_tap_tree!( $op_b ( $( $minisc_b )* ) );
-
-        $crate::parse_tap_tree!(@merge tree_a, tree_b)
-    }};
-
-    // Single leaf
-    ( $op:ident ( $( $minisc:tt )* ) ) => {{
-        use $crate::alloc::sync::Arc;
-        use $crate::miniscript::descriptor::TapTree;
-
-        $crate::fragment!( $op ( $( $minisc )* ) )
-            .map(|(a_minisc, a_keymap, a_networks)| (TapTree::Leaf(Arc::new(a_minisc)), a_keymap, a_networks))
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! apply_modifier {
-    ( $terminal_variant:ident, $inner:expr ) => {{
-        use $crate::descriptor::CheckMiniscript;
-
-        $inner
-            .map_err(|e| -> $crate::descriptor::DescriptorError { e.into() })
-            .and_then(|(minisc, keymap, networks)| {
-                let minisc = $crate::miniscript::Miniscript::from_ast(
-                    $crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
-                        $crate::alloc::sync::Arc::new(minisc),
-                    ),
-                )?;
-
-                minisc.check_miniscript()?;
-
-                Ok((minisc, keymap, networks))
-            })
-    }};
-
-    ( a: $inner:expr ) => {{
-        $crate::apply_modifier!(Alt, $inner)
-    }};
-    ( s: $inner:expr ) => {{
-        $crate::apply_modifier!(Swap, $inner)
-    }};
-    ( c: $inner:expr ) => {{
-        $crate::apply_modifier!(Check, $inner)
-    }};
-    ( d: $inner:expr ) => {{
-        $crate::apply_modifier!(DupIf, $inner)
-    }};
-    ( v: $inner:expr ) => {{
-        $crate::apply_modifier!(Verify, $inner)
-    }};
-    ( j: $inner:expr ) => {{
-        $crate::apply_modifier!(NonZero, $inner)
-    }};
-    ( n: $inner:expr ) => {{
-        $crate::apply_modifier!(ZeroNotEqual, $inner)
-    }};
-
-    // Modifiers expanded to other operators
-    ( t: $inner:expr ) => {{
-        $inner.and_then(|(a_minisc, a_keymap, a_networks)| {
-            $crate::impl_leaf_opcode_value_two!(
-                AndV,
-                $crate::alloc::sync::Arc::new(a_minisc),
-                $crate::alloc::sync::Arc::new($crate::fragment!(true).unwrap().0)
-            )
-            .map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
-        })
-    }};
-    ( l: $inner:expr ) => {{
-        $inner.and_then(|(a_minisc, a_keymap, a_networks)| {
-            $crate::impl_leaf_opcode_value_two!(
-                OrI,
-                $crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0),
-                $crate::alloc::sync::Arc::new(a_minisc)
-            )
-            .map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
-        })
-    }};
-    ( u: $inner:expr ) => {{
-        $inner.and_then(|(a_minisc, a_keymap, a_networks)| {
-            $crate::impl_leaf_opcode_value_two!(
-                OrI,
-                $crate::alloc::sync::Arc::new(a_minisc),
-                $crate::alloc::sync::Arc::new($crate::fragment!(false).unwrap().0)
-            )
-            .map(|(minisc, _, _)| (minisc, a_keymap, a_networks))
-        })
-    }};
-}
-
-/// Macro to write full descriptors with code
-///
-/// This macro expands to a `Result` of
-/// [`DescriptorTemplateOut`](super::template::DescriptorTemplateOut) and [`DescriptorError`](crate::descriptor::DescriptorError)
-///
-/// The syntax is very similar to the normal descriptor syntax, with the exception that modifiers
-/// cannot be grouped together. For instance, a descriptor fragment like `sdv:older(144)` has to be
-/// broken up to `s:d:v:older(144)`.
-///
-/// The `pk()`, `pk_k()` and `pk_h()` operands can take as argument any type that implements
-/// [`IntoDescriptorKey`]. This means that keys can also be written inline as strings, but in that
-/// case they must be wrapped in quotes, which is another difference compared to the standard
-/// descriptor syntax.
-///
-/// [`IntoDescriptorKey`]: crate::keys::IntoDescriptorKey
-///
-/// ## Example
-///
-/// Signature plus timelock descriptor:
-///
-/// ```
-/// # use std::str::FromStr;
-/// let (my_descriptor, my_keys_map, networks) = bdk_wallet::descriptor!(sh(wsh(and_v(v:pk("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"),older(50)))))?;
-/// # Ok::<(), Box<dyn std::error::Error>>(())
-/// ```
-///
-/// -------
-///
-/// 2-of-3 that becomes a 1-of-3 after a timelock has expired. Both `descriptor_a` and `descriptor_b` are equivalent: the first
-/// syntax is more suitable for a fixed number of items known at compile time, while the other accepts a
-/// [`Vec`] of items, which makes it more suitable for writing dynamic descriptors.
-///
-/// They both produce the descriptor: `wsh(thresh(2,pk(...),s:pk(...),sndv:older(...)))`
-///
-/// ```
-/// # use std::str::FromStr;
-/// let my_key_1 = bitcoin::PublicKey::from_str(
-///     "02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c",
-/// )?;
-/// let my_key_2 =
-///     bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
-/// let my_timelock = 50;
-///
-/// let (descriptor_a, key_map_a, networks) = bdk_wallet::descriptor! {
-///     wsh (
-///         thresh(2, pk(my_key_1), s:pk(my_key_2), s:n:d:v:older(my_timelock))
-///     )
-/// }?;
-///
-/// #[rustfmt::skip]
-/// let b_items = vec![
-///     bdk_wallet::fragment!(pk(my_key_1))?,
-///     bdk_wallet::fragment!(s:pk(my_key_2))?,
-///     bdk_wallet::fragment!(s:n:d:v:older(my_timelock))?,
-/// ];
-/// let (descriptor_b, mut key_map_b, networks) =
-///     bdk_wallet::descriptor!(wsh(thresh_vec(2, b_items)))?;
-///
-/// assert_eq!(descriptor_a, descriptor_b);
-/// assert_eq!(key_map_a.len(), key_map_b.len());
-/// # Ok::<(), Box<dyn std::error::Error>>(())
-/// ```
-///
-/// ------
-///
-/// Simple 2-of-2 multi-signature, equivalent to: `wsh(multi(2, ...))`
-///
-/// ```
-/// # use std::str::FromStr;
-/// let my_key_1 = bitcoin::PublicKey::from_str(
-///     "02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c",
-/// )?;
-/// let my_key_2 =
-///     bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
-///
-/// let (descriptor, key_map, networks) = bdk_wallet::descriptor! {
-///     wsh (
-///         multi(2, my_key_1, my_key_2)
-///     )
-/// }?;
-/// # Ok::<(), Box<dyn std::error::Error>>(())
-/// ```
-///
-/// ------
-///
-/// Native-Segwit single-sig, equivalent to: `wpkh(...)`
-///
-/// ```
-/// let my_key =
-///     bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
-///
-/// let (descriptor, key_map, networks) = bdk_wallet::descriptor!(wpkh(my_key))?;
-/// # Ok::<(), Box<dyn std::error::Error>>(())
-/// ```
-///
-/// [`Vec`]: alloc::vec::Vec
-#[macro_export]
-macro_rules! descriptor {
-    ( bare ( $( $minisc:tt )* ) ) => ({
-        $crate::impl_top_level_sh!(Bare, new, new, Legacy, $( $minisc )*)
-    });
-    ( sh ( wsh ( $( $minisc:tt )* ) ) ) => ({
-        $crate::descriptor!(shwsh ($( $minisc )*))
-    });
-    ( shwsh ( $( $minisc:tt )* ) ) => ({
-        $crate::impl_top_level_sh!(Sh, new_wsh, new_wsh_sortedmulti, Segwitv0, $( $minisc )*)
-    });
-    ( pk ( $key:expr ) ) => ({
-        // `pk()` is actually implemented as `bare(pk())`
-        $crate::descriptor!( bare ( pk ( $key ) ) )
-    });
-    ( pkh ( $key:expr ) ) => ({
-        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
-
-        $crate::impl_top_level_pk!(Pkh, $crate::miniscript::Legacy, $key)
-            .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
-            .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Pkh(a), b, c))
-    });
-    ( wpkh ( $key:expr ) ) => ({
-        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
-
-        $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
-            .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
-            .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Wpkh(a), b, c))
-    });
-    ( sh ( wpkh ( $key:expr ) ) ) => ({
-        $crate::descriptor!(shwpkh ( $key ))
-    });
-    ( shwpkh ( $key:expr ) ) => ({
-        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh};
-
-        $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
-            .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
-            .and_then(|(a, b, c)| Ok((Descriptor::<DescriptorPublicKey>::Sh(Sh::new_wpkh(a.into_inner())?), b, c)))
-    });
-    ( sh ( $( $minisc:tt )* ) ) => ({
-        $crate::impl_top_level_sh!(Sh, new, new_sortedmulti, Legacy, $( $minisc )*)
-    });
-    ( wsh ( $( $minisc:tt )* ) ) => ({
-        $crate::impl_top_level_sh!(Wsh, new, new_sortedmulti, Segwitv0, $( $minisc )*)
-    });
-
-    ( tr ( $internal_key:expr ) ) => ({
-        $crate::impl_top_level_tr!($internal_key, None)
-    });
-    ( tr ( $internal_key:expr, $( $taptree:tt )* ) ) => ({
-        let tap_tree = $crate::parse_tap_tree!( $( $taptree )* );
-        tap_tree
-            .and_then(|tap_tree| $crate::impl_top_level_tr!($internal_key, Some(tap_tree)))
-    });
-}
-
-#[doc(hidden)]
-pub struct TupleTwo<A, B> {
-    pub a: A,
-    pub b: B,
-}
-
-impl<A, B> TupleTwo<A, B> {
-    pub fn flattened(self) -> (A, B) {
-        (self.a, self.b)
-    }
-}
-
-impl<A, B> From<(A, (B, ()))> for TupleTwo<A, B> {
-    fn from((a, (b, _)): (A, (B, ()))) -> Self {
-        TupleTwo { a, b }
-    }
-}
-
-#[doc(hidden)]
-pub struct TupleThree<A, B, C> {
-    pub a: A,
-    pub b: B,
-    pub c: C,
-}
-
-impl<A, B, C> TupleThree<A, B, C> {
-    pub fn flattened(self) -> (A, B, C) {
-        (self.a, self.b, self.c)
-    }
-}
-
-impl<A, B, C> From<(A, (B, (C, ())))> for TupleThree<A, B, C> {
-    fn from((a, (b, (c, _))): (A, (B, (C, ())))) -> Self {
-        TupleThree { a, b, c }
-    }
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! group_multi_keys {
-    ( $( $key:expr ),+ ) => {{
-        use $crate::keys::IntoDescriptorKey;
-
-        let keys = vec![
-            $(
-                $key.into_descriptor_key(),
-            )*
-        ];
-
-        keys.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
-            .map_err($crate::descriptor::DescriptorError::Key)
-    }};
-}
-
-#[doc(hidden)]
-#[macro_export]
-macro_rules! fragment_internal {
-    // The @v prefix is used to parse a sequence of operands and return them in a vector. This is
-    // used by operands that take a variable number of arguments, like `thresh()` and `multi()`.
-    ( @v $op:ident ( $( $args:tt )* ) $( $tail:tt )* ) => ({
-        let mut v = vec![$crate::fragment!( $op ( $( $args )* ) )];
-        v.append(&mut $crate::fragment_internal!( @v $( $tail )* ));
-
-        v
-    });
-    // Match modifiers
-    ( @v $modif:tt : $( $tail:tt )* ) => ({
-        let mut v = $crate::fragment_internal!( @v $( $tail )* );
-        let first = v.drain(..1).next().unwrap();
-
-        let first = $crate::apply_modifier!($modif:first);
-
-        let mut v_final = vec![first];
-        v_final.append(&mut v);
-
-        v_final
-    });
-    // Remove commas between operands
-    ( @v , $( $tail:tt )* ) => ({
-        $crate::fragment_internal!( @v $( $tail )* )
-    });
-    ( @v ) => ({
-        vec![]
-    });
-
-    // The @t prefix is used to parse a sequence of operands and return them in a tuple. This
-    // allows checking at compile-time the number of arguments passed to an operand. For this
-    // reason it's used by `and_*()`, `or_*()`, etc.
-    //
-    // Unfortunately, due to the fact that concatenating tuples is pretty hard, the final result
-    // adds in the first spot the parsed operand and in the second spot the result of parsing
-    // all the following ones. For two operands the type then corresponds to: (X, (X, ())). For
-    // three operands it's (X, (X, (X, ()))), etc.
-    //
-    // To check that the right number of arguments has been passed we can "cast" those tuples to
-    // more convenient structures like `TupleTwo`. If the conversion succeeds, the right number of
-    // args was passed. Otherwise the compilation fails entirely.
-    ( @t $op:ident ( $( $args:tt )* ) $( $tail:tt )* ) => ({
-        ($crate::fragment!( $op ( $( $args )* ) ), $crate::fragment_internal!( @t $( $tail )* ))
-    });
-    // Match modifiers
-    ( @t $modif:tt : $( $tail:tt )* ) => ({
-        let (first, tail) = $crate::fragment_internal!( @t $( $tail )* );
-        ($crate::apply_modifier!($modif:first), tail)
-    });
-    // Remove commas between operands
-    ( @t , $( $tail:tt )* ) => ({
-        $crate::fragment_internal!( @t $( $tail )* )
-    });
-    ( @t ) => ({});
-
-    // Fallback to calling `fragment!()`
-    ( $( $tokens:tt )* ) => ({
-        $crate::fragment!($( $tokens )*)
-    });
-}
-
-/// Macro to write descriptor fragments with code
-///
-/// This macro will be expanded to an object of type `Result<(Miniscript<DescriptorPublicKey, _>, KeyMap, ValidNetworks), DescriptorError>`. It allows writing
-/// fragments of larger descriptors that can be pieced together using `fragment!(thresh_vec(m, ...))`.
-///
-/// The syntax to write macro fragment is the same as documented for the [`descriptor`] macro.
-#[macro_export]
-macro_rules! fragment {
-    // Modifiers
-    ( $modif:tt : $( $tail:tt )* ) => ({
-        let op = $crate::fragment!( $( $tail )* );
-        $crate::apply_modifier!($modif:op)
-    });
-
-    // Miniscript
-    ( true ) => ({
-        $crate::impl_leaf_opcode!(True)
-    });
-    ( false ) => ({
-        $crate::impl_leaf_opcode!(False)
-    });
-    ( pk_k ( $key:expr ) ) => ({
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-        $crate::keys::make_pk($key, &secp)
-    });
-    ( pk ( $key:expr ) ) => ({
-        $crate::fragment!(c:pk_k ( $key ))
-    });
-    ( pk_h ( $key:expr ) ) => ({
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-        $crate::keys::make_pkh($key, &secp)
-    });
-    ( after ( $value:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value).expect("valid `AbsLockTime`"))
-    });
-    ( older ( $value:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Older, $crate::miniscript::RelLockTime::from_consensus($value).expect("valid `RelLockTime`")) // TODO!!
-    });
-    ( sha256 ( $hash:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Sha256, $hash)
-    });
-    ( hash256 ( $hash:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Hash256, $hash)
-    });
-    ( ripemd160 ( $hash:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Ripemd160, $hash)
-    });
-    ( hash160 ( $hash:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Hash160, $hash)
-    });
-    ( and_v ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(AndV, $( $inner )*)
-    });
-    ( and_b ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(AndB, $( $inner )*)
-    });
-    ( and_or ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_three!(AndOr, $( $inner )*)
-    });
-    ( andor ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_three!(AndOr, $( $inner )*)
-    });
-    ( or_b ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(OrB, $( $inner )*)
-    });
-    ( or_d ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(OrD, $( $inner )*)
-    });
-    ( or_c ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(OrC, $( $inner )*)
-    });
-    ( or_i ( $( $inner:tt )* ) ) => ({
-        $crate::impl_node_opcode_two!(OrI, $( $inner )*)
-    });
-    ( thresh_vec ( $thresh:expr, $items:expr ) ) => ({
-        use $crate::miniscript::descriptor::KeyMap;
-
-        let (items, key_maps_networks): ($crate::alloc::vec::Vec<_>, $crate::alloc::vec::Vec<_>) = $items.into_iter().map(|(a, b, c)| (a, (b, c))).unzip();
-        let items = items.into_iter().map($crate::alloc::sync::Arc::new).collect();
-
-        let (key_maps, valid_networks) = key_maps_networks.into_iter().fold((KeyMap::default(), $crate::keys::any_network()), |(mut keys_acc, net_acc), (key, net)| {
-            keys_acc.extend(key.into_iter());
-            let net_acc = $crate::keys::merge_networks(&net_acc, &net);
-
-            (keys_acc, net_acc)
-        });
-
-        let thresh = $crate::miniscript::Threshold::new($thresh, items).expect("valid threshold and pks collection");
-        $crate::impl_leaf_opcode_value!(Thresh, thresh)
-            .map(|(minisc, _, _)| (minisc, key_maps, valid_networks))
-    });
-    ( thresh ( $thresh:expr, $( $inner:tt )* ) ) => ({
-        let items = $crate::fragment_internal!( @v $( $inner )* );
-
-        items.into_iter().collect::<Result<$crate::alloc::vec::Vec<_>, _>>()
-            .and_then(|items| $crate::fragment!(thresh_vec($thresh, items)))
-    });
-    ( multi_vec ( $thresh:expr, $keys:expr ) ) => ({
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-
-        let fun = |k, pks| {
-            let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection");
-            $crate::miniscript::Terminal::Multi(thresh)
-        };
-
-        $crate::keys::make_multi($thresh, fun, $keys, &secp)
-    });
-    ( multi ( $thresh:expr $(, $key:expr )+ ) ) => ({
-        $crate::group_multi_keys!( $( $key ),* )
-            .and_then(|keys| $crate::fragment!( multi_vec ( $thresh, keys ) ))
-    });
-    ( multi_a_vec ( $thresh:expr, $keys:expr ) ) => ({
-        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
-
-        let fun = |k, pks| {
-            let thresh = $crate::miniscript::Threshold::new(k, pks).expect("valid threshold and pks collection");
-            $crate::miniscript::Terminal::MultiA(thresh)
-        };
-
-        $crate::keys::make_multi($thresh, fun, $keys, &secp)
-    });
-    ( multi_a ( $thresh:expr $(, $key:expr )+ ) ) => ({
-        $crate::group_multi_keys!( $( $key ),* )
-            .and_then(|keys| $crate::fragment!( multi_a_vec ( $thresh, keys ) ))
-    });
-
-    // `sortedmulti()` is handled separately
-    ( sortedmulti ( $( $inner:tt )* ) ) => ({
-        compile_error!("`sortedmulti` can only be used as the root operand of a descriptor");
-    });
-    ( sortedmulti_vec ( $( $inner:tt )* ) ) => ({
-        compile_error!("`sortedmulti_vec` can only be used as the root operand of a descriptor");
-    });
-}
-
-#[cfg(test)]
-mod test {
-    use alloc::string::ToString;
-    use bitcoin::secp256k1::Secp256k1;
-    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
-    use miniscript::{Descriptor, Legacy, Segwitv0};
-
-    use core::str::FromStr;
-
-    use crate::descriptor::{DescriptorError, DescriptorMeta};
-    use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
-    use bitcoin::bip32;
-    use bitcoin::Network::{Bitcoin, Regtest, Signet, Testnet, Testnet4};
-    use bitcoin::PrivateKey;
-
-    // test the descriptor!() macro
-
-    // verify descriptor generates expected script(s) (if bare or pk) or address(es)
-    fn check(
-        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
-        is_witness: bool,
-        is_fixed: bool,
-        expected: &[&str],
-    ) {
-        let (desc, _key_map, _networks) = desc.unwrap();
-        assert_eq!(desc.is_witness(), is_witness);
-        assert_eq!(!desc.has_wildcard(), is_fixed);
-        for i in 0..expected.len() {
-            let child_desc = desc
-                .at_derivation_index(i as u32)
-                .expect("i is not hardened");
-            let address = child_desc.address(Regtest);
-            if let Ok(address) = address {
-                assert_eq!(address.to_string(), *expected.get(i).unwrap());
-            } else {
-                let script = child_desc.script_pubkey();
-                assert_eq!(script.to_hex_string(), *expected.get(i).unwrap());
-            }
-        }
-    }
-
-    // - at least one of each "type" of operator; i.e. one modifier, one leaf_opcode, one leaf_opcode_value, etc.
-    // - mixing up key types that implement IntoDescriptorKey in multi() or thresh()
-
-    // expected script for pk and bare manually created
-    // expected addresses created with `bitcoin-cli getdescriptorinfo` (for hash) and `bitcoin-cli deriveaddresses`
-
-    #[test]
-    fn test_fixed_legacy_descriptors() {
-        let pubkey1 = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        let pubkey2 = bitcoin::PublicKey::from_str(
-            "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af",
-        )
-        .unwrap();
-
-        check(
-            descriptor!(bare(multi(1,pubkey1,pubkey2))),
-            false,
-            true,
-            &["512103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd21032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af52ae"],
-        );
-        check(
-            descriptor!(pk(pubkey1)),
-            false,
-            true,
-            &["2103a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bdac"],
-        );
-        check(
-            descriptor!(pkh(pubkey1)),
-            false,
-            true,
-            &["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
-        );
-        check(
-            descriptor!(sh(multi(1, pubkey1, pubkey2))),
-            false,
-            true,
-            &["2MymURoV1bzuMnWMGiXzyomDkeuxXY7Suey"],
-        );
-    }
-
-    #[test]
-    fn test_fixed_segwitv0_descriptors() {
-        let pubkey1 = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        let pubkey2 = bitcoin::PublicKey::from_str(
-            "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af",
-        )
-        .unwrap();
-
-        check(
-            descriptor!(wpkh(pubkey1)),
-            true,
-            true,
-            &["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
-        );
-        check(
-            descriptor!(sh(wpkh(pubkey1))),
-            true,
-            true,
-            &["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
-        );
-        check(
-            descriptor!(wsh(multi(1, pubkey1, pubkey2))),
-            true,
-            true,
-            &["bcrt1qgw8jvv2hsrvjfa6q66rk6har7d32lrqm5unnf5cl63q9phxfvgps5fyfqe"],
-        );
-        check(
-            descriptor!(sh(wsh(multi(1, pubkey1, pubkey2)))),
-            true,
-            true,
-            &["2NCidRJysy7apkmE6JF5mLLaJFkrN3Ub9iy"],
-        );
-    }
-
-    #[test]
-    fn test_fixed_threeop_descriptors() {
-        let redeem_key = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        let move_key = bitcoin::PublicKey::from_str(
-            "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af",
-        )
-        .unwrap();
-
-        check(
-            descriptor!(sh(wsh(and_or(pk(redeem_key), older(1000), pk(move_key))))),
-            true,
-            true,
-            &["2MypGwr5eQWAWWJtiJgUEToVxc4zuokjQRe"],
-        );
-    }
-
-    #[test]
-    fn test_bip32_legacy_descriptors() {
-        let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-
-        let path = bip32::DerivationPath::from_str("m/0").unwrap();
-        let desc_key = (xprv, path.clone()).into_descriptor_key().unwrap();
-        check(
-            descriptor!(pk(desc_key)),
-            false,
-            false,
-            &[
-                "2102363ad03c10024e1b597a5b01b9982807fb638e00b06f3b2d4a89707de3b93c37ac",
-                "2102063a21fd780df370ed2fc8c4b86aa5ea642630609c203009df631feb7b480dd2ac",
-                "2102ba2685ad1fa5891cb100f1656b2ce3801822ccb9bac0336734a6f8c1b93ebbc0ac",
-            ],
-        );
-
-        let desc_key = (xprv, path.clone()).into_descriptor_key().unwrap();
-        check(
-            descriptor!(pkh(desc_key)),
-            false,
-            false,
-            &[
-                "muvBdsVpJxpFuTHMKA47htJPdCvdt4F9DP",
-                "mxQSHK7DL2t1DN3xFxov1janCoXSSkrSPj",
-                "mfz43r15GiWo4nizmyzMNubsnkDpByFFAn",
-            ],
-        );
-
-        let path2 = bip32::DerivationPath::from_str("m/2147483647'/0").unwrap();
-        let desc_key1 = (xprv, path).into_descriptor_key().unwrap();
-        let desc_key2 = (xprv, path2).into_descriptor_key().unwrap();
-
-        check(
-            descriptor!(sh(multi(1, desc_key1, desc_key2))),
-            false,
-            false,
-            &[
-                "2MtMDXsfwefZkEEhVViEPidvcKRUtJamJJ8",
-                "2MwAUZ1NYyWjhVvGTethFL6n7nZhS8WE6At",
-                "2MuT6Bj66HLwZd7s4SoD8XbK4GwriKEA6Gr",
-            ],
-        );
-    }
-
-    #[test]
-    fn test_bip32_segwitv0_descriptors() {
-        let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-
-        let path = bip32::DerivationPath::from_str("m/0").unwrap();
-        let desc_key = (xprv, path.clone()).into_descriptor_key().unwrap();
-        check(
-            descriptor!(wpkh(desc_key)),
-            true,
-            false,
-            &[
-                "bcrt1qnhm8w9fhc8cxzgqsmqdf9fyjccyvc0gltnymu0",
-                "bcrt1qhylfd55rn75w9fj06zspctad5w4hz33rf0ttad",
-                "bcrt1qq5sq3a6k9av9d8cne0k9wcldy4nqey5yt6889r",
-            ],
-        );
-
-        let desc_key = (xprv, path.clone()).into_descriptor_key().unwrap();
-        check(
-            descriptor!(sh(wpkh(desc_key))),
-            true,
-            false,
-            &[
-                "2MxvjQCaLqZ5QxZ7XotZDQ63hZw3NPss763",
-                "2NDUoevN4QMzhvHDMGhKuiT2fN9HXbFRMwn",
-                "2NF4BEAY2jF1Fu8vqfN3NVKoFtom77pUxrx",
-            ],
-        );
-
-        let path2 = bip32::DerivationPath::from_str("m/2147483647'/0").unwrap();
-        let desc_key1 = (xprv, path.clone()).into_descriptor_key().unwrap();
-        let desc_key2 = (xprv, path2.clone()).into_descriptor_key().unwrap();
-        check(
-            descriptor!(wsh(multi(1, desc_key1, desc_key2))),
-            true,
-            false,
-            &[
-                "bcrt1qfxv8mxmlv5sz8q2mnuyaqdfe9jr4vvmx0csjhn092p6f4qfygfkq2hng49",
-                "bcrt1qerj85g243e6jlcdxpmn9spk0gefcwvu7nw7ee059d5ydzpdhkm2qwfkf5k",
-                "bcrt1qxkl2qss3k58q9ktc8e89pwr4gnptfpw4hju4xstxcjc0hkcae3jstluty7",
-            ],
-        );
-
-        let desc_key1 = (xprv, path).into_descriptor_key().unwrap();
-        let desc_key2 = (xprv, path2).into_descriptor_key().unwrap();
-        check(
-            descriptor!(sh(wsh(multi(1, desc_key1, desc_key2)))),
-            true,
-            false,
-            &[
-                "2NFCtXvx9q4ci2kvKub17iSTgvRXGctCGhz",
-                "2NB2PrFPv5NxWCpygas8tPrGJG2ZFgeuwJw",
-                "2N79ZAGo5cMi5Jt7Wo9L5YmF5GkEw7sjWdC",
-            ],
-        );
-    }
-
-    #[test]
-    fn test_dsl_sortedmulti() {
-        let key_1 = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        let path_1 = bip32::DerivationPath::from_str("m/0").unwrap();
-
-        let key_2 = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF").unwrap();
-        let path_2 = bip32::DerivationPath::from_str("m/1").unwrap();
-
-        let desc_key1 = (key_1, path_1);
-        let desc_key2 = (key_2, path_2);
-
-        check(
-            descriptor!(sh(sortedmulti(1, desc_key1.clone(), desc_key2.clone()))),
-            false,
-            false,
-            &[
-                "2MsxzPEJDBzpGffJXPaDpfXZAUNnZhaMh2N",
-                "2My3x3DLPK3UbGWGpxrXr1RnbD8MNC4FpgS",
-                "2NByEuiQT7YLqHCTNxL5KwYjvtuCYcXNBSC",
-                "2N1TGbP81kj2VUKTSWgrwxoMfuWjvfUdyu7",
-                "2N3Bomq2fpAcLRNfZnD3bCWK9quan28CxCR",
-                "2N9nrZaEzEFDqEAU9RPvDnXGT6AVwBDKAQb",
-            ],
-        );
-
-        check(
-            descriptor!(sh(wsh(sortedmulti(
-                1,
-                desc_key1.clone(),
-                desc_key2.clone()
-            )))),
-            true,
-            false,
-            &[
-                "2NCogc5YyM4N6ruv1hUa7WLMW1BPeCK7N9B",
-                "2N6mkSAKi1V2oaBXby7XHdvBMKEDRQcFpNe",
-                "2NFmTSttm9v6bXeoWaBvpMcgfPQcZhNn3Eh",
-                "2Mvib87RBPUHXNEpX5S5Kv1qqrhBfgBGsJM",
-                "2MtMv5mcK2EjcLsH8Txpx2JxLLzHr4ttczL",
-                "2MsWCB56rb4T6yPv8QudZGHERTwNgesE4f6",
-            ],
-        );
-
-        check(
-            descriptor!(wsh(sortedmulti_vec(1, vec![desc_key1, desc_key2]))),
-            true,
-            false,
-            &[
-                "bcrt1qcvq0lg8q7a47ytrd7zk5y7uls7mulrenjgvflwylpppgwf8029es4vhpnj",
-                "bcrt1q80yn8sdt6l7pjvkz25lglyaqctlmsq9ugk80rmxt8yu0npdsj97sc7l4de",
-                "bcrt1qrvf6024v9s50qhffe3t2fr2q9ckdhx2g6jz32chm2pp24ymgtr5qfrdmct",
-                "bcrt1q6srfmra0ynypym35c7jvsxt2u4yrugeajq95kg2ps7lk6h2gaunsq9lzxn",
-                "bcrt1qhl8rrzzcdpu7tcup3lcg7tge52sqvwy5fcv4k78v6kxtwmqf3v6qpvyjza",
-                "bcrt1ql2elz9mhm9ll27ddpewhxs732xyl2fk2kpkqz9gdyh33wgcun4vstrd49k",
-            ],
-        );
-    }
-
-    // - verify the valid_networks returned is correctly computed based on the keys present in the descriptor
-    #[test]
-    fn test_valid_networks() {
-        let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        let path = bip32::DerivationPath::from_str("m/0").unwrap();
-        let desc_key = (xprv, path).into_descriptor_key().unwrap();
-
-        let (_desc, _key_map, valid_networks) = descriptor!(pkh(desc_key)).unwrap();
-        assert_eq!(
-            valid_networks,
-            [Testnet, Testnet4, Regtest, Signet]
-                .iter()
-                .cloned()
-                .collect()
-        );
-
-        let xprv = bip32::Xpriv::from_str("xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi").unwrap();
-        let path = bip32::DerivationPath::from_str("m/10/20/30/40").unwrap();
-        let desc_key = (xprv, path).into_descriptor_key().unwrap();
-
-        let (_desc, _key_map, valid_networks) = descriptor!(wpkh(desc_key)).unwrap();
-        assert_eq!(valid_networks, [Bitcoin].iter().cloned().collect());
-    }
-
-    // - verify the key_maps are correctly merged together
-    #[test]
-    fn test_key_maps_merged() {
-        let secp = Secp256k1::new();
-
-        let xprv1 = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        let path1 = bip32::DerivationPath::from_str("m/0").unwrap();
-        let desc_key1 = (xprv1, path1.clone()).into_descriptor_key().unwrap();
-
-        let xprv2 = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF").unwrap();
-        let path2 = bip32::DerivationPath::from_str("m/2147483647'/0").unwrap();
-        let desc_key2 = (xprv2, path2.clone()).into_descriptor_key().unwrap();
-
-        let xprv3 = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf").unwrap();
-        let path3 = bip32::DerivationPath::from_str("m/10/20/30/40").unwrap();
-        let desc_key3 = (xprv3, path3.clone()).into_descriptor_key().unwrap();
-
-        let (_desc, key_map, _valid_networks) =
-            descriptor!(sh(wsh(multi(2, desc_key1, desc_key2, desc_key3)))).unwrap();
-        assert_eq!(key_map.len(), 3);
-
-        let desc_key1: DescriptorKey<Segwitv0> = (xprv1, path1).into_descriptor_key().unwrap();
-        let desc_key2: DescriptorKey<Segwitv0> = (xprv2, path2).into_descriptor_key().unwrap();
-        let desc_key3: DescriptorKey<Segwitv0> = (xprv3, path3).into_descriptor_key().unwrap();
-
-        let (key1, _key_map, _valid_networks) = desc_key1.extract(&secp).unwrap();
-        let (key2, _key_map, _valid_networks) = desc_key2.extract(&secp).unwrap();
-        let (key3, _key_map, _valid_networks) = desc_key3.extract(&secp).unwrap();
-        assert_eq!(key_map.get(&key1).unwrap().to_string(), "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/0/*");
-        assert_eq!(key_map.get(&key2).unwrap().to_string(), "tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF/2147483647'/0/*");
-        assert_eq!(key_map.get(&key3).unwrap().to_string(), "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf/10/20/30/40/*");
-    }
-
-    // - verify the ScriptContext is correctly validated (i.e. passing a type that only impl IntoDescriptorKey<Segwitv0> to a pkh() descriptor should throw a compilation error
-    #[test]
-    fn test_script_context_validation() {
-        // this compiles
-        let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        let path = bip32::DerivationPath::from_str("m/0").unwrap();
-        let desc_key: DescriptorKey<Legacy> = (xprv, path).into_descriptor_key().unwrap();
-
-        let (desc, _key_map, _valid_networks) = descriptor!(pkh(desc_key)).unwrap();
-        assert_eq!(desc.to_string(), "pkh(tpubD6NzVbkrYhZ4WR7a4vY1VT3khMJMeAxVsfq9TBJyJWrNk247zCJtV7AWf6UJP7rAVsn8NNKdJi3gFyKPTmWZS9iukb91xbn2HbFSMQm2igY/0/*)#yrnz9pp2");
-
-        // as expected this does not compile due to invalid context
-        //let desc_key:DescriptorKey<Segwitv0> = (xprv, path.clone()).into_descriptor_key().unwrap();
-        //let (desc, _key_map, _valid_networks) = descriptor!(pkh(desc_key)).unwrap();
-    }
-
-    #[test]
-    fn test_dsl_modifiers() {
-        let private_key =
-            PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
-        let (descriptor, _, _) =
-            descriptor!(wsh(thresh(2,n:d:v:older(1),s:pk(private_key),s:pk(private_key)))).unwrap();
-
-        assert_eq!(descriptor.to_string(), "wsh(thresh(2,ndv:older(1),s:pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c),s:pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)))#zzk3ux8g")
-    }
-
-    #[test]
-    #[should_panic(expected = "Miniscript(ContextError(UncompressedKeysNotAllowed))")]
-    fn test_dsl_miniscript_checks() {
-        let mut uncompressed_pk =
-            PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();
-        uncompressed_pk.compressed = false;
-
-        descriptor!(wsh(v: pk(uncompressed_pk))).unwrap();
-    }
-
-    #[test]
-    fn test_dsl_tr_only_key() {
-        let private_key =
-            PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
-        let (descriptor, _, _) = descriptor!(tr(private_key)).unwrap();
-
-        assert_eq!(
-            descriptor.to_string(),
-            "tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)#heq9m95v"
-        )
-    }
-
-    #[test]
-    fn test_dsl_tr_simple_tree() {
-        let private_key =
-            PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
-        let (descriptor, _, _) =
-            descriptor!(tr(private_key, { pk(private_key), pk(private_key) })).unwrap();
-
-        assert_eq!(descriptor.to_string(), "tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c,{pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c),pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)})#xy5fjw6d")
-    }
-
-    #[test]
-    fn test_dsl_tr_single_leaf() {
-        let private_key =
-            PrivateKey::from_wif("cSQPHDBwXGjVzWRqAHm6zfvQhaTuj1f2bFH58h55ghbjtFwvmeXR").unwrap();
-        let (descriptor, _, _) = descriptor!(tr(private_key, pk(private_key))).unwrap();
-
-        assert_eq!(descriptor.to_string(), "tr(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c,pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c))#lzl2vmc7")
-    }
-}
diff --git a/crates/wallet/src/descriptor/error.rs b/crates/wallet/src/descriptor/error.rs
deleted file mode 100644 (file)
index e018b53..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptor errors
-use core::fmt;
-
-/// Errors related to the parsing and usage of descriptors
-#[derive(Debug, PartialEq)]
-pub enum Error {
-    /// Invalid HD Key path, such as having a wildcard but a length != 1
-    InvalidHdKeyPath,
-    /// The provided descriptor doesn't match its checksum
-    InvalidDescriptorChecksum,
-    /// The descriptor contains hardened derivation steps on public extended keys
-    HardenedDerivationXpub,
-    /// The descriptor contains multipath keys
-    MultiPath,
-    /// Error thrown while working with [`keys`](crate::keys)
-    Key(crate::keys::KeyError),
-    /// Error while extracting and manipulating policies
-    Policy(crate::descriptor::policy::PolicyError),
-
-    /// Invalid byte found in the descriptor checksum
-    InvalidDescriptorCharacter(u8),
-
-    /// BIP32 error
-    Bip32(bitcoin::bip32::Error),
-    /// Error during base58 decoding
-    Base58(bitcoin::base58::Error),
-    /// Key-related error
-    Pk(bitcoin::key::ParsePublicKeyError),
-    /// Miniscript error
-    Miniscript(miniscript::Error),
-    /// Hex decoding error
-    Hex(bitcoin::hex::HexToBytesError),
-    /// The provided wallet descriptors are identical
-    ExternalAndInternalAreTheSame,
-}
-
-impl From<crate::keys::KeyError> for Error {
-    fn from(key_error: crate::keys::KeyError) -> Error {
-        match key_error {
-            crate::keys::KeyError::Miniscript(inner) => Error::Miniscript(inner),
-            crate::keys::KeyError::Bip32(inner) => Error::Bip32(inner),
-            e => Error::Key(e),
-        }
-    }
-}
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::InvalidHdKeyPath => write!(f, "Invalid HD key path"),
-            Self::InvalidDescriptorChecksum => {
-                write!(f, "The provided descriptor doesn't match its checksum")
-            }
-            Self::HardenedDerivationXpub => write!(
-                f,
-                "The descriptor contains hardened derivation steps on public extended keys"
-            ),
-            Self::MultiPath => write!(
-                f,
-                "The descriptor contains multipath keys, which are not supported yet"
-            ),
-            Self::Key(err) => write!(f, "Key error: {}", err),
-            Self::Policy(err) => write!(f, "Policy error: {}", err),
-            Self::InvalidDescriptorCharacter(char) => {
-                write!(f, "Invalid descriptor character: {}", char)
-            }
-            Self::Bip32(err) => write!(f, "BIP32 error: {}", err),
-            Self::Base58(err) => write!(f, "Base58 error: {}", err),
-            Self::Pk(err) => write!(f, "Key-related error: {}", err),
-            Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
-            Self::Hex(err) => write!(f, "Hex decoding error: {}", err),
-            Self::ExternalAndInternalAreTheSame => {
-                write!(f, "External and internal descriptors are the same")
-            }
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for Error {}
-
-impl From<bitcoin::bip32::Error> for Error {
-    fn from(err: bitcoin::bip32::Error) -> Self {
-        Error::Bip32(err)
-    }
-}
-
-impl From<bitcoin::base58::Error> for Error {
-    fn from(err: bitcoin::base58::Error) -> Self {
-        Error::Base58(err)
-    }
-}
-
-impl From<bitcoin::key::ParsePublicKeyError> for Error {
-    fn from(err: bitcoin::key::ParsePublicKeyError) -> Self {
-        Error::Pk(err)
-    }
-}
-
-impl From<miniscript::Error> for Error {
-    fn from(err: miniscript::Error) -> Self {
-        Error::Miniscript(err)
-    }
-}
-
-impl From<bitcoin::hex::HexToBytesError> for Error {
-    fn from(err: bitcoin::hex::HexToBytesError) -> Self {
-        Error::Hex(err)
-    }
-}
-
-impl From<crate::descriptor::policy::PolicyError> for Error {
-    fn from(err: crate::descriptor::policy::PolicyError) -> Self {
-        Error::Policy(err)
-    }
-}
diff --git a/crates/wallet/src/descriptor/mod.rs b/crates/wallet/src/descriptor/mod.rs
deleted file mode 100644 (file)
index f5ef595..0000000
+++ /dev/null
@@ -1,917 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptors
-//!
-//! This module contains generic utilities to work with descriptors, plus some re-exported types
-//! from [`miniscript`].
-
-use crate::collections::BTreeMap;
-use alloc::string::String;
-use alloc::vec::Vec;
-
-use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, KeySource, Xpub};
-use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
-use bitcoin::{psbt, taproot};
-use bitcoin::{Network, TxOut};
-
-use miniscript::descriptor::{
-    DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
-    DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
-};
-pub use miniscript::{
-    Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
-};
-use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
-
-use crate::descriptor::policy::BuildSatisfaction;
-
-pub mod checksum;
-#[doc(hidden)]
-pub mod dsl;
-pub mod error;
-pub mod policy;
-pub mod template;
-
-pub use self::checksum::calc_checksum;
-pub use self::error::Error as DescriptorError;
-pub use self::policy::Policy;
-use self::template::DescriptorTemplateOut;
-use crate::keys::{IntoDescriptorKey, KeyError};
-use crate::wallet::signer::SignersContainer;
-use crate::wallet::utils::SecpCtx;
-
-/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
-pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
-
-/// Alias for a [`Descriptor`] that contains extended **derived** keys
-pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
-
-/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
-/// [`psbt::Output`]
-///
-/// [`psbt::Input`]: bitcoin::psbt::Input
-/// [`psbt::Output`]: bitcoin::psbt::Output
-pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
-
-/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
-/// [`psbt::Output`]
-///
-/// [`psbt::Input`]: bitcoin::psbt::Input
-/// [`psbt::Output`]: bitcoin::psbt::Output
-pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
-
-/// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
-pub trait IntoWalletDescriptor {
-    /// Convert to wallet descriptor
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
-}
-
-impl IntoWalletDescriptor for &str {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        let descriptor = match self.split_once('#') {
-            Some((desc, original_checksum)) => {
-                let checksum = calc_checksum(desc)?;
-                if original_checksum != checksum {
-                    return Err(DescriptorError::InvalidDescriptorChecksum);
-                }
-                desc
-            }
-            None => self,
-        };
-
-        ExtendedDescriptor::parse_descriptor(secp, descriptor)?
-            .into_wallet_descriptor(secp, network)
-    }
-}
-
-impl IntoWalletDescriptor for &String {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        self.as_str().into_wallet_descriptor(secp, network)
-    }
-}
-
-impl IntoWalletDescriptor for String {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        self.as_str().into_wallet_descriptor(secp, network)
-    }
-}
-
-impl IntoWalletDescriptor for ExtendedDescriptor {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        (self, KeyMap::default()).into_wallet_descriptor(secp, network)
-    }
-}
-
-impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        use crate::keys::DescriptorKey;
-
-        struct Translator<'s, 'd> {
-            secp: &'s SecpCtx,
-            descriptor: &'d ExtendedDescriptor,
-            network: Network,
-        }
-
-        impl miniscript::Translator<DescriptorPublicKey, String, DescriptorError> for Translator<'_, '_> {
-            fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
-                let secp = &self.secp;
-
-                let (_, _, networks) = if self.descriptor.is_taproot() {
-                    let descriptor_key: DescriptorKey<miniscript::Tap> =
-                        pk.clone().into_descriptor_key()?;
-                    descriptor_key.extract(secp)?
-                } else if self.descriptor.is_witness() {
-                    let descriptor_key: DescriptorKey<miniscript::Segwitv0> =
-                        pk.clone().into_descriptor_key()?;
-                    descriptor_key.extract(secp)?
-                } else {
-                    let descriptor_key: DescriptorKey<miniscript::Legacy> =
-                        pk.clone().into_descriptor_key()?;
-                    descriptor_key.extract(secp)?
-                };
-
-                if networks.contains(&self.network) {
-                    Ok(Default::default())
-                } else {
-                    Err(DescriptorError::Key(KeyError::InvalidNetwork))
-                }
-            }
-            fn sha256(
-                &mut self,
-                _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
-            ) -> Result<String, DescriptorError> {
-                Ok(Default::default())
-            }
-            fn hash256(
-                &mut self,
-                _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
-            ) -> Result<String, DescriptorError> {
-                Ok(Default::default())
-            }
-            fn ripemd160(
-                &mut self,
-                _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
-            ) -> Result<String, DescriptorError> {
-                Ok(Default::default())
-            }
-            fn hash160(
-                &mut self,
-                _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
-            ) -> Result<String, DescriptorError> {
-                Ok(Default::default())
-            }
-        }
-
-        // check the network for the keys
-        use miniscript::TranslateErr;
-        match self.0.translate_pk(&mut Translator {
-            secp,
-            network,
-            descriptor: &self.0,
-        }) {
-            Ok(_) => {}
-            Err(TranslateErr::TranslatorErr(e)) => return Err(e),
-            Err(TranslateErr::OuterError(e)) => return Err(e.into()),
-        }
-
-        Ok(self)
-    }
-}
-
-impl IntoWalletDescriptor for DescriptorTemplateOut {
-    fn into_wallet_descriptor(
-        self,
-        _secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        struct Translator {
-            network: Network,
-        }
-
-        impl miniscript::Translator<DescriptorPublicKey, DescriptorPublicKey, DescriptorError>
-            for Translator
-        {
-            fn pk(
-                &mut self,
-                pk: &DescriptorPublicKey,
-            ) -> Result<DescriptorPublicKey, DescriptorError> {
-                // workaround for xpubs generated by other key types, like bip39: since when the
-                // conversion is made one network has to be chosen, what we generally choose
-                // "mainnet", but then override the set of valid networks to specify that all of
-                // them are valid. here we reset the network to make sure the wallet struct gets a
-                // descriptor with the right network everywhere.
-                let pk = match pk {
-                    DescriptorPublicKey::XPub(ref xpub) => {
-                        let mut xpub = xpub.clone();
-                        xpub.xkey.network = self.network.into();
-
-                        DescriptorPublicKey::XPub(xpub)
-                    }
-                    other => other.clone(),
-                };
-
-                Ok(pk)
-            }
-            miniscript::translate_hash_clone!(
-                DescriptorPublicKey,
-                DescriptorPublicKey,
-                DescriptorError
-            );
-        }
-
-        let (desc, keymap, networks) = self;
-
-        if !networks.contains(&network) {
-            return Err(DescriptorError::Key(KeyError::InvalidNetwork));
-        }
-
-        // fixup the network for keys that need it in the descriptor
-        use miniscript::TranslateErr;
-        let translated = match desc.translate_pk(&mut Translator { network }) {
-            Ok(descriptor) => descriptor,
-            Err(TranslateErr::TranslatorErr(e)) => return Err(e),
-            Err(TranslateErr::OuterError(e)) => return Err(e.into()),
-        };
-        // ...and in the key map
-        let fixed_keymap = keymap
-            .into_iter()
-            .map(|(mut k, mut v)| {
-                match (&mut k, &mut v) {
-                    (DescriptorPublicKey::XPub(xpub), DescriptorSecretKey::XPrv(xprv)) => {
-                        xpub.xkey.network = network.into();
-                        xprv.xkey.network = network.into();
-                    }
-                    (_, DescriptorSecretKey::Single(key)) => {
-                        key.key.network = network.into();
-                    }
-                    _ => {}
-                }
-
-                (k, v)
-            })
-            .collect();
-
-        Ok((translated, fixed_keymap))
-    }
-}
-
-/// Extra checks for [`ExtendedDescriptor`].
-pub(crate) fn check_wallet_descriptor(
-    descriptor: &Descriptor<DescriptorPublicKey>,
-) -> Result<(), DescriptorError> {
-    // Ensure the keys don't contain any hardened derivation steps or hardened wildcards
-    let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
-        if let DescriptorPublicKey::XPub(DescriptorXKey {
-            derivation_path,
-            wildcard,
-            ..
-        }) = k
-        {
-            return *wildcard == Wildcard::Hardened
-                || derivation_path.into_iter().any(ChildNumber::is_hardened);
-        }
-
-        false
-    });
-    if descriptor_contains_hardened_steps {
-        return Err(DescriptorError::HardenedDerivationXpub);
-    }
-
-    if descriptor.is_multipath() {
-        return Err(DescriptorError::MultiPath);
-    }
-
-    // Run miniscript's sanity check, which will look for duplicated keys and other potential
-    // issues
-    descriptor.sanity_check()?;
-
-    Ok(())
-}
-
-#[doc(hidden)]
-/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
-pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
-    fn check_miniscript(&self) -> Result<(), miniscript::Error>;
-}
-
-impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
-    for miniscript::Miniscript<Pk, Ctx>
-{
-    fn check_miniscript(&self) -> Result<(), miniscript::Error> {
-        Ctx::check_global_validity(self)?;
-
-        Ok(())
-    }
-}
-
-/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
-pub trait ExtractPolicy {
-    /// Extract the spending [`policy`]
-    fn extract_policy(
-        &self,
-        signers: &SignersContainer,
-        psbt: BuildSatisfaction,
-        secp: &SecpCtx,
-    ) -> Result<Option<Policy>, DescriptorError>;
-}
-
-pub(crate) trait XKeyUtils {
-    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
-}
-
-impl<T> XKeyUtils for DescriptorMultiXKey<T>
-where
-    T: InnerXKey,
-{
-    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
-        match self.origin {
-            Some((fingerprint, _)) => fingerprint,
-            None => self.xkey.xkey_fingerprint(secp),
-        }
-    }
-}
-
-impl<T> XKeyUtils for DescriptorXKey<T>
-where
-    T: InnerXKey,
-{
-    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
-        match self.origin {
-            Some((fingerprint, _)) => fingerprint,
-            None => self.xkey.xkey_fingerprint(secp),
-        }
-    }
-}
-
-pub(crate) trait DescriptorMeta {
-    fn is_witness(&self) -> bool;
-    fn is_taproot(&self) -> bool;
-    fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>>;
-    fn derive_from_hd_keypaths(
-        &self,
-        hd_keypaths: &HdKeyPaths,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor>;
-    fn derive_from_tap_key_origins(
-        &self,
-        tap_key_origins: &TapKeyOrigins,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor>;
-    fn derive_from_psbt_key_origins(
-        &self,
-        key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor>;
-    fn derive_from_psbt_input(
-        &self,
-        psbt_input: &psbt::Input,
-        utxo: Option<TxOut>,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor>;
-}
-
-impl DescriptorMeta for ExtendedDescriptor {
-    fn is_witness(&self) -> bool {
-        matches!(
-            self.desc_type(),
-            DescriptorType::Wpkh
-                | DescriptorType::ShWpkh
-                | DescriptorType::Wsh
-                | DescriptorType::ShWsh
-                | DescriptorType::ShWshSortedMulti
-                | DescriptorType::WshSortedMulti
-        )
-    }
-
-    fn is_taproot(&self) -> bool {
-        self.desc_type() == DescriptorType::Tr
-    }
-
-    fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>> {
-        let mut answer = Vec::new();
-
-        self.for_each_key(|pk| {
-            if let DescriptorPublicKey::XPub(xpub) = pk {
-                answer.push(xpub.clone());
-            }
-
-            true
-        });
-
-        answer
-    }
-
-    fn derive_from_psbt_key_origins(
-        &self,
-        key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor> {
-        // Ensure that deriving `xpub` with `path` yields `expected`
-        let verify_key =
-            |xpub: &DescriptorXKey<Xpub>, path: &DerivationPath, expected: &SinglePubKey| {
-                let derived = xpub
-                    .xkey
-                    .derive_pub(secp, path)
-                    .expect("The path should never contain hardened derivation steps")
-                    .public_key;
-
-                match expected {
-                    SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true,
-                    SinglePubKey::XOnly(pk) if &XOnlyPublicKey::from(derived) == pk => true,
-                    _ => false,
-                }
-            };
-
-        let mut path_found = None;
-
-        // using `for_any_key` should make this stop as soon as we return `true`
-        self.for_any_key(|key| {
-            if let DescriptorPublicKey::XPub(xpub) = key {
-                // Check if the key matches one entry in our `key_origins`. If it does, `matches()` will
-                // return the "prefix" that matched, so we remove that prefix from the full path
-                // found in `key_origins` and save it in `derive_path`. We expect this to be a derivation
-                // path of length 1 if the key is `wildcard` and an empty path otherwise.
-                let root_fingerprint = xpub.root_fingerprint(secp);
-                let derive_path = key_origins
-                    .get_key_value(&root_fingerprint)
-                    .and_then(|(fingerprint, (path, expected))| {
-                        xpub.matches(&(*fingerprint, (*path).clone()), secp)
-                            .zip(Some((path, expected)))
-                    })
-                    .and_then(|(prefix, (full_path, expected))| {
-                        let derive_path = full_path
-                            .into_iter()
-                            .skip(prefix.into_iter().count())
-                            .cloned()
-                            .collect::<DerivationPath>();
-
-                        // `derive_path` only contains the replacement index for the wildcard, if present, or
-                        // an empty path for fixed descriptors. To verify the key we also need the normal steps
-                        // that come before the wildcard, so we take them directly from `xpub` and then append
-                        // the final index
-                        if verify_key(
-                            xpub,
-                            &xpub.derivation_path.extend(derive_path.clone()),
-                            expected,
-                        ) {
-                            Some(derive_path)
-                        } else {
-                            None
-                        }
-                    });
-
-                match derive_path {
-                    Some(path) if xpub.wildcard != Wildcard::None && path.len() == 1 => {
-                        // Ignore hardened wildcards
-                        if let ChildNumber::Normal { index } = path[0] {
-                            path_found = Some(index);
-                            return true;
-                        }
-                    }
-                    Some(path) if xpub.wildcard == Wildcard::None && path.is_empty() => {
-                        path_found = Some(0);
-                        return true;
-                    }
-                    _ => {}
-                }
-            }
-
-            false
-        });
-
-        path_found.map(|path| {
-            self.at_derivation_index(path)
-                .expect("We ignore hardened wildcards")
-        })
-    }
-
-    fn derive_from_hd_keypaths(
-        &self,
-        hd_keypaths: &HdKeyPaths,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor> {
-        // "Convert" an hd_keypaths map to the format required by `derive_from_psbt_key_origins`
-        let key_origins = hd_keypaths
-            .iter()
-            .map(|(pk, (fingerprint, path))| {
-                (
-                    *fingerprint,
-                    (path, SinglePubKey::FullKey(PublicKey::new(*pk))),
-                )
-            })
-            .collect();
-        self.derive_from_psbt_key_origins(key_origins, secp)
-    }
-
-    fn derive_from_tap_key_origins(
-        &self,
-        tap_key_origins: &TapKeyOrigins,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor> {
-        // "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins`
-        let key_origins = tap_key_origins
-            .iter()
-            .map(|(pk, (_, (fingerprint, path)))| (*fingerprint, (path, SinglePubKey::XOnly(*pk))))
-            .collect();
-        self.derive_from_psbt_key_origins(key_origins, secp)
-    }
-
-    fn derive_from_psbt_input(
-        &self,
-        psbt_input: &psbt::Input,
-        utxo: Option<TxOut>,
-        secp: &SecpCtx,
-    ) -> Option<DerivedDescriptor> {
-        if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.bip32_derivation, secp) {
-            return Some(derived);
-        }
-        if let Some(derived) = self.derive_from_tap_key_origins(&psbt_input.tap_key_origins, secp) {
-            return Some(derived);
-        }
-        if self.has_wildcard() {
-            // We can't try to bruteforce the derivation index, exit here
-            return None;
-        }
-
-        let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
-        match descriptor.desc_type() {
-            // TODO: add pk() here
-            DescriptorType::Pkh
-            | DescriptorType::Wpkh
-            | DescriptorType::ShWpkh
-            | DescriptorType::Tr
-                if utxo.is_some()
-                    && descriptor.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
-            {
-                Some(descriptor)
-            }
-            DescriptorType::Bare | DescriptorType::Sh | DescriptorType::ShSortedMulti
-                if psbt_input.redeem_script.is_some()
-                    && &descriptor.explicit_script().unwrap()
-                        == psbt_input.redeem_script.as_ref().unwrap() =>
-            {
-                Some(descriptor)
-            }
-            DescriptorType::Wsh
-            | DescriptorType::ShWsh
-            | DescriptorType::ShWshSortedMulti
-            | DescriptorType::WshSortedMulti
-                if psbt_input.witness_script.is_some()
-                    && &descriptor.explicit_script().unwrap()
-                        == psbt_input.witness_script.as_ref().unwrap() =>
-            {
-                Some(descriptor)
-            }
-            _ => None,
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use alloc::string::ToString;
-    use core::str::FromStr;
-
-    use assert_matches::assert_matches;
-    use bitcoin::hex::FromHex;
-    use bitcoin::secp256k1::Secp256k1;
-    use bitcoin::{bip32, Psbt};
-    use bitcoin::{NetworkKind, ScriptBuf};
-
-    use super::*;
-    use crate::psbt::PsbtUtils;
-
-    #[test]
-    fn test_derive_from_psbt_input_wpkh_wif() {
-        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
-            "wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
-        )
-        .unwrap();
-        let psbt = Psbt::deserialize(
-            &Vec::<u8>::from_hex(
-                "70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
-                 11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
-                 001493ce48570b55c42c2af816aeaba06cfee1224fae000000000001011fa086\
-                 01000000000016001493ce48570b55c42c2af816aeaba06cfee1224fae010304\
-                 010000000000",
-            )
-            .unwrap(),
-        )
-        .unwrap();
-
-        assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
-            .is_some());
-    }
-
-    #[test]
-    fn test_derive_from_psbt_input_pkh_tpub() {
-        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
-            "pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
-        )
-        .unwrap();
-        let psbt = Psbt::deserialize(
-            &Vec::<u8>::from_hex(
-                "70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
-                 e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
-                 a91432bb94283282f72b2e034709e348c44d5a4db0ef8700000000000100f902\
-                 0000000001010167e99c0eb67640f3a1b6805f2d8be8238c947f8aaf49eb0a9c\
-                 bee6a42c984200000000171600142b29a22019cca05b9c2b2d283a4c4489e1cf\
-                 9f8ffeffffff02a01dced06100000017a914e2abf033cadbd74f0f4c74946201\
-                 decd20d5c43c8780969800000000001976a9148b0fce5fb1264e599a65387313\
-                 3c95478b902eb288ac02473044022015d9211576163fa5b001e84dfa3d44efd9\
-                 86b8f3a0d3d2174369288b2b750906022048dacc0e5d73ae42512fd2b97e2071\
-                 a8d0bce443b390b1fe0b8128fe70ec919e01210232dad1c5a67dcb0116d407e2\
-                 52584228ab7ec00e8b9779d0c3ffe8114fc1a7d2c80600000103040100000022\
-                 0603433b83583f8c4879b329dd08bbc7da935e4cc02f637ff746e05f0466ffb2\
-                 a6a2180f0569432c00008000000080000000800a000000000000000000",
-            )
-            .unwrap(),
-        )
-        .unwrap();
-
-        assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
-            .is_some());
-    }
-
-    #[test]
-    fn test_derive_from_psbt_input_wsh() {
-        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
-            "wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
-        )
-        .unwrap();
-        let psbt = Psbt::deserialize(
-            &Vec::<u8>::from_hex(
-                "70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
-                 67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
-                 a914ad105f61102e0d01d7af40d06d6a5c3ae2f7fde387000000000001012b80\
-                 969800000000002200203ca72f106a72234754890ca7640c43f65d2174e44d33\
-                 336030f9059345091044010304010000000105252103b6633fef2397a0a9de9d\
-                 7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14ad56b20000",
-            )
-            .unwrap(),
-        )
-        .unwrap();
-
-        assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
-            .is_some());
-    }
-
-    #[test]
-    fn test_derive_from_psbt_input_sh() {
-        let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
-            "sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
-        )
-        .unwrap();
-        let psbt = Psbt::deserialize(
-            &Vec::<u8>::from_hex(
-                "70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
-                 a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
-                 a91457b148ba4d3e5fa8608a8657875124e3d1c9390887f09c0900000100e002\
-                 0000000001016ba1bbe05cc93574a0d611ec7d93ad0ab6685b28d0cd80e8a82d\
-                 debb326643c90100000000feffffff02809698000000000017a914d9a6e8c455\
-                 8e16c8253afe53ce37ad61cf4c38c487403504cf6100000017a9144044fb6e0b\
-                 757dfc1b34886b6a95aef4d3db137e870247304402202a9b72d939bcde8ba2a1\
-                 e0980597e47af4f5c152a78499143c3d0a78ac2286a602207a45b1df9e93b8c9\
-                 6f09f5c025fe3e413ca4b905fe65ee55d32a3276439a9b8f012102dc1fcc2636\
-                 4da1aa718f03d8d9bd6f2ff410ed2cf1245a168aa3bcc995ac18e0a806000001\
-                 03040100000001042821021403881a5587297818fcaf17d239cefca22fce84a4\
-                 5b3b1d23e836c4af671dbbad03f09c09b10000",
-            )
-            .unwrap(),
-        )
-        .unwrap();
-
-        assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
-            .is_some());
-    }
-
-    #[test]
-    fn test_to_wallet_descriptor_fixup_networks() {
-        use crate::keys::{any_network, IntoDescriptorKey};
-
-        let secp = Secp256k1::new();
-
-        let xprv = bip32::Xpriv::from_str("xprv9s21ZrQH143K3c3gF1DUWpWNr2SG2XrG8oYPpqYh7hoWsJy9NjabErnzriJPpnGHyKz5NgdXmq1KVbqS1r4NXdCoKitWg5e86zqXHa8kxyB").unwrap();
-        let path = bip32::DerivationPath::from_str("m/0").unwrap();
-
-        // here `to_descriptor_key` will set the valid networks for the key to only mainnet, since
-        // we are using an "xpub"
-        let key = (xprv, path.clone()).into_descriptor_key().unwrap();
-        // override it with any. this happens in some key conversions, like bip39
-        let key = key.override_valid_networks(any_network());
-
-        // make a descriptor out of it
-        let desc = crate::descriptor!(wpkh(key)).unwrap();
-        // this should convert the key that supports "any_network" to the right network (testnet)
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-
-        let mut xprv_testnet = xprv;
-        xprv_testnet.network = NetworkKind::Test;
-
-        let xpub_testnet = bip32::Xpub::from_priv(&secp, &xprv_testnet);
-        let desc_pubkey = DescriptorPublicKey::XPub(DescriptorXKey {
-            xkey: xpub_testnet,
-            origin: None,
-            derivation_path: path,
-            wildcard: Wildcard::Unhardened,
-        });
-
-        assert_eq!(wallet_desc.to_string(), "wpkh(tpubD6NzVbkrYhZ4XtJzoDja5snUjBNQRP5B3f4Hyn1T1x6PVPxzzVjvw6nJx2D8RBCxog9GEVjZoyStfepTz7TtKoBVdkCtnc7VCJh9dD4RAU9/0/*)#a3svx0ha");
-        assert_eq!(
-            keymap
-                .get(&desc_pubkey)
-                .map(|key| key.to_public(&secp).unwrap()),
-            Some(desc_pubkey)
-        );
-    }
-
-    // test IntoWalletDescriptor trait from &str with and without checksum appended
-    #[test]
-    fn test_descriptor_from_str_with_checksum() {
-        let secp = Secp256k1::new();
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc62"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
-    }
-
-    // test IntoWalletDescriptor trait from &str with keys from right and wrong network
-    #[test]
-    fn test_descriptor_from_str_with_keys_network() {
-        let secp = Secp256k1::new();
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Testnet4);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Regtest);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Regtest);
-        assert!(desc.is_ok());
-
-        let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
-            .into_wallet_descriptor(&secp, Network::Testnet);
-        assert!(desc.is_ok());
-
-        let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
-            .into_wallet_descriptor(&secp, Network::Bitcoin);
-        assert!(desc.is_ok());
-
-        let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Bitcoin);
-        assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
-
-        let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
-            .into_wallet_descriptor(&secp, Network::Bitcoin);
-        assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
-    }
-
-    // test IntoWalletDescriptor trait from the output of the descriptor!() macro
-    #[test]
-    fn test_descriptor_from_str_from_output_of_macro() {
-        let secp = Secp256k1::new();
-
-        let tpub = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK").unwrap();
-        let path = bip32::DerivationPath::from_str("m/1/2").unwrap();
-        let key = (tpub, path).into_descriptor_key().unwrap();
-
-        // make a descriptor out of it
-        let desc = crate::descriptor!(wpkh(key)).unwrap();
-
-        let (wallet_desc, _) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let wallet_desc_str = wallet_desc.to_string();
-        assert_eq!(wallet_desc_str, "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw");
-
-        let (wallet_desc2, _) = wallet_desc_str
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        assert_eq!(wallet_desc, wallet_desc2)
-    }
-
-    #[test]
-    fn test_check_wallet_descriptor() {
-        let secp = Secp256k1::new();
-
-        let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
-        let (descriptor, _) = descriptor
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .expect("must parse");
-        let result = check_wallet_descriptor(&descriptor);
-
-        assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
-
-        let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/<0;1>/*)";
-        let (descriptor, _) = descriptor
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .expect("must parse");
-        let result = check_wallet_descriptor(&descriptor);
-
-        assert_matches!(result, Err(DescriptorError::MultiPath));
-
-        // repeated pubkeys
-        let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
-        let (descriptor, _) = descriptor
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .expect("must parse");
-        let result = check_wallet_descriptor(&descriptor);
-
-        assert!(result.is_err());
-    }
-
-    #[test]
-    fn test_sh_wsh_sortedmulti_redeemscript() {
-        use miniscript::psbt::PsbtInputExt;
-
-        let secp = Secp256k1::new();
-
-        let descriptor = "sh(wsh(sortedmulti(3,tpubDEsqS36T4DVsKJd9UH8pAKzrkGBYPLEt9jZMwpKtzh1G6mgYehfHt9WCgk7MJG5QGSFWf176KaBNoXbcuFcuadAFKxDpUdMDKGBha7bY3QM/0/*,tpubDF3cpwfs7fMvXXuoQbohXtLjNM6ehwYT287LWtmLsd4r77YLg6MZg4vTETx5MSJ2zkfigbYWu31VA2Z2Vc1cZugCYXgS7FQu6pE8V6TriEH/0/*,tpubDE1SKfcW76Tb2AASv5bQWMuScYNAdoqLHoexw13sNDXwmUhQDBbCD3QAedKGLhxMrWQdMDKENzYtnXPDRvexQPNuDrLj52wAjHhNEm8sJ4p/0/*,tpubDFLc6oXwJmhm3FGGzXkfJNTh2KitoY3WhmmQvuAjMhD8YbyWn5mAqckbxXfm2etM3p5J6JoTpSrMqRSTfMLtNW46poDaEZJ1kjd3csRSjwH/0/*,tpubDEWD9NBeWP59xXmdqSNt4VYdtTGwbpyP8WS962BuqpQeMZmX9Pur14dhXdZT5a7wR1pK6dPtZ9fP5WR493hPzemnBvkfLLYxnUjAKj1JCQV/0/*,tpubDEHyZkkwd7gZWCTgQuYQ9C4myF2hMEmyHsBCCmLssGqoqUxeT3gzohF5uEVURkf9TtmeepJgkSUmteac38FwZqirjApzNX59XSHLcwaTZCH/0/*,tpubDEqLouCekwnMUWN486kxGzD44qVgeyuqHyxUypNEiQt5RnUZNJe386TKPK99fqRV1vRkZjYAjtXGTECz98MCsdLcnkM67U6KdYRzVubeCgZ/0/*)))";
-        let (descriptor, _) = descriptor
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        check_wallet_descriptor(&descriptor).expect("descriptor");
-
-        let descriptor = descriptor.at_derivation_index(0).unwrap();
-
-        let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
-
-        let mut psbt_input = psbt::Input::default();
-        psbt_input
-            .update_with_descriptor_unchecked(&descriptor)
-            .unwrap();
-
-        assert_eq!(psbt_input.redeem_script, Some(script.to_p2wsh()));
-        assert_eq!(psbt_input.witness_script, Some(script));
-    }
-}
diff --git a/crates/wallet/src/descriptor/policy.rs b/crates/wallet/src/descriptor/policy.rs
deleted file mode 100644 (file)
index 6fb4013..0000000
+++ /dev/null
@@ -1,1905 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptor policy
-//!
-//! This module implements the logic to extract and represent the spending policies of a descriptor
-//! in a more human-readable format.
-//!
-//! This is an **EXPERIMENTAL** feature, API and other major changes are expected.
-//!
-//! ## Example
-//!
-//! ```
-//! # use std::sync::Arc;
-//! # use bdk_wallet::descriptor::*;
-//! # use bdk_wallet::signer::*;
-//! # use bdk_wallet::bitcoin::secp256k1::Secp256k1;
-//! use bdk_wallet::descriptor::policy::BuildSatisfaction;
-//! let secp = Secp256k1::new();
-//! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
-//!
-//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(&secp, desc)?;
-//! println!("{:?}", extended_desc);
-//!
-//! let signers = Arc::new(SignersContainer::build(key_map, &extended_desc, &secp));
-//! let policy = extended_desc.extract_policy(&signers, BuildSatisfaction::None, &secp)?;
-//! println!("policy: {}", serde_json::to_string(&policy).unwrap());
-//! # Ok::<(), anyhow::Error>(())
-//! ```
-
-use crate::collections::{BTreeMap, HashSet, VecDeque};
-use alloc::string::String;
-use alloc::vec::Vec;
-use core::cmp::max;
-use miniscript::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
-
-use core::fmt;
-
-use serde::ser::SerializeMap;
-use serde::{Serialize, Serializer};
-
-use bitcoin::bip32::Fingerprint;
-use bitcoin::hashes::{hash160, ripemd160, sha256};
-use bitcoin::{absolute, key::XOnlyPublicKey, relative, PublicKey, Sequence};
-
-use miniscript::descriptor::{
-    DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
-};
-use miniscript::{hash256, Threshold};
-use miniscript::{
-    Descriptor, Miniscript, Satisfier, ScriptContext, SigType, Terminal, ToPublicKey,
-};
-
-use crate::descriptor::ExtractPolicy;
-use crate::keys::ExtScriptContext;
-use crate::wallet::signer::{SignerId, SignersContainer};
-use crate::wallet::utils::{After, Older, SecpCtx};
-
-use super::checksum::calc_checksum;
-use super::error::Error;
-use super::XKeyUtils;
-use bitcoin::psbt::{self, Psbt};
-use miniscript::psbt::PsbtInputSatisfier;
-
-/// A unique identifier for a key
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
-#[serde(rename_all = "snake_case")]
-pub enum PkOrF {
-    /// A legacy public key
-    Pubkey(PublicKey),
-    /// A x-only public key
-    XOnlyPubkey(XOnlyPublicKey),
-    /// An extended key fingerprint
-    Fingerprint(Fingerprint),
-}
-
-impl PkOrF {
-    fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
-        match k {
-            DescriptorPublicKey::Single(SinglePub {
-                key: SinglePubKey::FullKey(pk),
-                ..
-            }) => PkOrF::Pubkey(*pk),
-            DescriptorPublicKey::Single(SinglePub {
-                key: SinglePubKey::XOnly(pk),
-                ..
-            }) => PkOrF::XOnlyPubkey(*pk),
-            DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)),
-            DescriptorPublicKey::MultiXPub(multi) => {
-                PkOrF::Fingerprint(multi.root_fingerprint(secp))
-            }
-        }
-    }
-}
-
-/// An item that needs to be satisfied
-#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-#[serde(tag = "type", rename_all = "UPPERCASE")]
-pub enum SatisfiableItem {
-    // Leaves
-    /// ECDSA Signature for a raw public key
-    EcdsaSignature(PkOrF),
-    /// Schnorr Signature for a raw public key
-    SchnorrSignature(PkOrF),
-    /// SHA256 preimage hash
-    Sha256Preimage {
-        /// The digest value
-        hash: sha256::Hash,
-    },
-    /// Double SHA256 preimage hash
-    Hash256Preimage {
-        /// The digest value
-        hash: hash256::Hash,
-    },
-    /// RIPEMD160 preimage hash
-    Ripemd160Preimage {
-        /// The digest value
-        hash: ripemd160::Hash,
-    },
-    /// SHA256 then RIPEMD160 preimage hash
-    Hash160Preimage {
-        /// The digest value
-        hash: hash160::Hash,
-    },
-    /// Absolute timeclock timestamp
-    AbsoluteTimelock {
-        /// The timelock value
-        value: absolute::LockTime,
-    },
-    /// Relative timelock locktime
-    RelativeTimelock {
-        /// The timelock value
-        value: relative::LockTime,
-    },
-    /// Multi-signature public keys with threshold count
-    Multisig {
-        /// The raw public key or extended key fingerprint
-        keys: Vec<PkOrF>,
-        /// The required threshold count
-        threshold: usize,
-    },
-
-    // Complex item
-    /// Threshold items with threshold count
-    Thresh {
-        /// The policy items
-        items: Vec<Policy>,
-        /// The required threshold count
-        threshold: usize,
-    },
-}
-
-impl SatisfiableItem {
-    /// Returns whether the [`SatisfiableItem`] is a leaf item
-    pub fn is_leaf(&self) -> bool {
-        !matches!(
-            self,
-            SatisfiableItem::Thresh {
-                items: _,
-                threshold: _,
-            }
-        )
-    }
-
-    /// Returns a unique id for the [`SatisfiableItem`]
-    pub fn id(&self) -> String {
-        calc_checksum(&serde_json::to_string(self).expect("Failed to serialize a SatisfiableItem"))
-            .expect("Failed to compute a SatisfiableItem id")
-    }
-}
-
-fn combinations(vec: &[usize], size: usize) -> Vec<Vec<usize>> {
-    assert!(vec.len() >= size);
-
-    let mut answer = Vec::new();
-
-    let mut queue = VecDeque::new();
-    for (index, val) in vec.iter().enumerate() {
-        let mut new_vec = Vec::with_capacity(size);
-        new_vec.push(*val);
-        queue.push_back((index, new_vec));
-    }
-
-    while let Some((index, vals)) = queue.pop_front() {
-        if vals.len() >= size {
-            answer.push(vals);
-        } else {
-            for (new_index, val) in vec.iter().skip(index + 1).enumerate() {
-                let mut cloned = vals.clone();
-                cloned.push(*val);
-                queue.push_front((new_index, cloned));
-            }
-        }
-    }
-
-    answer
-}
-
-fn mix<T: Clone>(vec: Vec<Vec<T>>) -> Vec<Vec<T>> {
-    if vec.is_empty() || vec.iter().any(Vec::is_empty) {
-        return vec![];
-    }
-
-    let mut answer = Vec::new();
-    let size = vec.len();
-
-    let mut queue = VecDeque::new();
-    for i in &vec[0] {
-        let mut new_vec = Vec::with_capacity(size);
-        new_vec.push(i.clone());
-        queue.push_back(new_vec);
-    }
-
-    while let Some(vals) = queue.pop_front() {
-        if vals.len() >= size {
-            answer.push(vals);
-        } else {
-            let level = vals.len();
-            for i in &vec[level] {
-                let mut cloned = vals.clone();
-                cloned.push(i.clone());
-                queue.push_front(cloned);
-            }
-        }
-    }
-
-    answer
-}
-
-/// Type for a map of sets of [`Condition`] items keyed by each set's index
-pub type ConditionMap = BTreeMap<usize, HashSet<Condition>>;
-/// Type for a map of folded sets of [`Condition`] items keyed by a vector of the combined set's indexes
-pub type FoldedConditionMap = BTreeMap<Vec<usize>, HashSet<Condition>>;
-
-fn serialize_folded_cond_map<S>(
-    input_map: &FoldedConditionMap,
-    serializer: S,
-) -> Result<S::Ok, S::Error>
-where
-    S: Serializer,
-{
-    let mut map = serializer.serialize_map(Some(input_map.len()))?;
-    for (k, v) in input_map {
-        let k_string = format!("{:?}", k);
-        map.serialize_entry(&k_string, v)?;
-    }
-    map.end()
-}
-
-/// Represent if and how much a policy item is satisfied by the wallet's descriptor
-#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-#[serde(tag = "type", rename_all = "UPPERCASE")]
-pub enum Satisfaction {
-    /// Only a partial satisfaction of some kind of threshold policy
-    Partial {
-        /// Total number of items
-        n: usize,
-        /// Threshold
-        m: usize,
-        /// The items that can be satisfied by the descriptor or are satisfied in the PSBT
-        items: Vec<usize>,
-        #[serde(skip_serializing_if = "Option::is_none")]
-        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
-        sorted: Option<bool>,
-        #[serde(skip_serializing_if = "BTreeMap::is_empty")]
-        /// Extra conditions that also need to be satisfied
-        conditions: ConditionMap,
-    },
-    /// Can reach the threshold of some kind of threshold policy
-    PartialComplete {
-        /// Total number of items
-        n: usize,
-        /// Threshold
-        m: usize,
-        /// The items that can be satisfied by the descriptor
-        items: Vec<usize>,
-        #[serde(skip_serializing_if = "Option::is_none")]
-        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
-        sorted: Option<bool>,
-        #[serde(
-            serialize_with = "serialize_folded_cond_map",
-            skip_serializing_if = "BTreeMap::is_empty"
-        )]
-        /// Extra conditions that also need to be satisfied
-        conditions: FoldedConditionMap,
-    },
-
-    /// Can satisfy the policy item
-    Complete {
-        /// Extra conditions that also need to be satisfied
-        condition: Condition,
-    },
-    /// Cannot satisfy or contribute to the policy item
-    None,
-}
-
-impl Satisfaction {
-    /// Returns whether the [`Satisfaction`] is a leaf item
-    pub fn is_leaf(&self) -> bool {
-        match self {
-            Satisfaction::None | Satisfaction::Complete { .. } => true,
-            Satisfaction::PartialComplete { .. } | Satisfaction::Partial { .. } => false,
-        }
-    }
-
-    // add `inner` as one of self's partial items. this only makes sense on partials
-    fn add(&mut self, inner: &Satisfaction, inner_index: usize) -> Result<(), PolicyError> {
-        match self {
-            Satisfaction::None | Satisfaction::Complete { .. } => Err(PolicyError::AddOnLeaf),
-            Satisfaction::PartialComplete { .. } => Err(PolicyError::AddOnPartialComplete),
-            Satisfaction::Partial {
-                n,
-                ref mut conditions,
-                ref mut items,
-                ..
-            } => {
-                if inner_index >= *n || items.contains(&inner_index) {
-                    return Err(PolicyError::IndexOutOfRange(inner_index));
-                }
-
-                match inner {
-                    // not relevant if not completed yet
-                    Satisfaction::None | Satisfaction::Partial { .. } => return Ok(()),
-                    Satisfaction::Complete { condition } => {
-                        items.push(inner_index);
-                        conditions.insert(inner_index, vec![*condition].into_iter().collect());
-                    }
-                    Satisfaction::PartialComplete {
-                        conditions: other_conditions,
-                        ..
-                    } => {
-                        items.push(inner_index);
-                        let conditions_set = other_conditions
-                            .values()
-                            .fold(HashSet::new(), |set, i| set.union(i).cloned().collect());
-                        conditions.insert(inner_index, conditions_set);
-                    }
-                }
-
-                Ok(())
-            }
-        }
-    }
-
-    fn finalize(&mut self) {
-        // if partial try to bump it to a partialcomplete
-        if let Satisfaction::Partial {
-            n,
-            m,
-            items,
-            conditions,
-            sorted,
-        } = self
-        {
-            if items.len() >= *m {
-                let mut map = BTreeMap::new();
-                let indexes = combinations(items, *m);
-                // `indexes` at this point is a Vec<Vec<usize>>, with the "n choose k" of items (m of n)
-                indexes
-                    .into_iter()
-                    // .inspect(|x| println!("--- orig --- {:?}", x))
-                    // we map each of the combinations of elements into a tuple of ([chosen items], [conditions]). unfortunately, those items have potentially more than one
-                    // condition (think about ORs), so we also use `mix` to expand those, i.e. [[0], [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
-                    // consider every possible options and check whether or not they are compatible.
-                    // since this step can turn one item of the iterator into multiple ones, we use `flat_map()` to expand them out
-                    .flat_map(|i_vec| {
-                        mix(i_vec
-                            .iter()
-                            .map(|i| {
-                                conditions
-                                    .get(i)
-                                    .map(|set| set.clone().into_iter().collect())
-                                    .unwrap_or_default()
-                            })
-                            .collect())
-                        .into_iter()
-                        .map(|x| (i_vec.clone(), x))
-                        .collect::<Vec<(Vec<usize>, Vec<Condition>)>>()
-                    })
-                    // .inspect(|x| println!("flat {:?}", x))
-                    // try to fold all the conditions for this specific combination of indexes/options. if they are not compatible, try_fold will be Err
-                    .map(|(key, val)| {
-                        (
-                            key,
-                            val.into_iter()
-                                .try_fold(Condition::default(), |acc, v| acc.merge(&v)),
-                        )
-                    })
-                    // .inspect(|x| println!("try_fold {:?}", x))
-                    // filter out all the incompatible combinations
-                    .filter(|(_, val)| val.is_ok())
-                    // .inspect(|x| println!("filter {:?}", x))
-                    // push them into the map
-                    .for_each(|(key, val)| {
-                        map.entry(key)
-                            .or_insert_with(HashSet::new)
-                            .insert(val.unwrap());
-                    });
-                // TODO: if the map is empty, the conditions are not compatible, return an error?
-                *self = Satisfaction::PartialComplete {
-                    n: *n,
-                    m: *m,
-                    items: items.clone(),
-                    conditions: map,
-                    sorted: *sorted,
-                };
-            }
-        }
-    }
-}
-
-impl From<bool> for Satisfaction {
-    fn from(other: bool) -> Self {
-        if other {
-            Satisfaction::Complete {
-                condition: Default::default(),
-            }
-        } else {
-            Satisfaction::None
-        }
-    }
-}
-
-/// Descriptor spending policy
-#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-pub struct Policy {
-    /// Identifier for this policy node
-    pub id: String,
-
-    /// Type of this policy node
-    #[serde(flatten)]
-    pub item: SatisfiableItem,
-    /// How much a given PSBT already satisfies this policy node in terms of signatures
-    pub satisfaction: Satisfaction,
-    /// How the wallet's descriptor can satisfy this policy node
-    pub contribution: Satisfaction,
-}
-
-/// An extra condition that must be satisfied but that is out of control of the user
-/// TODO: use `bitcoin::LockTime` and `bitcoin::Sequence`
-#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Default, Serialize)]
-pub struct Condition {
-    /// Optional CheckSequenceVerify condition
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub csv: Option<Sequence>,
-    /// Optional timelock condition
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub timelock: Option<absolute::LockTime>,
-}
-
-impl Condition {
-    fn merge_nlocktime(
-        a: absolute::LockTime,
-        b: absolute::LockTime,
-    ) -> Result<absolute::LockTime, PolicyError> {
-        if !a.is_same_unit(b) {
-            Err(PolicyError::MixedTimelockUnits)
-        } else if a > b {
-            Ok(a)
-        } else {
-            Ok(b)
-        }
-    }
-
-    fn merge_nsequence(a: Sequence, b: Sequence) -> Result<Sequence, PolicyError> {
-        if a.is_time_locked() != b.is_time_locked() {
-            Err(PolicyError::MixedTimelockUnits)
-        } else {
-            Ok(max(a, b))
-        }
-    }
-
-    pub(crate) fn merge(mut self, other: &Condition) -> Result<Self, PolicyError> {
-        match (self.csv, other.csv) {
-            (Some(a), Some(b)) => self.csv = Some(Self::merge_nsequence(a, b)?),
-            (None, any) => self.csv = any,
-            _ => {}
-        }
-
-        match (self.timelock, other.timelock) {
-            (Some(a), Some(b)) => self.timelock = Some(Self::merge_nlocktime(a, b)?),
-            (None, any) => self.timelock = any,
-            _ => {}
-        }
-
-        Ok(self)
-    }
-
-    /// Returns `true` if there are no extra conditions to verify
-    pub fn is_null(&self) -> bool {
-        self.csv.is_none() && self.timelock.is_none()
-    }
-}
-
-/// Errors that can happen while extracting and manipulating policies
-#[derive(Debug, PartialEq, Eq)]
-pub enum PolicyError {
-    /// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
-    NotEnoughItemsSelected(String),
-    /// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
-    IndexOutOfRange(usize),
-    /// Can not add to an item that is [`Satisfaction::None`] or [`Satisfaction::Complete`]
-    AddOnLeaf,
-    /// Can not add to an item that is [`Satisfaction::PartialComplete`]
-    AddOnPartialComplete,
-    /// Can not merge CSV or timelock values unless both are less than or both are equal or greater than 500_000_000
-    MixedTimelockUnits,
-    /// Incompatible conditions (not currently used)
-    IncompatibleConditions,
-}
-
-impl fmt::Display for PolicyError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::NotEnoughItemsSelected(err) => write!(f, "Not enough items selected: {}", err),
-            Self::IndexOutOfRange(index) => write!(f, "Index out of range: {}", index),
-            Self::AddOnLeaf => write!(f, "Add on leaf"),
-            Self::AddOnPartialComplete => write!(f, "Add on partial complete"),
-            Self::MixedTimelockUnits => write!(f, "Mixed timelock units"),
-            Self::IncompatibleConditions => write!(f, "Incompatible conditions"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for PolicyError {}
-
-impl Policy {
-    fn new(item: SatisfiableItem) -> Self {
-        Policy {
-            id: item.id(),
-            item,
-            satisfaction: Satisfaction::None,
-            contribution: Satisfaction::None,
-        }
-    }
-
-    fn make_and(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
-        match (a, b) {
-            (None, None) => Ok(None),
-            (Some(x), None) | (None, Some(x)) => Ok(Some(x)),
-            (Some(a), Some(b)) => Self::make_thresh(vec![a, b], 2),
-        }
-    }
-
-    fn make_or(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
-        match (a, b) {
-            (None, None) => Ok(None),
-            (Some(x), None) | (None, Some(x)) => Ok(Some(x)),
-            (Some(a), Some(b)) => Self::make_thresh(vec![a, b], 1),
-        }
-    }
-
-    fn make_thresh(items: Vec<Policy>, threshold: usize) -> Result<Option<Policy>, PolicyError> {
-        if threshold == 0 {
-            return Ok(None);
-        }
-
-        let mut contribution = Satisfaction::Partial {
-            n: items.len(),
-            m: threshold,
-            items: vec![],
-            conditions: Default::default(),
-            sorted: None,
-        };
-        let mut satisfaction = contribution.clone();
-        for (index, item) in items.iter().enumerate() {
-            contribution.add(&item.contribution, index)?;
-            satisfaction.add(&item.satisfaction, index)?;
-        }
-
-        contribution.finalize();
-        satisfaction.finalize();
-
-        let mut policy: Policy = SatisfiableItem::Thresh { items, threshold }.into();
-        policy.contribution = contribution;
-        policy.satisfaction = satisfaction;
-
-        Ok(Some(policy))
-    }
-
-    fn make_multi<Ctx: ScriptContext + 'static, const MAX: usize>(
-        threshold: &Threshold<DescriptorPublicKey, MAX>,
-        signers: &SignersContainer,
-        build_sat: BuildSatisfaction,
-        sorted: bool,
-        secp: &SecpCtx,
-    ) -> Result<Option<Policy>, PolicyError> {
-        let parsed_keys = threshold.iter().map(|k| PkOrF::from_key(k, secp)).collect();
-
-        let mut contribution = Satisfaction::Partial {
-            n: threshold.n(),
-            m: threshold.k(),
-            items: vec![],
-            conditions: Default::default(),
-            sorted: Some(sorted),
-        };
-        let mut satisfaction = contribution.clone();
-
-        for (index, key) in threshold.iter().enumerate() {
-            if signers.find(signer_id(key, secp)).is_some() {
-                contribution.add(
-                    &Satisfaction::Complete {
-                        condition: Default::default(),
-                    },
-                    index,
-                )?;
-            }
-            if let Some(psbt) = build_sat.psbt() {
-                if Ctx::find_signature(psbt, key, secp) {
-                    satisfaction.add(
-                        &Satisfaction::Complete {
-                            condition: Default::default(),
-                        },
-                        index,
-                    )?;
-                }
-            }
-        }
-        satisfaction.finalize();
-        contribution.finalize();
-
-        let mut policy: Policy = SatisfiableItem::Multisig {
-            keys: parsed_keys,
-            threshold: threshold.k(),
-        }
-        .into();
-        policy.contribution = contribution;
-        policy.satisfaction = satisfaction;
-        Ok(Some(policy))
-    }
-
-    /// Return whether or not a specific path in the policy tree is required to unambiguously
-    /// create a transaction
-    ///
-    /// What this means is that for some spending policies the user should select which paths in
-    /// the tree it intends to satisfy while signing, because the transaction must be created differently based
-    /// on that.
-    pub fn requires_path(&self) -> bool {
-        self.get_condition(&BTreeMap::new()).is_err()
-    }
-
-    /// Return the conditions that are set by the spending policy for a given path in the
-    /// policy tree
-    pub fn get_condition(
-        &self,
-        path: &BTreeMap<String, Vec<usize>>,
-    ) -> Result<Condition, PolicyError> {
-        // if items.len() == threshold, selected can be omitted and we take all of them by default
-        let default = match &self.item {
-            SatisfiableItem::Thresh { items, threshold } if items.len() == *threshold => {
-                (0..*threshold).collect()
-            }
-            SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
-            _ => HashSet::new(),
-        };
-        let selected: HashSet<_> = match path.get(&self.id) {
-            Some(arr) => arr.iter().copied().collect(),
-            _ => default,
-        };
-
-        match &self.item {
-            SatisfiableItem::Thresh { items, threshold } => {
-                let mapped_req = items
-                    .iter()
-                    .map(|i| i.get_condition(path))
-                    .collect::<Vec<_>>();
-
-                // if all the requirements are null we don't care about `selected` because there
-                // are no requirements
-                if mapped_req
-                    .iter()
-                    .all(|cond| matches!(cond, Ok(c) if c.is_null()))
-                {
-                    return Ok(Condition::default());
-                }
-
-                // make sure all the indexes in the `selected` list are within range
-                for index in &selected {
-                    if *index >= items.len() {
-                        return Err(PolicyError::IndexOutOfRange(*index));
-                    }
-                }
-
-                // if we have something, make sure we have enough items. note that the user can set
-                // an empty value for this step in case of n-of-n, because `selected` is set to all
-                // the elements above
-                if selected.len() < *threshold {
-                    return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
-                }
-
-                // check the selected items, see if there are conflicting requirements
-                mapped_req
-                    .into_iter()
-                    .enumerate()
-                    .filter(|(index, _)| selected.contains(index))
-                    .try_fold(Condition::default(), |acc, (_, cond)| acc.merge(&cond?))
-            }
-            SatisfiableItem::Multisig { keys, threshold } => {
-                if selected.len() < *threshold {
-                    return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
-                }
-                if let Some(item) = selected.into_iter().find(|&i| i >= keys.len()) {
-                    return Err(PolicyError::IndexOutOfRange(item));
-                }
-
-                Ok(Condition::default())
-            }
-            SatisfiableItem::AbsoluteTimelock { value } => Ok(Condition {
-                csv: None,
-                timelock: Some(*value),
-            }),
-            SatisfiableItem::RelativeTimelock { value } => Ok(Condition {
-                csv: Some((*value).into()),
-                timelock: None,
-            }),
-            _ => Ok(Condition::default()),
-        }
-    }
-}
-
-impl From<SatisfiableItem> for Policy {
-    fn from(other: SatisfiableItem) -> Self {
-        Self::new(other)
-    }
-}
-
-fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
-    // For consistency we always compute the key hash in "ecdsa" form (with the leading sign
-    // prefix) even if we are in a taproot descriptor. We just want some kind of unique identifier
-    // for a key, so it doesn't really matter how the identifier is computed.
-    match key {
-        DescriptorPublicKey::Single(SinglePub {
-            key: SinglePubKey::FullKey(pk),
-            ..
-        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
-        DescriptorPublicKey::Single(SinglePub {
-            key: SinglePubKey::XOnly(pk),
-            ..
-        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
-        DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
-        DescriptorPublicKey::MultiXPub(xpub) => xpub.root_fingerprint(secp).into(),
-    }
-}
-
-fn make_generic_signature<M: Fn() -> SatisfiableItem, F: Fn(&Psbt) -> bool>(
-    key: &DescriptorPublicKey,
-    signers: &SignersContainer,
-    build_sat: BuildSatisfaction,
-    secp: &SecpCtx,
-    make_policy: M,
-    find_sig: F,
-) -> Policy {
-    let mut policy: Policy = make_policy().into();
-
-    policy.contribution = if signers.find(signer_id(key, secp)).is_some() {
-        Satisfaction::Complete {
-            condition: Default::default(),
-        }
-    } else {
-        Satisfaction::None
-    };
-
-    if let Some(psbt) = build_sat.psbt() {
-        policy.satisfaction = if find_sig(psbt) {
-            Satisfaction::Complete {
-                condition: Default::default(),
-            }
-        } else {
-            Satisfaction::None
-        };
-    }
-
-    policy
-}
-
-fn generic_sig_in_psbt<
-    // C is for "check", it's a closure we use to *check* if a psbt input contains the signature
-    // for a specific key
-    C: Fn(&psbt::Input, &SinglePubKey) -> bool,
-    // E is for "extract", it extracts a key from the bip32 derivations found in the psbt input
-    E: Fn(&psbt::Input, Fingerprint) -> Option<SinglePubKey>,
->(
-    psbt: &Psbt,
-    key: &DescriptorPublicKey,
-    secp: &SecpCtx,
-    check: C,
-    extract: E,
-) -> bool {
-    //TODO check signature validity
-    psbt.inputs.iter().all(|input| match key {
-        DescriptorPublicKey::Single(SinglePub { key, .. }) => check(input, key),
-        DescriptorPublicKey::XPub(xpub) => {
-            //TODO check actual derivation matches
-            match extract(input, xpub.root_fingerprint(secp)) {
-                Some(pubkey) => check(input, &pubkey),
-                None => false,
-            }
-        }
-        DescriptorPublicKey::MultiXPub(xpub) => {
-            //TODO check actual derivation matches
-            match extract(input, xpub.root_fingerprint(secp)) {
-                Some(pubkey) => check(input, &pubkey),
-                None => false,
-            }
-        }
-    })
-}
-
-trait SigExt: ScriptContext {
-    fn make_signature(
-        key: &DescriptorPublicKey,
-        signers: &SignersContainer,
-        build_sat: BuildSatisfaction,
-        secp: &SecpCtx,
-    ) -> Policy;
-
-    fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool;
-}
-
-impl<T: ScriptContext + 'static> SigExt for T {
-    fn make_signature(
-        key: &DescriptorPublicKey,
-        signers: &SignersContainer,
-        build_sat: BuildSatisfaction,
-        secp: &SecpCtx,
-    ) -> Policy {
-        if T::as_enum().is_taproot() {
-            make_generic_signature(
-                key,
-                signers,
-                build_sat,
-                secp,
-                || SatisfiableItem::SchnorrSignature(PkOrF::from_key(key, secp)),
-                |psbt| Self::find_signature(psbt, key, secp),
-            )
-        } else {
-            make_generic_signature(
-                key,
-                signers,
-                build_sat,
-                secp,
-                || SatisfiableItem::EcdsaSignature(PkOrF::from_key(key, secp)),
-                |psbt| Self::find_signature(psbt, key, secp),
-            )
-        }
-    }
-
-    fn find_signature(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool {
-        if T::as_enum().is_taproot() {
-            generic_sig_in_psbt(
-                psbt,
-                key,
-                secp,
-                |input, pk| {
-                    let pk = match pk {
-                        SinglePubKey::XOnly(pk) => pk,
-                        _ => return false,
-                    };
-
-                    if input.tap_internal_key == Some(*pk) && input.tap_key_sig.is_some() {
-                        true
-                    } else {
-                        input.tap_script_sigs.keys().any(|(sk, _)| sk == pk)
-                    }
-                },
-                |input, fing| {
-                    input
-                        .tap_key_origins
-                        .iter()
-                        .find(|(_, (_, (f, _)))| f == &fing)
-                        .map(|(pk, _)| SinglePubKey::XOnly(*pk))
-                },
-            )
-        } else {
-            generic_sig_in_psbt(
-                psbt,
-                key,
-                secp,
-                |input, pk| match pk {
-                    SinglePubKey::FullKey(pk) => input.partial_sigs.contains_key(pk),
-                    _ => false,
-                },
-                |input, fing| {
-                    input
-                        .bip32_derivation
-                        .iter()
-                        .find(|(_, (f, _))| f == &fing)
-                        .map(|(pk, _)| SinglePubKey::FullKey(PublicKey::new(*pk)))
-                },
-            )
-        }
-    }
-}
-
-impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
-    fn extract_policy(
-        &self,
-        signers: &SignersContainer,
-        build_sat: BuildSatisfaction,
-        secp: &SecpCtx,
-    ) -> Result<Option<Policy>, Error> {
-        Ok(match &self.node {
-            // Leaves
-            Terminal::True | Terminal::False => None,
-            Terminal::PkK(pubkey) => Some(Ctx::make_signature(pubkey, signers, build_sat, secp)),
-            Terminal::PkH(pubkey_hash) => {
-                Some(Ctx::make_signature(pubkey_hash, signers, build_sat, secp))
-            }
-            Terminal::After(value) => {
-                let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
-                    value: (*value).into(),
-                }
-                .into();
-                policy.contribution = Satisfaction::Complete {
-                    condition: Condition {
-                        timelock: Some((*value).into()),
-                        csv: None,
-                    },
-                };
-                if let BuildSatisfaction::PsbtTimelocks {
-                    current_height,
-                    psbt,
-                    ..
-                } = build_sat
-                {
-                    let after = After::new(Some(current_height), false);
-                    let after_sat =
-                        Satisfier::<bitcoin::PublicKey>::check_after(&after, (*value).into());
-                    let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
-                        Satisfier::<bitcoin::PublicKey>::check_after(&sat, (*value).into())
-                    });
-                    if after_sat && inputs_sat {
-                        policy.satisfaction = policy.contribution.clone();
-                    }
-                }
-
-                Some(policy)
-            }
-            Terminal::Older(value) => {
-                let mut policy: Policy = SatisfiableItem::RelativeTimelock {
-                    value: (*value).into(),
-                }
-                .into();
-                policy.contribution = Satisfaction::Complete {
-                    condition: Condition {
-                        timelock: None,
-                        csv: Some((*value).into()),
-                    },
-                };
-                if let BuildSatisfaction::PsbtTimelocks {
-                    current_height,
-                    input_max_height,
-                    psbt,
-                } = build_sat
-                {
-                    let older = Older::new(Some(current_height), Some(input_max_height), false);
-                    let older_sat =
-                        Satisfier::<bitcoin::PublicKey>::check_older(&older, (*value).into());
-                    let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
-                        Satisfier::<bitcoin::PublicKey>::check_older(&sat, (*value).into())
-                    });
-                    if older_sat && inputs_sat {
-                        policy.satisfaction = policy.contribution.clone();
-                    }
-                }
-
-                Some(policy)
-            }
-            Terminal::Sha256(hash) => Some(SatisfiableItem::Sha256Preimage { hash: *hash }.into()),
-            Terminal::Hash256(hash) => {
-                Some(SatisfiableItem::Hash256Preimage { hash: *hash }.into())
-            }
-            Terminal::Ripemd160(hash) => {
-                Some(SatisfiableItem::Ripemd160Preimage { hash: *hash }.into())
-            }
-            Terminal::Hash160(hash) => {
-                Some(SatisfiableItem::Hash160Preimage { hash: *hash }.into())
-            }
-            Terminal::Multi(threshold) => Policy::make_multi::<Ctx, MAX_PUBKEYS_PER_MULTISIG>(
-                threshold, signers, build_sat, false, secp,
-            )?,
-            Terminal::MultiA(threshold) => Policy::make_multi::<Ctx, MAX_PUBKEYS_IN_CHECKSIGADD>(
-                threshold, signers, build_sat, false, secp,
-            )?,
-            // Identities
-            Terminal::Alt(inner)
-            | Terminal::Swap(inner)
-            | Terminal::Check(inner)
-            | Terminal::DupIf(inner)
-            | Terminal::Verify(inner)
-            | Terminal::NonZero(inner)
-            | Terminal::ZeroNotEqual(inner) => inner.extract_policy(signers, build_sat, secp)?,
-            // Complex policies
-            Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
-                a.extract_policy(signers, build_sat, secp)?,
-                b.extract_policy(signers, build_sat, secp)?,
-            )?,
-            Terminal::AndOr(x, y, z) => Policy::make_or(
-                Policy::make_and(
-                    x.extract_policy(signers, build_sat, secp)?,
-                    y.extract_policy(signers, build_sat, secp)?,
-                )?,
-                z.extract_policy(signers, build_sat, secp)?,
-            )?,
-            Terminal::OrB(a, b)
-            | Terminal::OrD(a, b)
-            | Terminal::OrC(a, b)
-            | Terminal::OrI(a, b) => Policy::make_or(
-                a.extract_policy(signers, build_sat, secp)?,
-                b.extract_policy(signers, build_sat, secp)?,
-            )?,
-            Terminal::Thresh(threshold) => {
-                let mut k = threshold.k();
-                let nodes = threshold.data();
-                let mapped: Vec<_> = nodes
-                    .iter()
-                    .map(|n| n.extract_policy(signers, build_sat, secp))
-                    .collect::<Result<Vec<_>, _>>()?
-                    .into_iter()
-                    .flatten()
-                    .collect();
-
-                if mapped.len() < nodes.len() {
-                    k = match k.checked_sub(nodes.len() - mapped.len()) {
-                        None => return Ok(None),
-                        Some(x) => x,
-                    };
-                }
-
-                Policy::make_thresh(mapped, k)?
-            }
-
-            // Unsupported
-            Terminal::RawPkH(_) => None,
-        })
-    }
-}
-
-fn psbt_inputs_sat(psbt: &Psbt) -> impl Iterator<Item = PsbtInputSatisfier> {
-    (0..psbt.inputs.len()).map(move |i| PsbtInputSatisfier::new(psbt, i))
-}
-
-/// Options to build the satisfaction field in the policy
-#[derive(Debug, Clone, Copy)]
-pub enum BuildSatisfaction<'a> {
-    /// Don't generate `satisfaction` field
-    None,
-    /// Analyze the given PSBT to check for existing signatures
-    Psbt(&'a Psbt),
-    /// Like `Psbt` variant and also check for expired timelocks
-    PsbtTimelocks {
-        /// Given PSBT
-        psbt: &'a Psbt,
-        /// Current blockchain height
-        current_height: u32,
-        /// The highest confirmation height between the inputs
-        /// CSV should consider different inputs, but we consider the worst condition for the tx as whole
-        input_max_height: u32,
-    },
-}
-impl<'a> BuildSatisfaction<'a> {
-    fn psbt(&self) -> Option<&'a Psbt> {
-        match self {
-            BuildSatisfaction::None => None,
-            BuildSatisfaction::Psbt(psbt) => Some(psbt),
-            BuildSatisfaction::PsbtTimelocks { psbt, .. } => Some(psbt),
-        }
-    }
-}
-
-impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
-    fn extract_policy(
-        &self,
-        signers: &SignersContainer,
-        build_sat: BuildSatisfaction,
-        secp: &SecpCtx,
-    ) -> Result<Option<Policy>, Error> {
-        fn make_sortedmulti<Ctx: ScriptContext + 'static>(
-            keys: &SortedMultiVec<DescriptorPublicKey, Ctx>,
-            signers: &SignersContainer,
-            build_sat: BuildSatisfaction,
-            secp: &SecpCtx,
-        ) -> Result<Option<Policy>, Error> {
-            let threshold = Threshold::new(keys.k(), keys.pks().to_vec())
-                .expect("valid threshold and pks collection");
-            Ok(Policy::make_multi::<Ctx, MAX_PUBKEYS_PER_MULTISIG>(
-                &threshold, signers, build_sat, true, secp,
-            )?)
-        }
-
-        match self {
-            Descriptor::Pkh(pk) => Ok(Some(miniscript::Legacy::make_signature(
-                pk.as_inner(),
-                signers,
-                build_sat,
-                secp,
-            ))),
-            Descriptor::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature(
-                pk.as_inner(),
-                signers,
-                build_sat,
-                secp,
-            ))),
-            Descriptor::Sh(sh) => match sh.as_inner() {
-                ShInner::Wpkh(pk) => Ok(Some(miniscript::Segwitv0::make_signature(
-                    pk.as_inner(),
-                    signers,
-                    build_sat,
-                    secp,
-                ))),
-                ShInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
-                ShInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp),
-                ShInner::Wsh(wsh) => match wsh.as_inner() {
-                    WshInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
-                    WshInner::SortedMulti(ref keys) => {
-                        make_sortedmulti(keys, signers, build_sat, secp)
-                    }
-                },
-            },
-            Descriptor::Wsh(wsh) => match wsh.as_inner() {
-                WshInner::Ms(ms) => Ok(ms.extract_policy(signers, build_sat, secp)?),
-                WshInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp),
-            },
-            Descriptor::Bare(ms) => Ok(ms.as_inner().extract_policy(signers, build_sat, secp)?),
-            Descriptor::Tr(tr) => {
-                // If there's no tap tree, treat this as a single sig, otherwise build a `Thresh`
-                // node with threshold = 1 and the key spend signature plus all the tree leaves
-                let key_spend_sig =
-                    miniscript::Tap::make_signature(tr.internal_key(), signers, build_sat, secp);
-
-                if tr.tap_tree().is_none() {
-                    Ok(Some(key_spend_sig))
-                } else {
-                    let mut items = vec![key_spend_sig];
-                    items.append(
-                        &mut tr
-                            .iter_scripts()
-                            .filter_map(|(_, ms)| {
-                                ms.extract_policy(signers, build_sat, secp).transpose()
-                            })
-                            .collect::<Result<Vec<_>, _>>()?,
-                    );
-
-                    Ok(Policy::make_thresh(items, 1)?)
-                }
-            }
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::descriptor;
-    use crate::descriptor::{ExtractPolicy, IntoWalletDescriptor};
-
-    use super::*;
-    use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh};
-    use crate::keys::{DescriptorKey, IntoDescriptorKey};
-    use crate::wallet::signer::SignersContainer;
-    use alloc::{string::ToString, sync::Arc};
-    use assert_matches::assert_matches;
-    use bitcoin::bip32;
-    use bitcoin::secp256k1::Secp256k1;
-    use bitcoin::Network;
-    use core::str::FromStr;
-
-    const TPRV0_STR:&str = "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf";
-    const TPRV1_STR:&str = "tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N";
-
-    const PATH: &str = "m/44'/1'/0'/0";
-
-    fn setup_keys<Ctx: ScriptContext>(
-        tprv: &str,
-        path: &str,
-        secp: &SecpCtx,
-    ) -> (DescriptorKey<Ctx>, DescriptorKey<Ctx>, Fingerprint) {
-        let path = bip32::DerivationPath::from_str(path).unwrap();
-        let tprv = bip32::Xpriv::from_str(tprv).unwrap();
-        let tpub = bip32::Xpub::from_priv(secp, &tprv);
-        let fingerprint = tprv.fingerprint(secp);
-        let prvkey = (tprv, path.clone()).into_descriptor_key().unwrap();
-        let pubkey = (tpub, path).into_descriptor_key().unwrap();
-
-        (prvkey, pubkey, fingerprint)
-    }
-
-    // test ExtractPolicy trait for simple descriptors; wpkh(), sh(multi())
-
-    #[test]
-    fn test_extract_policy_for_wpkh() {
-        let secp = Secp256k1::new();
-
-        let (prvkey, pubkey, fingerprint) = setup_keys(TPRV0_STR, PATH, &secp);
-        let desc = descriptor!(wpkh(pubkey)).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
-        assert_matches!(&policy.contribution, Satisfaction::None);
-
-        let desc = descriptor!(wpkh(prvkey)).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
-        assert_matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv.is_none() && condition.timelock.is_none());
-    }
-
-    // 2 pub keys descriptor, required 2 prv keys
-    #[test]
-    fn test_extract_policy_for_sh_multi_partial_0of2() {
-        let secp = Secp256k1::new();
-        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (_prvkey1, pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let desc = descriptor!(sh(multi(2, pubkey0, pubkey1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
-            && keys[0] == PkOrF::Fingerprint(fingerprint0)
-            && keys[1] == PkOrF::Fingerprint(fingerprint1)
-        );
-        // TODO should this be "Satisfaction::None" since we have no prv keys?
-        // TODO should items and conditions not be empty?
-        assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
-            && m == &2usize
-            && items.is_empty()
-            && conditions.is_empty()
-        );
-    }
-
-    // 1 prv and 1 pub key descriptor, required 2 prv keys
-    #[test]
-    fn test_extract_policy_for_sh_multi_partial_1of2() {
-        let secp = Secp256k1::new();
-        let (prvkey0, _pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (_prvkey1, pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let desc = descriptor!(sh(multi(2, prvkey0, pubkey1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2usize
-            && keys[0] == PkOrF::Fingerprint(fingerprint0)
-            && keys[1] == PkOrF::Fingerprint(fingerprint1)
-        );
-
-        assert_matches!(&policy.contribution, Satisfaction::Partial { n, m, items, conditions, ..} if n == &2usize
-             && m == &2usize
-             && items.len() == 1
-             && conditions.contains_key(&0)
-        );
-    }
-
-    // 1 prv and 1 pub key descriptor, required 1 prv keys
-    #[test]
-    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
-    fn test_extract_policy_for_sh_multi_complete_1of2() {
-        let secp = Secp256k1::new();
-
-        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let desc = descriptor!(sh(multi(1, pubkey0, prvkey1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &1
-            && keys[0] == PkOrF::Fingerprint(fingerprint0)
-            && keys[1] == PkOrF::Fingerprint(fingerprint1)
-        );
-        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
-             && m == &1
-             && items.len() == 2
-             && conditions.contains_key(&vec![0])
-             && conditions.contains_key(&vec![1])
-        );
-    }
-
-    // 2 prv keys descriptor, required 2 prv keys
-    #[test]
-    fn test_extract_policy_for_sh_multi_complete_2of2() {
-        let secp = Secp256k1::new();
-
-        let (prvkey0, _pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let desc = descriptor!(sh(multi(2, prvkey0, prvkey1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, Multisig { keys, threshold } if threshold == &2
-            && keys[0] == PkOrF::Fingerprint(fingerprint0)
-            && keys[1] == PkOrF::Fingerprint(fingerprint1)
-        );
-
-        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
-             && m == &2
-             && items.len() == 2
-             && conditions.contains_key(&vec![0,1])
-        );
-    }
-
-    // test ExtractPolicy trait with extended and single keys
-
-    #[test]
-    fn test_extract_policy_for_single_wpkh() {
-        let secp = Secp256k1::new();
-
-        let (prvkey, pubkey, fingerprint) = setup_keys(TPRV0_STR, PATH, &secp);
-        let desc = descriptor!(wpkh(pubkey)).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint);
-        assert_matches!(&policy.contribution, Satisfaction::None);
-
-        let desc = descriptor!(wpkh(prvkey)).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == fingerprint);
-        assert_matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv.is_none() && condition.timelock.is_none());
-    }
-
-    // single key, 1 prv and 1 pub key descriptor, required 1 prv keys
-    #[test]
-    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
-    fn test_extract_policy_for_single_wsh_multi_complete_1of2() {
-        let secp = Secp256k1::new();
-
-        let (_prvkey0, pubkey0, fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (prvkey1, _pubkey1, fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let desc = descriptor!(sh(multi(1, pubkey0, prvkey1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(policy.item, Multisig { keys, threshold } if threshold == 1
-            && keys[0] == PkOrF::Fingerprint(fingerprint0)
-            && keys[1] == PkOrF::Fingerprint(fingerprint1)
-        );
-        assert_matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2
-             && m == 1
-             && items.len() == 2
-             && conditions.contains_key(&vec![0])
-             && conditions.contains_key(&vec![1])
-        );
-    }
-
-    // test ExtractPolicy trait with descriptors containing timelocks in a thresh()
-
-    #[test]
-    #[ignore] // see https://github.com/bitcoindevkit/bdk/issues/225
-    fn test_extract_policy_for_wsh_multi_timelock() {
-        let secp = Secp256k1::new();
-
-        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (_prvkey1, pubkey1, _fingerprint1) = setup_keys(TPRV1_STR, PATH, &secp);
-        let sequence = 50;
-        #[rustfmt::skip]
-        let desc = descriptor!(wsh(thresh(
-            2,
-            pk(prvkey0),
-            s:pk(pubkey1),
-            s:d:v:older(sequence)
-        )))
-        .unwrap();
-
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(&policy.item, Thresh { items, threshold } if items.len() == 3 && threshold == &2);
-
-        assert_matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
-             && m == &2
-             && items.len() == 3
-             && conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
-             && conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
-             && conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
-        );
-    }
-
-    // - mixed timelocks should fail
-
-    #[test]
-    #[ignore]
-    fn test_extract_policy_for_wsh_mixed_timelocks() {
-        let secp = Secp256k1::new();
-        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let locktime_threshold = 500000000; // if less than this means block number, else block time in seconds
-        let locktime_blocks = 100;
-        let locktime_seconds = locktime_blocks + locktime_threshold;
-        let desc = descriptor!(sh(and_v(
-            v: pk(prvkey0),
-            and_v(v: after(locktime_seconds), after(locktime_blocks))
-        )))
-        .unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let _policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-        // println!("desc policy = {:?}", policy); // TODO remove
-        // TODO how should this fail with mixed timelocks?
-    }
-
-    // - multiple timelocks of the same type should be correctly merged together
-    #[test]
-    #[ignore]
-    fn test_extract_policy_for_multiple_same_timelocks() {
-        let secp = Secp256k1::new();
-        let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR, PATH, &secp);
-        let locktime_blocks0 = 100;
-        let locktime_blocks1 = 200;
-        let desc = descriptor!(sh(and_v(
-            v: pk(prvkey0),
-            and_v(v: after(locktime_blocks0), after(locktime_blocks1))
-        )))
-        .unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let _policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-        // println!("desc policy = {:?}", policy); // TODO remove
-        // TODO how should this merge timelocks?
-        let (prvkey1, _pubkey1, _fingerprint1) = setup_keys(TPRV0_STR, PATH, &secp);
-        let locktime_seconds0 = 500000100;
-        let locktime_seconds1 = 500000200;
-        let desc = descriptor!(sh(and_v(
-            v: pk(prvkey1),
-            and_v(v: after(locktime_seconds0), after(locktime_seconds1))
-        )))
-        .unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let _policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        // println!("desc policy = {:?}", policy); // TODO remove
-
-        // TODO how should this merge timelocks?
-    }
-
-    #[test]
-    fn test_get_condition_multisig() {
-        let secp = Secp256k1::new();
-
-        let (_, pk0, _) = setup_keys(TPRV0_STR, PATH, &secp);
-        let (_, pk1, _) = setup_keys(TPRV1_STR, PATH, &secp);
-
-        let desc = descriptor!(wsh(multi(1, pk0, pk1))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        // no args, choose the default
-        let no_args = policy.get_condition(&vec![].into_iter().collect());
-        assert_eq!(no_args, Ok(Condition::default()));
-
-        // enough args
-        let eq_thresh =
-            policy.get_condition(&vec![(policy.id.clone(), vec![0])].into_iter().collect());
-        assert_eq!(eq_thresh, Ok(Condition::default()));
-
-        // more args, it doesn't really change anything
-        let gt_thresh =
-            policy.get_condition(&vec![(policy.id.clone(), vec![0, 1])].into_iter().collect());
-        assert_eq!(gt_thresh, Ok(Condition::default()));
-
-        // not enough args, error
-        let lt_thresh =
-            policy.get_condition(&vec![(policy.id.clone(), vec![])].into_iter().collect());
-        assert_eq!(
-            lt_thresh,
-            Err(PolicyError::NotEnoughItemsSelected(policy.id.clone()))
-        );
-
-        // index out of range
-        let out_of_range =
-            policy.get_condition(&vec![(policy.id.clone(), vec![5])].into_iter().collect());
-        assert_eq!(out_of_range, Err(PolicyError::IndexOutOfRange(5)));
-    }
-
-    const ALICE_TPRV_STR:&str = "tprv8ZgxMBicQKsPf6T5X327efHnvJDr45Xnb8W4JifNWtEoqXu9MRYS4v1oYe6DFcMVETxy5w3bqpubYRqvcVTqovG1LifFcVUuJcbwJwrhYzP";
-    const BOB_TPRV_STR:&str = "tprv8ZgxMBicQKsPeinZ155cJAn117KYhbaN6MV3WeG6sWhxWzcvX1eg1awd4C9GpUN1ncLEM2rzEvunAg3GizdZD4QPPCkisTz99tXXB4wZArp";
-    const CAROL_TPRV_STR:&str = "tprv8ZgxMBicQKsPdC3CicFifuLCEyVVdXVUNYorxUWj3iGZ6nimnLAYAY9SYB7ib8rKzRxrCKFcEytCt6szwd2GHnGPRCBLAEAoSVDefSNk4Bt";
-    const ALICE_BOB_PATH: &str = "m/0'";
-
-    #[test]
-    fn test_extract_satisfaction() {
-        const ALICE_SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstHMEQCIBj0jLjUeVYXNQ6cqB+gbtvuKMjV54wSgWlm1cfcgpHVAiBa3DtC9l/1Mt4IDCvR7mmwQd3eAP/m5++81euhJNSrgQEBBUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSriIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
-        const BOB_SIGNED_PSBT: &str =   "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhIMEUCIQD5zDtM5MwklurwJ5aW76RsO36Iqyu+6uMdVlhL6ws2GQIgesAiz4dbKS7UmhDsC/c1ezu0o6hp00UUtsCMfUZ4anYBAQVHUiEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZsshAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIUq4iBgL4YT/L4um0jzGaJHqw5733wgbPJujHRB/Pj4FCXWeZiAwcLu4+AAAAgAAAAAAiBgN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmywzJEXwuAAAAgAAAAAAAAA==";
-        const ALICE_BOB_SIGNED_PSBT: &str =   "cHNidP8BAFMBAAAAAZb0njwT2wRS3AumaaP3yb7T4MxOePpSWih4Nq+jWChMAQAAAAD/////Af4lAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASuJJgAAAAAAACIAIERw5kTLo9DUH9QDJSClHQwPpC7VGJ+ZMDpa8U+2fzcYIgIC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhIMEUCIQD5zDtM5MwklurwJ5aW76RsO36Iqyu+6uMdVlhL6ws2GQIgesAiz4dbKS7UmhDsC/c1ezu0o6hp00UUtsCMfUZ4anYBIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstHMEQCIBj0jLjUeVYXNQ6cqB+gbtvuKMjV54wSgWlm1cfcgpHVAiBa3DtC9l/1Mt4IDCvR7mmwQd3eAP/m5++81euhJNSrgQEBBUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSriIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAEHAAEI2wQARzBEAiAY9Iy41HlWFzUOnKgfoG7b7ijI1eeMEoFpZtXH3IKR1QIgWtw7QvZf9TLeCAwr0e5psEHd3gD/5ufvvNXroSTUq4EBSDBFAiEA+cw7TOTMJJbq8CeWlu+kbDt+iKsrvurjHVZYS+sLNhkCIHrAIs+HWyku1JoQ7Av3NXs7tKOoadNFFLbAjH1GeGp2AUdSIQN4C2NhCT9V+7h1vb7ryHIwqNzfz6RaXmw/lAfwvjZmyyEC+GE/y+LptI8xmiR6sOe998IGzybox0Qfz4+BQl1nmYhSrgAA";
-
-        let secp = Secp256k1::new();
-
-        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(wsh(multi(2, prvkey_alice, prvkey_bob))).unwrap();
-
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-
-        let addr = wallet_desc
-            .at_derivation_index(0)
-            .unwrap()
-            .address(Network::Testnet)
-            .unwrap();
-        assert_eq!(
-            "tb1qg3cwv3xt50gdg875qvjjpfgaps86gtk4rz0ejvp6ttc5ldnlxuvqlcn0xk",
-            addr.to_string()
-        );
-
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let psbt = Psbt::from_str(ALICE_SIGNED_PSBT).unwrap();
-
-        let policy_alice_psbt = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
-            .unwrap()
-            .unwrap();
-        //println!("{}", serde_json::to_string(&policy_alice_psbt).unwrap());
-
-        assert_matches!(&policy_alice_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
-             && m == &2
-             && items == &vec![0]
-        );
-
-        let psbt = Psbt::from_str(BOB_SIGNED_PSBT).unwrap();
-        let policy_bob_psbt = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
-            .unwrap()
-            .unwrap();
-        //println!("{}", serde_json::to_string(&policy_bob_psbt).unwrap());
-
-        assert_matches!(&policy_bob_psbt.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &2
-             && m == &2
-             && items == &vec![1]
-        );
-
-        let psbt = Psbt::from_str(ALICE_BOB_SIGNED_PSBT).unwrap();
-        let policy_alice_bob_psbt = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::Psbt(&psbt), &secp)
-            .unwrap()
-            .unwrap();
-        assert_matches!(&policy_alice_bob_psbt.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &2
-             && m == &2
-             && items == &vec![0, 1]
-        );
-    }
-
-    #[test]
-    fn test_extract_satisfaction_timelock() {
-        //const PSBT_POLICY_CONSIDER_TIMELOCK_NOT_EXPIRED: &str = "cHNidP8BAFMBAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAD/////ATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
-        const PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED:     &str = "cHNidP8BAFMCAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAACAAAAATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAAA";
-        const PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED_SIGNED: &str ="cHNidP8BAFMCAAAAAdld52uJFGT7Yde0YZdSVh2vL020Zm2exadH5R4GSNScAAAAAAACAAAAATrcAAAAAAAAF6kUXv2Fn+YemPP4PUpNR1ZbU16/eRCHAAAAAAABASvI3AAAAAAAACIAILhzvvcBzw/Zfnc9ispRK0PCahxn1F6RHXTZAmw5tqNPIgIDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42ZstIMEUCIQCtZxNm6H3Ux3pnc64DSpgohMdBj+57xhFHcURYt2BpPAIgG3OnI7bcj/3GtWX1HHyYGSI7QGa/zq5YnsmK1Cw29NABAQVSdmNSsmlofCEDeAtjYQk/Vfu4db2+68hyMKjc38+kWl5sP5QH8L42Zsusk3whAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIrJNShyIGAvhhP8vi6bSPMZokerDnvffCBs8m6MdEH8+PgUJdZ5mIDBwu7j4AAACAAAAAACIGA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLDMkRfC4AAACAAAAAAAEHAAEIoAQASDBFAiEArWcTZuh91Md6Z3OuA0qYKITHQY/ue8YRR3FEWLdgaTwCIBtzpyO23I/9xrVl9Rx8mBkiO0Bmv86uWJ7JitQsNvTQAQEBUnZjUrJpaHwhA3gLY2EJP1X7uHW9vuvIcjCo3N/PpFpebD+UB/C+NmbLrJN8IQL4YT/L4um0jzGaJHqw5733wgbPJujHRB/Pj4FCXWeZiKyTUocAAA==";
-
-        let secp = Secp256k1::new();
-
-        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc =
-            descriptor!(wsh(thresh(2,n:d:v:older(2),s:pk(prvkey_alice),s:pk(prvkey_bob)))).unwrap();
-
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let addr = wallet_desc
-            .at_derivation_index(0)
-            .unwrap()
-            .address(Network::Testnet)
-            .unwrap();
-        assert_eq!(
-            "tb1qsydsey4hexagwkvercqsmes6yet0ndkyt6uzcphtqnygjd8hmzmsfxrv58",
-            addr.to_string()
-        );
-
-        let psbt = Psbt::from_str(PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED).unwrap();
-
-        let build_sat = BuildSatisfaction::PsbtTimelocks {
-            psbt: &psbt,
-            current_height: 10,
-            input_max_height: 9,
-        };
-
-        let policy = wallet_desc
-            .extract_policy(&signers_container, build_sat, &secp)
-            .unwrap()
-            .unwrap();
-        assert_matches!(&policy.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
-             && m == &2
-             && items.is_empty()
-        );
-        //println!("{}", serde_json::to_string(&policy).unwrap());
-
-        let build_sat_expired = BuildSatisfaction::PsbtTimelocks {
-            psbt: &psbt,
-            current_height: 12,
-            input_max_height: 9,
-        };
-
-        let policy_expired = wallet_desc
-            .extract_policy(&signers_container, build_sat_expired, &secp)
-            .unwrap()
-            .unwrap();
-        assert_matches!(&policy_expired.satisfaction, Satisfaction::Partial { n, m, items, .. } if n == &3
-             && m == &2
-             && items == &vec![0]
-        );
-        //println!("{}", serde_json::to_string(&policy_expired).unwrap());
-
-        let psbt_signed = Psbt::from_str(PSBT_POLICY_CONSIDER_TIMELOCK_EXPIRED_SIGNED).unwrap();
-
-        let build_sat_expired_signed = BuildSatisfaction::PsbtTimelocks {
-            psbt: &psbt_signed,
-            current_height: 12,
-            input_max_height: 9,
-        };
-
-        let policy_expired_signed = wallet_desc
-            .extract_policy(&signers_container, build_sat_expired_signed, &secp)
-            .unwrap()
-            .unwrap();
-        assert_matches!(&policy_expired_signed.satisfaction, Satisfaction::PartialComplete { n, m, items, .. } if n == &3
-             && m == &2
-             && items == &vec![0, 1]
-        );
-        //println!("{}", serde_json::to_string(&policy_expired_signed).unwrap());
-    }
-
-    #[test]
-    fn test_extract_pkh() {
-        let secp = Secp256k1::new();
-
-        let (prvkey_alice, _, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (prvkey_bob, _, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (prvkey_carol, _, _) = setup_keys(CAROL_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(wsh(c: andor(
-            pk(prvkey_alice),
-            pk_k(prvkey_bob),
-            pk_h(prvkey_carol),
-        )))
-        .unwrap();
-
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let policy = wallet_desc.extract_policy(&signers_container, BuildSatisfaction::None, &secp);
-        assert!(policy.is_ok());
-    }
-
-    #[test]
-    fn test_extract_tr_key_spend() {
-        let secp = Secp256k1::new();
-
-        let (prvkey, _, fingerprint) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(tr(prvkey)).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap();
-        assert_eq!(
-            policy,
-            Some(Policy {
-                id: "48u0tz0n".to_string(),
-                item: SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(fingerprint)),
-                satisfaction: Satisfaction::None,
-                contribution: Satisfaction::Complete {
-                    condition: Condition::default()
-                }
-            })
-        );
-    }
-
-    #[test]
-    fn test_extract_tr_script_spend() {
-        let secp = Secp256k1::new();
-
-        let (alice_prv, _, alice_fing) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (_, bob_pub, bob_fing) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(tr(bob_pub, pk(alice_prv))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-        let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-
-        let policy = wallet_desc
-            .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(policy.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
-        assert_matches!(policy.contribution, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![1]);
-
-        let alice_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(alice_fing));
-        let bob_sig = SatisfiableItem::SchnorrSignature(PkOrF::Fingerprint(bob_fing));
-
-        let thresh_items = match policy.item {
-            SatisfiableItem::Thresh { items, .. } => items,
-            _ => unreachable!(),
-        };
-
-        assert_eq!(thresh_items[0].item, bob_sig);
-        assert_eq!(thresh_items[1].item, alice_sig);
-    }
-
-    #[test]
-    fn test_extract_tr_satisfaction_key_spend() {
-        const UNSIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAUKgMCqtGLSiGYhsTols2UJ/VQQgQi/SXO38uXs2SahdAQAAAAD/////ARyWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRIEiEBFjbZa1xdjLfFjrKzuC1F1LeRyI/gL6IuGKNmUuSIRYnkGTDxwXMHP32fkDFoGJY28trxbkkVgR2z7jZa2pOJA0AyRF8LgAAAIADAAAAARcgJ5Bkw8cFzBz99n5AxaBiWNvLa8W5JFYEds+42WtqTiQAAA==";
-        const SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAUKgMCqtGLSiGYhsTols2UJ/VQQgQi/SXO38uXs2SahdAQAAAAD/////ARyWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRIEiEBFjbZa1xdjLfFjrKzuC1F1LeRyI/gL6IuGKNmUuSARNAIsRvARpRxuyQosVA7guRQT9vXr+S25W2tnP2xOGBsSgq7A4RL8yrbvwDmNlWw9R0Nc/6t+IsyCyy7dD/lbUGgyEWJ5Bkw8cFzBz99n5AxaBiWNvLa8W5JFYEds+42WtqTiQNAMkRfC4AAACAAwAAAAEXICeQZMPHBcwc/fZ+QMWgYljby2vFuSRWBHbPuNlrak4kAAA=";
-
-        let unsigned_psbt = Psbt::from_str(UNSIGNED_PSBT).unwrap();
-        let signed_psbt = Psbt::from_str(SIGNED_PSBT).unwrap();
-
-        let secp = Secp256k1::new();
-
-        let (_, pubkey, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(tr(pubkey)).unwrap();
-        let (wallet_desc, _) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-
-        let policy_unsigned = wallet_desc
-            .extract_policy(
-                &SignersContainer::default(),
-                BuildSatisfaction::Psbt(&unsigned_psbt),
-                &secp,
-            )
-            .unwrap()
-            .unwrap();
-        let policy_signed = wallet_desc
-            .extract_policy(
-                &SignersContainer::default(),
-                BuildSatisfaction::Psbt(&signed_psbt),
-                &secp,
-            )
-            .unwrap()
-            .unwrap();
-
-        assert_eq!(policy_unsigned.satisfaction, Satisfaction::None);
-        assert_eq!(
-            policy_signed.satisfaction,
-            Satisfaction::Complete {
-                condition: Default::default()
-            }
-        );
-    }
-
-    #[test]
-    fn test_extract_tr_satisfaction_script_spend() {
-        const UNSIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAWZalxaErOL7P3WPIUc8DsjgE68S+ww+uqiqEI2SAwlPAAAAAAD/////AQiWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRINa6bLPZwp3/CYWoxyI3mLYcSC5f9LInAMUng94nspa2IhXBgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYjIHhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQarMAhFnhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQaLQH2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHRwu7j4AAACAAgAAACEWgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYNAMkRfC4AAACAAgAAAAEXIIIj2PpHKJUtR6dJ4jiv/u1R8+hfp7M/CVcZ81s5IE6GARgg9qJ1hXN1EeiPWYbh1XiQouSzQH+AD1Xe5h5+AYXVYh0AAA==";
-        const SIGNED_PSBT: &str = "cHNidP8BAFMBAAAAAWZalxaErOL7P3WPIUc8DsjgE68S+ww+uqiqEI2SAwlPAAAAAAD/////AQiWmAAAAAAAF6kU4R3W8CnGzZcSsaovTYu0X8vHt3WHAAAAAAABASuAlpgAAAAAACJRINa6bLPZwp3/CYWoxyI3mLYcSC5f9LInAMUng94nspa2AQcAAQhCAUALcP9w/+Ddly9DWdhHTnQ9uCDWLPZjR6vKbKePswW2Ee6W5KNfrklus/8z98n7BQ1U4vADHk0FbadeeL8rrbHlARNAC3D/cP/g3ZcvQ1nYR050Pbgg1iz2Y0erymynj7MFthHuluSjX65JbrP/M/fJ+wUNVOLwAx5NBW2nXni/K62x5UEUeEbK57HG1FUp69HHhjBZH9bSvss8e3qhLoMuXPK5hBr2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHUAXNmWieJ80Fs+PMa2C186YOBPZbYG/ieEUkagMwzJ788SoCucNdp5wnxfpuJVygFhglDrXGzujFtC82PrMohwuIhXBgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYjIHhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQarMAhFnhGyuexxtRVKevRx4YwWR/W0r7LPHt6oS6DLlzyuYQaLQH2onWFc3UR6I9ZhuHVeJCi5LNAf4APVd7mHn4BhdViHRwu7j4AAACAAgAAACEWgiPY+kcolS1Hp0niOK/+7VHz6F+nsz8JVxnzWzkgToYNAMkRfC4AAACAAgAAAAEXIIIj2PpHKJUtR6dJ4jiv/u1R8+hfp7M/CVcZ81s5IE6GARgg9qJ1hXN1EeiPWYbh1XiQouSzQH+AD1Xe5h5+AYXVYh0AAA==";
-
-        let unsigned_psbt = Psbt::from_str(UNSIGNED_PSBT).unwrap();
-        let signed_psbt = Psbt::from_str(SIGNED_PSBT).unwrap();
-
-        let secp = Secp256k1::new();
-
-        let (_, alice_pub, _) = setup_keys(ALICE_TPRV_STR, ALICE_BOB_PATH, &secp);
-        let (_, bob_pub, _) = setup_keys(BOB_TPRV_STR, ALICE_BOB_PATH, &secp);
-
-        let desc = descriptor!(tr(bob_pub, pk(alice_pub))).unwrap();
-        let (wallet_desc, _) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-
-        let policy_unsigned = wallet_desc
-            .extract_policy(
-                &SignersContainer::default(),
-                BuildSatisfaction::Psbt(&unsigned_psbt),
-                &secp,
-            )
-            .unwrap()
-            .unwrap();
-        let policy_signed = wallet_desc
-            .extract_policy(
-                &SignersContainer::default(),
-                BuildSatisfaction::Psbt(&signed_psbt),
-                &secp,
-            )
-            .unwrap()
-            .unwrap();
-
-        assert_matches!(policy_unsigned.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
-        assert_matches!(policy_unsigned.satisfaction, Satisfaction::Partial { n: 2, m: 1, items, .. } if items.is_empty());
-
-        assert_matches!(policy_signed.item, SatisfiableItem::Thresh { ref items, threshold: 1 } if items.len() == 2);
-        assert_matches!(policy_signed.satisfaction, Satisfaction::PartialComplete { n: 2, m: 1, items, .. } if items == vec![0, 1]);
-
-        let satisfied_items = match policy_signed.item {
-            SatisfiableItem::Thresh { items, .. } => items,
-            _ => unreachable!(),
-        };
-
-        assert_eq!(
-            satisfied_items[0].satisfaction,
-            Satisfaction::Complete {
-                condition: Default::default()
-            }
-        );
-        assert_eq!(
-            satisfied_items[1].satisfaction,
-            Satisfaction::Complete {
-                condition: Default::default()
-            }
-        );
-    }
-}
diff --git a/crates/wallet/src/descriptor/template.rs b/crates/wallet/src/descriptor/template.rs
deleted file mode 100644 (file)
index ee9ec9a..0000000
+++ /dev/null
@@ -1,1018 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Descriptor templates
-//!
-//! This module contains the definition of various common script templates that are ready to be
-//! used. See the documentation of each template for an example.
-
-use bitcoin::bip32;
-use bitcoin::Network;
-
-use miniscript::{Legacy, Segwitv0, Tap};
-
-use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
-use crate::descriptor::DescriptorError;
-use crate::keys::{DerivableKey, IntoDescriptorKey, ValidNetworks};
-use crate::wallet::utils::SecpCtx;
-use crate::{descriptor, KeychainKind};
-
-/// Type alias for the return type of [`DescriptorTemplate`], [`descriptor!`](crate::descriptor!) and others
-pub type DescriptorTemplateOut = (ExtendedDescriptor, KeyMap, ValidNetworks);
-
-/// Trait for descriptor templates that can be built into a full descriptor
-///
-/// Since [`IntoWalletDescriptor`] is implemented for any [`DescriptorTemplate`], they can also be
-/// passed directly to the [`Wallet`](crate::Wallet) constructor.
-///
-/// ## Example
-///
-/// ```
-/// use bdk_wallet::descriptor::error::Error as DescriptorError;
-/// use bdk_wallet::keys::{IntoDescriptorKey, KeyError};
-/// use bdk_wallet::miniscript::Legacy;
-/// use bdk_wallet::template::{DescriptorTemplate, DescriptorTemplateOut};
-/// use bitcoin::Network;
-///
-/// struct MyP2PKH<K: IntoDescriptorKey<Legacy>>(K);
-///
-/// impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for MyP2PKH<K> {
-///     fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-///         Ok(bdk_wallet::descriptor!(pkh(self.0))?)
-///     }
-/// }
-/// ```
-pub trait DescriptorTemplate {
-    /// Build the complete descriptor
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError>;
-}
-
-/// Turns a [`DescriptorTemplate`] into a valid wallet descriptor by calling its
-/// [`build`](DescriptorTemplate::build) method
-impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
-    fn into_wallet_descriptor(
-        self,
-        secp: &SecpCtx,
-        network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        self.build(network)?.into_wallet_descriptor(secp, network)
-    }
-}
-
-/// P2PKH template. Expands to a descriptor `pkh(key)`
-///
-/// ## Example
-///
-/// ```
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::Wallet;
-/// # use bdk_wallet::KeychainKind;
-/// use bdk_wallet::template::P2Pkh;
-///
-/// let key_external =
-///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let key_internal =
-///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet = Wallet::create(P2Pkh(key_external), P2Pkh(key_internal))
-///     .network(Network::Testnet)
-///     .create_wallet_no_persist()?;
-///
-/// assert_eq!(
-///     wallet
-///         .next_unused_address(KeychainKind::External)
-///         .to_string(),
-///     "mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"
-/// );
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct P2Pkh<K: IntoDescriptorKey<Legacy>>(pub K);
-
-impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
-    fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        descriptor!(pkh(self.0))
-    }
-}
-
-/// P2WPKH-P2SH template. Expands to a descriptor `sh(wpkh(key))`
-///
-/// ## Example
-///
-/// ```
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::Wallet;
-/// # use bdk_wallet::KeychainKind;
-/// use bdk_wallet::template::P2Wpkh_P2Sh;
-///
-/// let key_external =
-///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let key_internal =
-///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet = Wallet::create(P2Wpkh_P2Sh(key_external), P2Wpkh_P2Sh(key_internal))
-///     .network(Network::Testnet)
-///     .create_wallet_no_persist()?;
-///
-/// assert_eq!(
-///     wallet
-///         .next_unused_address(KeychainKind::External)
-///         .to_string(),
-///     "2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"
-/// );
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[allow(non_camel_case_types)]
-#[derive(Debug, Clone)]
-pub struct P2Wpkh_P2Sh<K: IntoDescriptorKey<Segwitv0>>(pub K);
-
-impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
-    fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        descriptor!(sh(wpkh(self.0)))
-    }
-}
-
-/// P2WPKH template. Expands to a descriptor `wpkh(key)`
-///
-/// ## Example
-///
-/// ```
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::Wallet;
-/// # use bdk_wallet::KeychainKind;
-/// use bdk_wallet::template::P2Wpkh;
-///
-/// let key_external =
-///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let key_internal =
-///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet = Wallet::create(P2Wpkh(key_external), P2Wpkh(key_internal))
-///     .network(Network::Testnet)
-///     .create_wallet_no_persist()?;
-///
-/// assert_eq!(
-///     wallet
-///         .next_unused_address(KeychainKind::External)
-///         .to_string(),
-///     "tb1q4525hmgw265tl3drrl8jjta7ayffu6jf68ltjd"
-/// );
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct P2Wpkh<K: IntoDescriptorKey<Segwitv0>>(pub K);
-
-impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
-    fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        descriptor!(wpkh(self.0))
-    }
-}
-
-/// P2TR template. Expands to a descriptor `tr(key)`
-///
-/// ## Example
-///
-/// ```
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::Wallet;
-/// # use bdk_wallet::KeychainKind;
-/// use bdk_wallet::template::P2TR;
-///
-/// let key_external =
-///     bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
-/// let key_internal =
-///     bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
-/// let mut wallet = Wallet::create(P2TR(key_external), P2TR(key_internal))
-///     .network(Network::Testnet)
-///     .create_wallet_no_persist()?;
-///
-/// assert_eq!(
-///     wallet
-///         .next_unused_address(KeychainKind::External)
-///         .to_string(),
-///     "tb1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xq7hps46"
-/// );
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct P2TR<K: IntoDescriptorKey<Tap>>(pub K);
-
-impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
-    fn build(self, _network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        descriptor!(tr(self.0))
-    }
-}
-
-/// BIP44 template. Expands to `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
-///
-/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
-///
-/// See [`Bip44Public`] for a template that can work with a `xpub`/`tpub`.
-///
-/// ## Example
-///
-/// ```rust
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip44;
-///
-/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::create(Bip44(key.clone(), KeychainKind::External), Bip44(key, KeychainKind::Internal))
-///     .network(Network::Testnet)
-///     .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "mmogjc7HJEZkrLqyQYqJmxUqFaC7i4uf89");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDCuorCpzvYS2LCD75BR46KHE8GdDeg1wsAgNZeNr6DaB5gQK1o14uErKwKLuFmeemkQ6N2m3rNgvctdJLyr7nwu2yia7413Hhg8WWE44cgT/0/*)#5wrnv0xt");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
-
-impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Pkh(legacy::make_bipxx_private(44, self.0, self.1, network)?).build(network)
-    }
-}
-
-/// BIP44 public template. Expands to `pkh(key/{0,1}/*)`
-///
-/// This assumes that the key used has already been derived with `m/44'/0'/0'` for Mainnet or `m/44'/1'/0'` for Testnet.
-///
-/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
-///
-/// See [`Bip44`] for a template that does the full derivation, but requires private data
-/// for the key.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{KeychainKind, Wallet};
-/// use bdk_wallet::template::Bip44Public;
-///
-/// let key = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
-/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::create(
-///     Bip44Public(key.clone(), fingerprint, KeychainKind::External),
-///     Bip44Public(key, fingerprint, KeychainKind::Internal),
-///     )
-///     .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "miNG7dJTzJqNbFS19svRdTCisC65dsubtR");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "pkh([c55b303f/44'/1'/0']tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU/0/*)#cfhumdqz");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
-
-impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Pkh(legacy::make_bipxx_public(
-            44, self.0, self.1, self.2, network,
-        )?)
-        .build(network)
-    }
-}
-
-/// BIP49 template. Expands to `sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*))`
-///
-/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
-///
-/// See [`Bip49Public`] for a template that can work with a `xpub`/`tpub`.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip49;
-///
-/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::create(
-///     Bip49(key.clone(), KeychainKind::External),
-///     Bip49(key, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N4zkWAoGdUv4NXhSsU8DvS5MB36T8nKHEB");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDDYr4kdnZgjjShzYNjZUZXUUtpXaofdkMaipyS8ThEh45qFmhT4hKYways7UXmg6V7het1QiFo9kf4kYUXyDvV4rHEyvSpys9pjCB3pukxi/0/*))#s9vxlc8e");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
-
-impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Wpkh_P2Sh(segwit_v0::make_bipxx_private(49, self.0, self.1, network)?).build(network)
-    }
-}
-
-/// BIP49 public template. Expands to `sh(wpkh(key/{0,1}/*))`
-///
-/// This assumes that the key used has already been derived with `m/49'/0'/0'` for Mainnet or `m/49'/1'/0'` for Testnet.
-///
-/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
-///
-/// See [`Bip49`] for a template that does the full derivation, but requires private data
-/// for the key.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip49Public;
-///
-/// let key = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
-/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::create(
-///     Bip49Public(key.clone(), fingerprint, KeychainKind::External),
-///     Bip49Public(key, fingerprint, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "sh(wpkh([c55b303f/49'/1'/0']tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L/0/*))#3tka9g0q");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
-
-impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Wpkh_P2Sh(segwit_v0::make_bipxx_public(
-            49, self.0, self.1, self.2, network,
-        )?)
-        .build(network)
-    }
-}
-
-/// BIP84 template. Expands to `wpkh(key/84'/{0,1}'/0'/{0,1}/*)`
-///
-/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
-///
-/// See [`Bip84Public`] for a template that can work with a `xpub`/`tpub`.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip84;
-///
-/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::create(
-///     Bip84(key.clone(), KeychainKind::External),
-///     Bip84(key, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qhl85z42h7r4su5u37rvvw0gk8j2t3n9y7zsg4n");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDDc5mum24DekpNw92t6fHGp8Gr2JjF9J7i4TZBtN6Vp8xpAULG5CFaKsfugWa5imhrQQUZKXe261asP5koDHo5bs3qNTmf3U3o4v9SaB8gg/0/*)#6kfecsmr");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
-
-impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Wpkh(segwit_v0::make_bipxx_private(84, self.0, self.1, network)?).build(network)
-    }
-}
-
-/// BIP84 public template. Expands to `wpkh(key/{0,1}/*)`
-///
-/// This assumes that the key used has already been derived with `m/84'/0'/0'` for Mainnet or `m/84'/1'/0'` for Testnet.
-///
-/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
-///
-/// See [`Bip84`] for a template that does the full derivation, but requires private data
-/// for the key.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip84Public;
-///
-/// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
-/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::create(
-///     Bip84Public(key.clone(), fingerprint, KeychainKind::External),
-///     Bip84Public(key, fingerprint, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2pr6y4qc7");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "wpkh([c55b303f/84'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#dhu402yv");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
-
-impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2Wpkh(segwit_v0::make_bipxx_public(
-            84, self.0, self.1, self.2, network,
-        )?)
-        .build(network)
-    }
-}
-
-/// BIP86 template. Expands to `tr(key/86'/{0,1}'/0'/{0,1}/*)`
-///
-/// Since there are hardened derivation steps, this template requires a private derivable key (generally a `xprv`/`tprv`).
-///
-/// See [`Bip86Public`] for a template that can work with a `xpub`/`tpub`.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip86;
-///
-/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
-/// let mut wallet = Wallet::create(
-///     Bip86(key.clone(), KeychainKind::External),
-///     Bip86(key, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1p5unlj09djx8xsjwe97269kqtxqpwpu2epeskgqjfk4lnf69v4tnqpp35qu");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDCiHofpEs47kx358bPdJmTZHmCDqQ8qw32upCSxHrSEdeeBs2T5Mq6QMB2ukeMqhNBiyhosBvJErteVhfURPGXPv3qLJPw5MVpHUewsbP2m/0/*)#dkgvr5hm");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
-
-impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2TR(segwit_v1::make_bipxx_private(86, self.0, self.1, network)?).build(network)
-    }
-}
-
-/// BIP86 public template. Expands to `tr(key/{0,1}/*)`
-///
-/// This assumes that the key used has already been derived with `m/86'/0'/0'` for Mainnet or `m/86'/1'/0'` for Testnet.
-///
-/// This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
-///
-/// See [`Bip86`] for a template that does the full derivation, but requires private data
-/// for the key.
-///
-/// ## Example
-///
-/// ```
-/// # use std::str::FromStr;
-/// # use bdk_wallet::bitcoin::{PrivateKey, Network};
-/// # use bdk_wallet::{Wallet, KeychainKind};
-/// use bdk_wallet::template::Bip86Public;
-///
-/// let key = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
-/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
-/// let mut wallet = Wallet::create(
-///     Bip86Public(key.clone(), fingerprint, KeychainKind::External),
-///     Bip86Public(key, fingerprint, KeychainKind::Internal),
-/// )
-/// .network(Network::Testnet)
-/// .create_wallet_no_persist()?;
-///
-/// assert_eq!(wallet.next_unused_address(KeychainKind::External).to_string(), "tb1pwjp9f2k5n0xq73ecuu0c5njvgqr3vkh7yaylmpqvsuuaafymh0msvcmh37");
-/// assert_eq!(wallet.public_descriptor(KeychainKind::External).to_string(), "tr([c55b303f/86'/1'/0']tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q/0/*)#2p65srku");
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-#[derive(Debug, Clone)]
-pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
-
-impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {
-    fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
-        P2TR(segwit_v1::make_bipxx_public(
-            86, self.0, self.1, self.2, network,
-        )?)
-        .build(network)
-    }
-}
-
-macro_rules! expand_make_bipxx {
-    ( $mod_name:ident, $ctx:ty ) => {
-        mod $mod_name {
-            use super::*;
-
-            pub(super) fn make_bipxx_private<K: DerivableKey<$ctx>>(
-                bip: u32,
-                key: K,
-                keychain: KeychainKind,
-                network: Network,
-            ) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
-                let mut derivation_path = alloc::vec::Vec::with_capacity(4);
-                derivation_path.push(bip32::ChildNumber::from_hardened_idx(bip)?);
-
-                match network {
-                    Network::Bitcoin => {
-                        derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);
-                    }
-                    _ => {
-                        derivation_path.push(bip32::ChildNumber::from_hardened_idx(1)?);
-                    }
-                }
-                derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);
-
-                match keychain {
-                    KeychainKind::External => {
-                        derivation_path.push(bip32::ChildNumber::from_normal_idx(0)?)
-                    }
-                    KeychainKind::Internal => {
-                        derivation_path.push(bip32::ChildNumber::from_normal_idx(1)?)
-                    }
-                };
-
-                let derivation_path: bip32::DerivationPath = derivation_path.into();
-
-                Ok((key, derivation_path))
-            }
-            pub(super) fn make_bipxx_public<K: DerivableKey<$ctx>>(
-                bip: u32,
-                key: K,
-                parent_fingerprint: bip32::Fingerprint,
-                keychain: KeychainKind,
-                network: Network,
-            ) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
-                let derivation_path: bip32::DerivationPath = match keychain {
-                    KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
-                    KeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
-                };
-
-                let source_path = bip32::DerivationPath::from(vec![
-                    bip32::ChildNumber::from_hardened_idx(bip)?,
-                    match network {
-                        Network::Bitcoin => bip32::ChildNumber::from_hardened_idx(0)?,
-                        _ => bip32::ChildNumber::from_hardened_idx(1)?,
-                    },
-                    bip32::ChildNumber::from_hardened_idx(0)?,
-                ]);
-
-                Ok((key, (parent_fingerprint, source_path), derivation_path))
-            }
-        }
-    };
-}
-
-expand_make_bipxx!(legacy, Legacy);
-expand_make_bipxx!(segwit_v0, Segwitv0);
-expand_make_bipxx!(segwit_v1, Tap);
-
-#[cfg(test)]
-mod test {
-    // test existing descriptor templates, make sure they are expanded to the right descriptors
-
-    use alloc::{string::ToString, vec::Vec};
-    use core::str::FromStr;
-
-    use super::*;
-    use crate::descriptor::{DescriptorError, DescriptorMeta};
-    use crate::keys::ValidNetworks;
-    use assert_matches::assert_matches;
-    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
-    use miniscript::Descriptor;
-
-    // BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
-    #[test]
-    fn test_bip44_template_cointype() {
-        use bitcoin::bip32::ChildNumber::{self, Hardened};
-
-        let xprvkey = bitcoin::bip32::Xpriv::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap();
-        assert!(xprvkey.network.is_mainnet());
-        let xdesc = Bip44(xprvkey, KeychainKind::Internal)
-            .build(Network::Bitcoin)
-            .unwrap();
-
-        if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
-            let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
-            let purpose = path.first().unwrap();
-            assert_matches!(purpose, Hardened { index: 44 });
-            let coin_type = path.get(1).unwrap();
-            assert_matches!(coin_type, Hardened { index: 0 });
-        }
-
-        let tprvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        assert!(!tprvkey.network.is_mainnet());
-        let tdesc = Bip44(tprvkey, KeychainKind::Internal)
-            .build(Network::Testnet)
-            .unwrap();
-
-        if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
-            let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
-            let purpose = path.first().unwrap();
-            assert_matches!(purpose, Hardened { index: 44 });
-            let coin_type = path.get(1).unwrap();
-            assert_matches!(coin_type, Hardened { index: 1 });
-        }
-    }
-
-    // verify template descriptor generates expected address(es)
-    fn check(
-        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
-        is_witness: bool,
-        is_taproot: bool,
-        is_fixed: bool,
-        network: Network,
-        expected: &[&str],
-    ) {
-        let (desc, _key_map, _networks) = desc.unwrap();
-        assert_eq!(desc.is_witness(), is_witness);
-        assert_eq!(desc.is_taproot(), is_taproot);
-        assert_eq!(!desc.has_wildcard(), is_fixed);
-        for i in 0..expected.len() {
-            let index = i as u32;
-            let child_desc = if !desc.has_wildcard() {
-                desc.at_derivation_index(0).unwrap()
-            } else {
-                desc.at_derivation_index(index).unwrap()
-            };
-            let address = child_desc.address(network).unwrap();
-            assert_eq!(address.to_string(), *expected.get(i).unwrap());
-        }
-    }
-
-    // P2PKH
-    #[test]
-    fn test_p2ph_template() {
-        let prvkey =
-            bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
-                .unwrap();
-        check(
-            P2Pkh(prvkey).build(Network::Bitcoin),
-            false,
-            false,
-            true,
-            Network::Regtest,
-            &["mwJ8hxFYW19JLuc65RCTaP4v1rzVU8cVMT"],
-        );
-
-        let pubkey = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        check(
-            P2Pkh(pubkey).build(Network::Bitcoin),
-            false,
-            false,
-            true,
-            Network::Regtest,
-            &["muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi"],
-        );
-    }
-
-    // P2WPKH-P2SH `sh(wpkh(key))`
-    #[test]
-    fn test_p2wphp2sh_template() {
-        let prvkey =
-            bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
-                .unwrap();
-        check(
-            P2Wpkh_P2Sh(prvkey).build(Network::Bitcoin),
-            true,
-            false,
-            true,
-            Network::Regtest,
-            &["2NB4ox5VDRw1ecUv6SnT3VQHPXveYztRqk5"],
-        );
-
-        let pubkey = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        check(
-            P2Wpkh_P2Sh(pubkey).build(Network::Bitcoin),
-            true,
-            false,
-            true,
-            Network::Regtest,
-            &["2N5LiC3CqzxDamRTPG1kiNv1FpNJQ7x28sb"],
-        );
-    }
-
-    // P2WPKH `wpkh(key)`
-    #[test]
-    fn test_p2wph_template() {
-        let prvkey =
-            bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
-                .unwrap();
-        check(
-            P2Wpkh(prvkey).build(Network::Bitcoin),
-            true,
-            false,
-            true,
-            Network::Regtest,
-            &["bcrt1q4525hmgw265tl3drrl8jjta7ayffu6jfcwxx9y"],
-        );
-
-        let pubkey = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        check(
-            P2Wpkh(pubkey).build(Network::Bitcoin),
-            true,
-            false,
-            true,
-            Network::Regtest,
-            &["bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h"],
-        );
-    }
-
-    // P2TR `tr(key)`
-    #[test]
-    fn test_p2tr_template() {
-        let prvkey =
-            bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")
-                .unwrap();
-        check(
-            P2TR(prvkey).build(Network::Bitcoin),
-            false,
-            true,
-            true,
-            Network::Regtest,
-            &["bcrt1pvjf9t34fznr53u5tqhejz4nr69luzkhlvsdsdfq9pglutrpve2xqnwtkqq"],
-        );
-
-        let pubkey = bitcoin::PublicKey::from_str(
-            "03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd",
-        )
-        .unwrap();
-        check(
-            P2TR(pubkey).build(Network::Bitcoin),
-            false,
-            true,
-            true,
-            Network::Regtest,
-            &["bcrt1pw74tdcrxlzn5r8z6ku2vztr86fgq0m245s72mjktf4afwzsf8ugs4evwdf"],
-        );
-    }
-
-    // BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
-    #[test]
-    fn test_bip44_template() {
-        let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        check(
-            Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
-            false,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "n453VtnjDHPyDt2fDstKSu7A3YCJoHZ5g5",
-                "mvfrrumXgTtwFPWDNUecBBgzuMXhYM7KRP",
-                "mzYvhRAuQqbdSKMVVzXNYyqihgNdRadAUQ",
-            ],
-        );
-        check(
-            Bip44(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
-            false,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "muHF98X9KxEzdKrnFAX85KeHv96eXopaip",
-                "n4hpyLJE5ub6B5Bymv4eqFxS5KjrewSmYR",
-                "mgvkdv1ffmsXd2B1sRKQ5dByK3SzpG42rA",
-            ],
-        );
-    }
-
-    // BIP44 public `pkh(key/{0,1}/*)`
-    #[test]
-    fn test_bip44_public_template() {
-        let pubkey = bitcoin::bip32::Xpub::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
-        let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
-        check(
-            Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
-            false,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "miNG7dJTzJqNbFS19svRdTCisC65dsubtR",
-                "n2UqaDbCjWSFJvpC84m3FjUk5UaeibCzYg",
-                "muCPpS6Ue7nkzeJMWDViw7Lkwr92Yc4K8g",
-            ],
-        );
-        check(
-            Bip44Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
-            false,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "moDr3vJ8wpt5nNxSK55MPq797nXJb2Ru9H",
-                "ms7A1Yt4uTezT2XkefW12AvLoko8WfNJMG",
-                "mhYiyat2rtEnV77cFfQsW32y1m2ceCGHPo",
-            ],
-        );
-    }
-
-    // BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))`
-    #[test]
-    fn test_bip49_template() {
-        let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        check(
-            Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "2N9bCAJXGm168MjVwpkBdNt6ucka3PKVoUV",
-                "2NDckYkqrYyDMtttEav5hB3Bfw9EGAW5HtS",
-                "2NAFTVtksF9T4a97M7nyCjwUBD24QevZ5Z4",
-            ],
-        );
-        check(
-            Bip49(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "2NB3pA8PnzJLGV8YEKNDFpbViZv3Bm1K6CG",
-                "2NBiX2Wzxngb5rPiWpUiJQ2uLVB4HBjFD4p",
-                "2NA8ek4CdQ6aMkveYF6AYuEYNrftB47QGTn",
-            ],
-        );
-    }
-
-    // BIP49 public `sh(wpkh(key/{0,1}/*))`
-    #[test]
-    fn test_bip49_public_template() {
-        let pubkey = bitcoin::bip32::Xpub::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
-        let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
-        check(
-            Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "2N3K4xbVAHoiTQSwxkZjWDfKoNC27pLkYnt",
-                "2NCTQfJ1sZa3wQ3pPseYRHbaNEpC3AquEfX",
-                "2MveFxAuC8BYPzTybx7FxSzW8HSd8ATT4z7",
-            ],
-        );
-        check(
-            Bip49Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "2NF2vttKibwyxigxtx95Zw8K7JhDbo5zPVJ",
-                "2Mtmyd8taksxNVWCJ4wVvaiss7QPZGcAJuH",
-                "2NBs3CTVYPr1HCzjB4YFsnWCPCtNg8uMEfp",
-            ],
-        );
-    }
-
-    // BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)`
-    #[test]
-    fn test_bip84_template() {
-        let prvkey = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-        check(
-            Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s",
-                "bcrt1qx0v6zgfwe50m4kqc58cqzcyem7ay2sfl3gvqhp",
-                "bcrt1q4h7fq9zhxst6e69p3n882nfj649l7w9g3zccfp",
-            ],
-        );
-        check(
-            Bip84(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa",
-                "bcrt1qqqasfhxpkkf7zrxqnkr2sfhn74dgsrc3e3ky45",
-                "bcrt1qpks7n0gq74hsgsz3phn5vuazjjq0f5eqhsgyce",
-            ],
-        );
-    }
-
-    // BIP84 public `wpkh(key/{0,1}/*)`
-    #[test]
-    fn test_bip84_public_template() {
-        let pubkey = bitcoin::bip32::Xpub::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
-        let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
-        check(
-            Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "bcrt1qedg9fdlf8cnnqfd5mks6uz5w4kgpk2prcdvd0h",
-                "bcrt1q3lncdlwq3lgcaaeyruynjnlccr0ve0kakh6ana",
-                "bcrt1qt9800y6xl3922jy3uyl0z33jh5wfpycyhcylr9",
-            ],
-        );
-        check(
-            Bip84Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
-            true,
-            false,
-            false,
-            Network::Regtest,
-            &[
-                "bcrt1qm6wqukenh7guu792lj2njgw9n78cmwsy8xy3z2",
-                "bcrt1q694twxtjn4nnrvnyvra769j0a23rllj5c6cgwp",
-                "bcrt1qhlac3c5ranv5w5emlnqs7wxhkxt8maelylcarp",
-            ],
-        );
-    }
-
-    // BIP86 `tr(key/86'/0'/0'/{0,1}/*)`
-    // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
-    #[test]
-    fn test_bip86_template() {
-        let prvkey = bitcoin::bip32::Xpriv::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
-        check(
-            Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
-            false,
-            true,
-            false,
-            Network::Bitcoin,
-            &[
-                "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
-                "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
-                "bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
-            ],
-        );
-        check(
-            Bip86(prvkey, KeychainKind::Internal).build(Network::Bitcoin),
-            false,
-            true,
-            false,
-            Network::Bitcoin,
-            &[
-                "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
-                "bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
-                "bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
-            ],
-        );
-    }
-
-    // BIP86 public `tr(key/{0,1}/*)`
-    // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
-    #[test]
-    fn test_bip86_public_template() {
-        let pubkey = bitcoin::bip32::Xpub::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
-        let fingerprint = bitcoin::bip32::Fingerprint::from_str("73c5da0a").unwrap();
-        check(
-            Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
-            false,
-            true,
-            false,
-            Network::Bitcoin,
-            &[
-                "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
-                "bc1p4qhjn9zdvkux4e44uhx8tc55attvtyu358kutcqkudyccelu0was9fqzwh",
-                "bc1p0d0rhyynq0awa9m8cqrcr8f5nxqx3aw29w4ru5u9my3h0sfygnzs9khxz8",
-            ],
-        );
-        check(
-            Bip86Public(pubkey, fingerprint, KeychainKind::Internal).build(Network::Bitcoin),
-            false,
-            true,
-            false,
-            Network::Bitcoin,
-            &[
-                "bc1p3qkhfews2uk44qtvauqyr2ttdsw7svhkl9nkm9s9c3x4ax5h60wqwruhk7",
-                "bc1ptdg60grjk9t3qqcqczp4tlyy3z47yrx9nhlrjsmw36q5a72lhdrs9f00nj",
-                "bc1pgcwgsu8naxp7xlp5p7ufzs7emtfza2las7r2e7krzjhe5qj5xz2q88kmk5",
-            ],
-        );
-    }
-}
diff --git a/crates/wallet/src/keys/bip39.rs b/crates/wallet/src/keys/bip39.rs
deleted file mode 100644 (file)
index 7ec3c0e..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! BIP-0039
-
-// TODO: maybe write our own implementation of bip39? Seems stupid to have an extra dependency for
-// something that should be fairly simple to re-implement.
-
-use alloc::string::String;
-use bitcoin::bip32;
-use bitcoin::Network;
-
-use miniscript::ScriptContext;
-
-pub use bip39::{Error, Language, Mnemonic};
-
-type Seed = [u8; 64];
-
-/// Type describing entropy length (aka word count) in the mnemonic
-pub enum WordCount {
-    /// 12 words mnemonic (128 bits entropy)
-    Words12 = 128,
-    /// 15 words mnemonic (160 bits entropy)
-    Words15 = 160,
-    /// 18 words mnemonic (192 bits entropy)
-    Words18 = 192,
-    /// 21 words mnemonic (224 bits entropy)
-    Words21 = 224,
-    /// 24 words mnemonic (256 bits entropy)
-    Words24 = 256,
-}
-
-use super::{
-    any_network, DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey, KeyError,
-};
-
-fn set_valid_on_any_network<Ctx: ScriptContext>(
-    descriptor_key: DescriptorKey<Ctx>,
-) -> DescriptorKey<Ctx> {
-    // We have to pick one network to build the xprv, but since the bip39 standard doesn't
-    // encode the network, the xprv we create is actually valid everywhere. So we override the
-    // valid networks with `any_network()`.
-    descriptor_key.override_valid_networks(any_network())
-}
-
-/// Type for a BIP39 mnemonic with an optional passphrase
-pub type MnemonicWithPassphrase = (Mnemonic, Option<String>);
-
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for Seed {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        Ok(bip32::Xpriv::new_master(Network::Bitcoin, &self[..])?.into())
-    }
-
-    fn into_descriptor_key(
-        self,
-        source: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let descriptor_key = self
-            .into_extended_key()?
-            .into_descriptor_key(source, derivation_path)?;
-
-        Ok(set_valid_on_any_network(descriptor_key))
-    }
-}
-
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for MnemonicWithPassphrase {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        let (mnemonic, passphrase) = self;
-        let seed: Seed = mnemonic.to_seed(passphrase.as_deref().unwrap_or(""));
-
-        seed.into_extended_key()
-    }
-
-    fn into_descriptor_key(
-        self,
-        source: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let descriptor_key = self
-            .into_extended_key()?
-            .into_descriptor_key(source, derivation_path)?;
-
-        Ok(set_valid_on_any_network(descriptor_key))
-    }
-}
-
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for (GeneratedKey<Mnemonic, Ctx>, Option<String>) {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        let (mnemonic, passphrase) = self;
-        (mnemonic.into_key(), passphrase).into_extended_key()
-    }
-
-    fn into_descriptor_key(
-        self,
-        source: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let (mnemonic, passphrase) = self;
-        (mnemonic.into_key(), passphrase).into_descriptor_key(source, derivation_path)
-    }
-}
-
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for Mnemonic {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        (self, None).into_extended_key()
-    }
-
-    fn into_descriptor_key(
-        self,
-        source: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let descriptor_key = self
-            .into_extended_key()?
-            .into_descriptor_key(source, derivation_path)?;
-
-        Ok(set_valid_on_any_network(descriptor_key))
-    }
-}
-
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-impl<Ctx: ScriptContext> GeneratableKey<Ctx> for Mnemonic {
-    type Entropy = [u8; 32];
-
-    type Options = (WordCount, Language);
-    type Error = Option<bip39::Error>;
-
-    fn generate_with_entropy(
-        (word_count, language): Self::Options,
-        entropy: Self::Entropy,
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        let entropy = &entropy[..(word_count as usize / 8)];
-        let mnemonic = Mnemonic::from_entropy_in(language, entropy)?;
-
-        Ok(GeneratedKey::new(mnemonic, any_network()))
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use alloc::string::ToString;
-    use core::str::FromStr;
-
-    use bitcoin::bip32;
-
-    use bip39::{Language, Mnemonic};
-
-    use crate::keys::{any_network, GeneratableKey, GeneratedKey};
-
-    use super::WordCount;
-
-    #[test]
-    fn test_keys_bip39_mnemonic() {
-        let mnemonic =
-            "aim bunker wash balance finish force paper analyst cabin spoon stable organ";
-        let mnemonic = Mnemonic::parse_in(Language::English, mnemonic).unwrap();
-        let path = bip32::DerivationPath::from_str("m/44'/0'/0'/0").unwrap();
-
-        let key = (mnemonic, path);
-        let (desc, keys, networks) = crate::descriptor!(wpkh(key)).unwrap();
-        assert_eq!(desc.to_string(), "wpkh([be83839f/44'/0'/0']xpub6DCQ1YcqvZtSwGWMrwHELPehjWV3f2MGZ69yBADTxFEUAoLwb5Mp5GniQK6tTp3AgbngVz9zEFbBJUPVnkG7LFYt8QMTfbrNqs6FNEwAPKA/0/*)#0r8v4nkv");
-        assert_eq!(keys.len(), 1);
-        assert_eq!(networks, any_network());
-    }
-
-    #[test]
-    fn test_keys_bip39_mnemonic_passphrase() {
-        let mnemonic =
-            "aim bunker wash balance finish force paper analyst cabin spoon stable organ";
-        let mnemonic = Mnemonic::parse_in(Language::English, mnemonic).unwrap();
-        let path = bip32::DerivationPath::from_str("m/44'/0'/0'/0").unwrap();
-
-        let key = ((mnemonic, Some("passphrase".into())), path);
-        let (desc, keys, networks) = crate::descriptor!(wpkh(key)).unwrap();
-        assert_eq!(desc.to_string(), "wpkh([8f6cb80c/44'/0'/0']xpub6DWYS8bbihFevy29M4cbw4ZR3P5E12jB8R88gBDWCTCNpYiDHhYWNywrCF9VZQYagzPmsZpxXpytzSoxynyeFr4ZyzheVjnpLKuse4fiwZw/0/*)#h0j0tg5m");
-        assert_eq!(keys.len(), 1);
-        assert_eq!(networks, any_network());
-    }
-
-    #[test]
-    fn test_keys_generate_bip39() {
-        let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
-            Mnemonic::generate_with_entropy(
-                (WordCount::Words12, Language::English),
-                crate::keys::test::TEST_ENTROPY,
-            )
-            .unwrap();
-        assert_eq!(generated_mnemonic.valid_networks, any_network());
-        assert_eq!(
-            generated_mnemonic.to_string(),
-            "primary fetch primary fetch primary fetch primary fetch primary fetch primary fever"
-        );
-
-        let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
-            Mnemonic::generate_with_entropy(
-                (WordCount::Words24, Language::English),
-                crate::keys::test::TEST_ENTROPY,
-            )
-            .unwrap();
-        assert_eq!(generated_mnemonic.valid_networks, any_network());
-        assert_eq!(generated_mnemonic.to_string(), "primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary fetch primary foster");
-    }
-
-    #[test]
-    fn test_keys_generate_bip39_random() {
-        let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
-            Mnemonic::generate((WordCount::Words12, Language::English)).unwrap();
-        assert_eq!(generated_mnemonic.valid_networks, any_network());
-
-        let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
-            Mnemonic::generate((WordCount::Words24, Language::English)).unwrap();
-        assert_eq!(generated_mnemonic.valid_networks, any_network());
-    }
-}
diff --git a/crates/wallet/src/keys/mod.rs b/crates/wallet/src/keys/mod.rs
deleted file mode 100644 (file)
index eac3749..0000000
+++ /dev/null
@@ -1,1033 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Key formats
-
-use crate::collections::HashSet;
-use alloc::string::{String, ToString};
-use alloc::vec::Vec;
-use core::any::TypeId;
-use core::fmt;
-use core::marker::PhantomData;
-use core::ops::Deref;
-use core::str::FromStr;
-
-use rand_core::{CryptoRng, RngCore};
-
-use bitcoin::secp256k1::{self, Secp256k1, Signing};
-
-use bitcoin::bip32;
-use bitcoin::{key::XOnlyPublicKey, Network, PrivateKey, PublicKey};
-
-use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
-pub use miniscript::descriptor::{
-    DescriptorPublicKey, DescriptorSecretKey, KeyMap, SinglePriv, SinglePub, SinglePubKey,
-    SortedMultiVec,
-};
-pub use miniscript::ScriptContext;
-use miniscript::{Miniscript, Terminal};
-
-use crate::descriptor::{CheckMiniscript, DescriptorError};
-use crate::wallet::utils::SecpCtx;
-
-#[cfg(feature = "keys-bip39")]
-#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
-pub mod bip39;
-
-/// Set of valid networks for a key
-pub type ValidNetworks = HashSet<Network>;
-
-/// Create a set containing mainnet, testnet, testnet4, signet, and regtest
-pub fn any_network() -> ValidNetworks {
-    vec![
-        Network::Bitcoin,
-        Network::Testnet,
-        Network::Testnet4,
-        Network::Regtest,
-        Network::Signet,
-    ]
-    .into_iter()
-    .collect()
-}
-/// Create a set only containing mainnet
-pub fn mainnet_network() -> ValidNetworks {
-    vec![Network::Bitcoin].into_iter().collect()
-}
-/// Create a set containing test networks
-pub fn test_networks() -> ValidNetworks {
-    vec![
-        Network::Testnet,
-        Network::Testnet4,
-        Network::Regtest,
-        Network::Signet,
-    ]
-    .into_iter()
-    .collect()
-}
-/// Compute the intersection of two sets
-pub fn merge_networks(a: &ValidNetworks, b: &ValidNetworks) -> ValidNetworks {
-    a.intersection(b).cloned().collect()
-}
-
-/// Container for public or secret keys
-#[derive(Debug)]
-pub enum DescriptorKey<Ctx: ScriptContext> {
-    #[doc(hidden)]
-    Public(DescriptorPublicKey, ValidNetworks, PhantomData<Ctx>),
-    #[doc(hidden)]
-    Secret(DescriptorSecretKey, ValidNetworks, PhantomData<Ctx>),
-}
-
-impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
-    /// Create an instance given a public key and a set of valid networks
-    pub fn from_public(public: DescriptorPublicKey, networks: ValidNetworks) -> Self {
-        DescriptorKey::Public(public, networks, PhantomData)
-    }
-
-    /// Create an instance given a secret key and a set of valid networks
-    pub fn from_secret(secret: DescriptorSecretKey, networks: ValidNetworks) -> Self {
-        DescriptorKey::Secret(secret, networks, PhantomData)
-    }
-
-    /// Override the computed set of valid networks
-    pub fn override_valid_networks(self, networks: ValidNetworks) -> Self {
-        match self {
-            DescriptorKey::Public(key, _, _) => DescriptorKey::Public(key, networks, PhantomData),
-            DescriptorKey::Secret(key, _, _) => DescriptorKey::Secret(key, networks, PhantomData),
-        }
-    }
-
-    // This method is used internally by `bdk_wallet::fragment!` and `bdk_wallet::descriptor!`. It has to be
-    // public because it is effectively called by external crates once the macros are expanded,
-    // but since it is not meant to be part of the public api we hide it from the docs.
-    #[doc(hidden)]
-    pub fn extract(
-        self,
-        secp: &SecpCtx,
-    ) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
-        match self {
-            DescriptorKey::Public(public, valid_networks, _) => {
-                Ok((public, KeyMap::default(), valid_networks))
-            }
-            DescriptorKey::Secret(secret, valid_networks, _) => {
-                let mut key_map = KeyMap::new();
-
-                let public = secret
-                    .to_public(secp)
-                    .map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
-                key_map.insert(public.clone(), secret);
-
-                Ok((public, key_map, valid_networks))
-            }
-        }
-    }
-}
-
-/// Enum representation of the known valid [`ScriptContext`]s
-#[derive(Debug, Eq, PartialEq, Copy, Clone)]
-pub enum ScriptContextEnum {
-    /// Legacy scripts
-    Legacy,
-    /// Segwitv0 scripts
-    Segwitv0,
-    /// Taproot scripts
-    Tap,
-}
-
-impl ScriptContextEnum {
-    /// Returns whether the script context is [`ScriptContextEnum::Legacy`]
-    pub fn is_legacy(&self) -> bool {
-        self == &ScriptContextEnum::Legacy
-    }
-
-    /// Returns whether the script context is [`ScriptContextEnum::Segwitv0`]
-    pub fn is_segwit_v0(&self) -> bool {
-        self == &ScriptContextEnum::Segwitv0
-    }
-
-    /// Returns whether the script context is [`ScriptContextEnum::Tap`]
-    pub fn is_taproot(&self) -> bool {
-        self == &ScriptContextEnum::Tap
-    }
-}
-
-/// Trait that adds extra useful methods to [`ScriptContext`]s
-pub trait ExtScriptContext: ScriptContext {
-    /// Returns the [`ScriptContext`] as a [`ScriptContextEnum`]
-    fn as_enum() -> ScriptContextEnum;
-
-    /// Returns whether the script context is [`Legacy`](miniscript::Legacy)
-    fn is_legacy() -> bool {
-        Self::as_enum().is_legacy()
-    }
-
-    /// Returns whether the script context is [`Segwitv0`](miniscript::Segwitv0)
-    fn is_segwit_v0() -> bool {
-        Self::as_enum().is_segwit_v0()
-    }
-
-    /// Returns whether the script context is [`Tap`](miniscript::Tap), aka Taproot or Segwit V1
-    fn is_taproot() -> bool {
-        Self::as_enum().is_taproot()
-    }
-}
-
-impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
-    fn as_enum() -> ScriptContextEnum {
-        match TypeId::of::<Ctx>() {
-            t if t == TypeId::of::<miniscript::Legacy>() => ScriptContextEnum::Legacy,
-            t if t == TypeId::of::<miniscript::Segwitv0>() => ScriptContextEnum::Segwitv0,
-            t if t == TypeId::of::<miniscript::Tap>() => ScriptContextEnum::Tap,
-            _ => unimplemented!("Unknown ScriptContext type"),
-        }
-    }
-}
-
-/// Trait for objects that can be turned into a public or secret [`DescriptorKey`]
-///
-/// The generic type `Ctx` is used to define the context in which the key is valid: some key
-/// formats, like the mnemonics used by Electrum wallets, encode internally whether the wallet is
-/// legacy or segwit. Thus, trying to turn a valid legacy mnemonic into a `DescriptorKey`
-/// that would become part of a segwit descriptor should fail.
-///
-/// For key types that do care about this, the [`ExtScriptContext`] trait provides some useful
-/// methods that can be used to check at runtime which `Ctx` is being used.
-///
-/// For key types that can do this check statically (because they can only work within a
-/// single `Ctx`), the "specialized" trait can be implemented to make the compiler handle the type
-/// checking.
-///
-/// Keys also have control over the networks they support: constructing the return object with
-/// [`DescriptorKey::from_public`] or [`DescriptorKey::from_secret`] allows to specify a set of
-/// [`ValidNetworks`].
-///
-/// ## Examples
-///
-/// Key type valid in any context:
-///
-/// ```
-/// use bdk_wallet::bitcoin::PublicKey;
-///
-/// use bdk_wallet::keys::{DescriptorKey, IntoDescriptorKey, KeyError, ScriptContext};
-///
-/// pub struct MyKeyType {
-///     pubkey: PublicKey,
-/// }
-///
-/// impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for MyKeyType {
-///     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-///         self.pubkey.into_descriptor_key()
-///     }
-/// }
-/// ```
-///
-/// Key type that is only valid on mainnet:
-///
-/// ```
-/// use bdk_wallet::bitcoin::PublicKey;
-///
-/// use bdk_wallet::keys::{
-///     mainnet_network, DescriptorKey, DescriptorPublicKey, IntoDescriptorKey, KeyError,
-///     ScriptContext, SinglePub, SinglePubKey,
-/// };
-///
-/// pub struct MyKeyType {
-///     pubkey: PublicKey,
-/// }
-///
-/// impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for MyKeyType {
-///     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-///         Ok(DescriptorKey::from_public(
-///             DescriptorPublicKey::Single(SinglePub {
-///                 origin: None,
-///                 key: SinglePubKey::FullKey(self.pubkey),
-///             }),
-///             mainnet_network(),
-///         ))
-///     }
-/// }
-/// ```
-///
-/// Key type that internally encodes in which context it's valid. The context is checked at runtime:
-///
-/// ```
-/// use bdk_wallet::bitcoin::PublicKey;
-///
-/// use bdk_wallet::keys::{
-///     DescriptorKey, ExtScriptContext, IntoDescriptorKey, KeyError, ScriptContext,
-/// };
-///
-/// pub struct MyKeyType {
-///     is_legacy: bool,
-///     pubkey: PublicKey,
-/// }
-///
-/// impl<Ctx: ScriptContext + 'static> IntoDescriptorKey<Ctx> for MyKeyType {
-///     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-///         if Ctx::is_legacy() == self.is_legacy {
-///             self.pubkey.into_descriptor_key()
-///         } else {
-///             Err(KeyError::InvalidScriptContext)
-///         }
-///     }
-/// }
-/// ```
-///
-/// Key type that can only work within [`miniscript::Segwitv0`] context. Only the specialized version
-/// of the trait is implemented.
-///
-/// This example deliberately fails to compile, to demonstrate how the compiler can catch when keys
-/// are misused. In this case, the "segwit-only" key is used to build a `pkh()` descriptor, which
-/// makes the compiler (correctly) fail.
-///
-/// ```compile_fail
-/// use bdk_wallet::bitcoin::PublicKey;
-/// use core::str::FromStr;
-///
-/// use bdk_wallet::keys::{DescriptorKey, IntoDescriptorKey, KeyError};
-///
-/// pub struct MySegwitOnlyKeyType {
-///     pubkey: PublicKey,
-/// }
-///
-/// impl IntoDescriptorKey<bdk_wallet::miniscript::Segwitv0> for MySegwitOnlyKeyType {
-///     fn into_descriptor_key(self) -> Result<DescriptorKey<bdk_wallet::miniscript::Segwitv0>, KeyError> {
-///         self.pubkey.into_descriptor_key()
-///     }
-/// }
-///
-/// let key = MySegwitOnlyKeyType {
-///     pubkey: PublicKey::from_str("...")?,
-/// };
-/// let (descriptor, _, _) = bdk_wallet::descriptor!(pkh(key))?;
-/// //                                               ^^^^^ changing this to `wpkh` would make it compile
-///
-/// # Ok::<_, Box<dyn std::error::Error>>(())
-/// ```
-pub trait IntoDescriptorKey<Ctx: ScriptContext>: Sized {
-    /// Turn the key into a [`DescriptorKey`] within the requested [`ScriptContext`]
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError>;
-}
-
-/// Enum for extended keys that can be either `xprv` or `xpub`
-///
-/// An instance of [`ExtendedKey`] can be constructed from an [`Xpriv`](bip32::Xpriv)
-/// or an [`Xpub`](bip32::Xpub) by using the `From` trait.
-///
-/// Defaults to the [`Legacy`](miniscript::Legacy) context.
-pub enum ExtendedKey<Ctx: ScriptContext = miniscript::Legacy> {
-    /// A private extended key, aka an `xprv`
-    Private((bip32::Xpriv, PhantomData<Ctx>)),
-    /// A public extended key, aka an `xpub`
-    Public((bip32::Xpub, PhantomData<Ctx>)),
-}
-
-impl<Ctx: ScriptContext> ExtendedKey<Ctx> {
-    /// Return whether or not the key contains the private data
-    pub fn has_secret(&self) -> bool {
-        match self {
-            ExtendedKey::Private(_) => true,
-            ExtendedKey::Public(_) => false,
-        }
-    }
-
-    /// Transform the [`ExtendedKey`] into an [`Xpriv`](bip32::Xpriv) for the
-    /// given [`Network`], if the key contains the private data
-    pub fn into_xprv(self, network: Network) -> Option<bip32::Xpriv> {
-        match self {
-            ExtendedKey::Private((mut xprv, _)) => {
-                xprv.network = network.into();
-                Some(xprv)
-            }
-            ExtendedKey::Public(_) => None,
-        }
-    }
-
-    /// Transform the [`ExtendedKey`] into an [`Xpub`](bip32::Xpub) for the
-    /// given [`Network`]
-    pub fn into_xpub<C: Signing>(
-        self,
-        network: bitcoin::Network,
-        secp: &Secp256k1<C>,
-    ) -> bip32::Xpub {
-        let mut xpub = match self {
-            ExtendedKey::Private((xprv, _)) => bip32::Xpub::from_priv(secp, &xprv),
-            ExtendedKey::Public((xpub, _)) => xpub,
-        };
-
-        xpub.network = network.into();
-        xpub
-    }
-}
-
-impl<Ctx: ScriptContext> From<bip32::Xpub> for ExtendedKey<Ctx> {
-    fn from(xpub: bip32::Xpub) -> Self {
-        ExtendedKey::Public((xpub, PhantomData))
-    }
-}
-
-impl<Ctx: ScriptContext> From<bip32::Xpriv> for ExtendedKey<Ctx> {
-    fn from(xprv: bip32::Xpriv) -> Self {
-        ExtendedKey::Private((xprv, PhantomData))
-    }
-}
-
-/// Trait for keys that can be derived.
-///
-/// When extra metadata are provided, a [`DerivableKey`] can be transformed into a
-/// [`DescriptorKey`]: the trait [`IntoDescriptorKey`] is automatically implemented
-/// for `(DerivableKey, DerivationPath)` and
-/// `(DerivableKey, KeySource, DerivationPath)` tuples.
-///
-/// For key types that don't encode any indication about the path to use (like bip39), it's
-/// generally recommended to implement this trait instead of [`IntoDescriptorKey`]. The same
-/// rules regarding script context and valid networks apply.
-///
-/// ## Examples
-///
-/// Key types that can be directly converted into an [`Xpriv`] or
-/// an [`Xpub`] can implement only the required `into_extended_key()` method.
-///
-/// ```
-/// use bdk_wallet::bitcoin;
-/// use bdk_wallet::bitcoin::bip32;
-/// use bdk_wallet::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext};
-///
-/// struct MyCustomKeyType {
-///     key_data: bitcoin::PrivateKey,
-///     chain_code: [u8; 32],
-///     network: bitcoin::Network,
-/// }
-///
-/// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType {
-///     fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-///         let xprv = bip32::Xpriv {
-///             network: self.network.into(),
-///             depth: 0,
-///             parent_fingerprint: bip32::Fingerprint::default(),
-///             private_key: self.key_data.inner,
-///             chain_code: bip32::ChainCode::from(&self.chain_code),
-///             child_number: bip32::ChildNumber::Normal { index: 0 },
-///         };
-///
-///         xprv.into_extended_key()
-///     }
-/// }
-/// ```
-///
-/// Types that don't internally encode the [`Network`] in which they are valid need some extra
-/// steps to override the set of valid networks, otherwise only the network specified in the
-/// [`Xpriv`] or [`Xpub`] will be considered valid.
-///
-/// ```
-/// use bdk_wallet::bitcoin;
-/// use bdk_wallet::bitcoin::bip32;
-/// use bdk_wallet::keys::{
-///     any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext,
-/// };
-///
-/// struct MyCustomKeyType {
-///     key_data: bitcoin::PrivateKey,
-///     chain_code: [u8; 32],
-/// }
-///
-/// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType {
-///     fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-///         let xprv = bip32::Xpriv {
-///             network: bitcoin::Network::Bitcoin.into(), // pick an arbitrary network here
-///             depth: 0,
-///             parent_fingerprint: bip32::Fingerprint::default(),
-///             private_key: self.key_data.inner,
-///             chain_code: bip32::ChainCode::from(&self.chain_code),
-///             child_number: bip32::ChildNumber::Normal { index: 0 },
-///         };
-///
-///         xprv.into_extended_key()
-///     }
-///
-///     fn into_descriptor_key(
-///         self,
-///         source: Option<bip32::KeySource>,
-///         derivation_path: bip32::DerivationPath,
-///     ) -> Result<DescriptorKey<Ctx>, KeyError> {
-///         let descriptor_key = self
-///             .into_extended_key()?
-///             .into_descriptor_key(source, derivation_path)?;
-///
-///         // Override the set of valid networks here
-///         Ok(descriptor_key.override_valid_networks(any_network()))
-///     }
-/// }
-/// ```
-///
-/// [`DerivationPath`]: (bip32::DerivationPath)
-/// [`Xpriv`]: (bip32::Xpriv)
-/// [`Xpub`]: (bip32::Xpub)
-pub trait DerivableKey<Ctx: ScriptContext = miniscript::Legacy>: Sized {
-    /// Consume `self` and turn it into an [`ExtendedKey`]
-    #[cfg_attr(
-        feature = "keys-bip39",
-        doc = r##"
-This can be used to get direct access to `xprv`s and `xpub`s for types that implement this trait,
-like [`Mnemonic`](bip39::Mnemonic) when the `keys-bip39` feature is enabled.
-```rust
-use bdk_wallet::bitcoin::Network;
-use bdk_wallet::keys::{DerivableKey, ExtendedKey};
-use bdk_wallet::keys::bip39::{Mnemonic, Language};
-
-# fn main() -> Result<(), Box<dyn std::error::Error>> {
-let xkey: ExtendedKey =
-    Mnemonic::parse_in(
-        Language::English,
-        "jelly crash boy whisper mouse ecology tuna soccer memory million news short",
-    )?
-    .into_extended_key()?;
-let xprv = xkey.into_xprv(Network::Bitcoin).unwrap();
-# Ok(()) }
-```
-"##
-    )]
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError>;
-
-    /// Consume `self` and turn it into a [`DescriptorKey`] by adding the extra metadata, such as
-    /// key origin and derivation path
-    fn into_descriptor_key(
-        self,
-        origin: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        match self.into_extended_key()? {
-            ExtendedKey::Private((xprv, _)) => DescriptorSecretKey::XPrv(DescriptorXKey {
-                origin,
-                xkey: xprv,
-                derivation_path,
-                wildcard: Wildcard::Unhardened,
-            })
-            .into_descriptor_key(),
-            ExtendedKey::Public((xpub, _)) => DescriptorPublicKey::XPub(DescriptorXKey {
-                origin,
-                xkey: xpub,
-                derivation_path,
-                wildcard: Wildcard::Unhardened,
-            })
-            .into_descriptor_key(),
-        }
-    }
-}
-
-/// Identity conversion
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for ExtendedKey<Ctx> {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        Ok(self)
-    }
-}
-
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for bip32::Xpub {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        Ok(self.into())
-    }
-}
-
-impl<Ctx: ScriptContext> DerivableKey<Ctx> for bip32::Xpriv {
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        Ok(self.into())
-    }
-}
-
-/// Output of a [`GeneratableKey`] key generation
-pub struct GeneratedKey<K, Ctx: ScriptContext> {
-    key: K,
-    valid_networks: ValidNetworks,
-    phantom: PhantomData<Ctx>,
-}
-
-impl<K, Ctx: ScriptContext> GeneratedKey<K, Ctx> {
-    fn new(key: K, valid_networks: ValidNetworks) -> Self {
-        GeneratedKey {
-            key,
-            valid_networks,
-            phantom: PhantomData,
-        }
-    }
-
-    /// Consumes `self` and returns the key
-    pub fn into_key(self) -> K {
-        self.key
-    }
-}
-
-impl<K, Ctx: ScriptContext> Deref for GeneratedKey<K, Ctx> {
-    type Target = K;
-
-    fn deref(&self) -> &Self::Target {
-        &self.key
-    }
-}
-
-impl<K: Clone, Ctx: ScriptContext> Clone for GeneratedKey<K, Ctx> {
-    fn clone(&self) -> GeneratedKey<K, Ctx> {
-        GeneratedKey {
-            key: self.key.clone(),
-            valid_networks: self.valid_networks.clone(),
-            phantom: self.phantom,
-        }
-    }
-}
-
-// Make generated "derivable" keys themselves "derivable". Also make sure they are assigned the
-// right `valid_networks`.
-impl<Ctx, K> DerivableKey<Ctx> for GeneratedKey<K, Ctx>
-where
-    Ctx: ScriptContext,
-    K: DerivableKey<Ctx>,
-{
-    fn into_extended_key(self) -> Result<ExtendedKey<Ctx>, KeyError> {
-        self.key.into_extended_key()
-    }
-
-    fn into_descriptor_key(
-        self,
-        origin: Option<bip32::KeySource>,
-        derivation_path: bip32::DerivationPath,
-    ) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let descriptor_key = self.key.into_descriptor_key(origin, derivation_path)?;
-        Ok(descriptor_key.override_valid_networks(self.valid_networks))
-    }
-}
-
-// Make generated keys directly usable in descriptors, and make sure they get assigned the right
-// `valid_networks`.
-impl<Ctx, K> IntoDescriptorKey<Ctx> for GeneratedKey<K, Ctx>
-where
-    Ctx: ScriptContext,
-    K: IntoDescriptorKey<Ctx>,
-{
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let desc_key = self.key.into_descriptor_key()?;
-        Ok(desc_key.override_valid_networks(self.valid_networks))
-    }
-}
-
-/// Trait for keys that can be generated
-///
-/// The same rules about [`ScriptContext`] and [`ValidNetworks`] from [`IntoDescriptorKey`] apply.
-///
-/// This trait is particularly useful when combined with [`DerivableKey`]: if `Self`
-/// implements it, the returned [`GeneratedKey`] will also implement it. The same is true for
-/// [`IntoDescriptorKey`]: the generated keys can be directly used in descriptors if `Self` is also
-/// [`IntoDescriptorKey`].
-pub trait GeneratableKey<Ctx: ScriptContext>: Sized {
-    /// Type specifying the amount of entropy required e.g. `[u8;32]`
-    type Entropy: AsMut<[u8]> + Default;
-
-    /// Extra options required by the `generate_with_entropy`
-    type Options;
-    /// Returned error in case of failure
-    type Error: core::fmt::Debug;
-
-    /// Generate a key given the extra options and the entropy
-    fn generate_with_entropy(
-        options: Self::Options,
-        entropy: Self::Entropy,
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error>;
-
-    /// Generate a key given the options with random entropy.
-    ///
-    /// Uses the thread-local random number generator.
-    #[cfg(feature = "std")]
-    fn generate(options: Self::Options) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        Self::generate_with_aux_rand(options, &mut bitcoin::key::rand::thread_rng())
-    }
-
-    /// Generate a key given the options with random entropy.
-    ///
-    /// Uses a provided random number generator (rng).
-    fn generate_with_aux_rand(
-        options: Self::Options,
-        rng: &mut (impl CryptoRng + RngCore),
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        let mut entropy = Self::Entropy::default();
-        rng.fill_bytes(entropy.as_mut());
-        Self::generate_with_entropy(options, entropy)
-    }
-}
-
-/// Trait that allows generating a key with the default options
-///
-/// This trait is automatically implemented if the [`GeneratableKey::Options`] implements [`Default`].
-pub trait GeneratableDefaultOptions<Ctx>: GeneratableKey<Ctx>
-where
-    Ctx: ScriptContext,
-    <Self as GeneratableKey<Ctx>>::Options: Default,
-{
-    /// Generate a key with the default options and a given entropy
-    fn generate_with_entropy_default(
-        entropy: Self::Entropy,
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        Self::generate_with_entropy(Default::default(), entropy)
-    }
-
-    /// Generate a key with the default options and a random entropy
-    ///
-    /// Uses the thread-local random number generator.
-    #[cfg(feature = "std")]
-    fn generate_default() -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        Self::generate_with_aux_rand(Default::default(), &mut bitcoin::key::rand::thread_rng())
-    }
-
-    /// Generate a key with the default options and a random entropy
-    ///
-    /// Uses a provided random number generator (rng).
-    fn generate_default_with_aux_rand(
-        rng: &mut (impl CryptoRng + RngCore),
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        Self::generate_with_aux_rand(Default::default(), rng)
-    }
-}
-
-/// Automatic implementation of [`GeneratableDefaultOptions`] for [`GeneratableKey`]s where
-/// `Options` implements `Default`
-impl<Ctx, K> GeneratableDefaultOptions<Ctx> for K
-where
-    Ctx: ScriptContext,
-    K: GeneratableKey<Ctx>,
-    <K as GeneratableKey<Ctx>>::Options: Default,
-{
-}
-
-impl<Ctx: ScriptContext> GeneratableKey<Ctx> for bip32::Xpriv {
-    type Entropy = [u8; 32];
-
-    type Options = ();
-    type Error = bip32::Error;
-
-    fn generate_with_entropy(
-        _: Self::Options,
-        entropy: Self::Entropy,
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        // pick a arbitrary network here, but say that we support all of them
-        let xprv = bip32::Xpriv::new_master(Network::Bitcoin, entropy.as_ref())?;
-        Ok(GeneratedKey::new(xprv, any_network()))
-    }
-}
-
-/// Options for generating a [`PrivateKey`]
-///
-/// Defaults to creating compressed keys, which save on-chain bytes and fees
-#[derive(Debug, Copy, Clone)]
-pub struct PrivateKeyGenerateOptions {
-    /// Whether the generated key should be "compressed" or not
-    pub compressed: bool,
-}
-
-impl Default for PrivateKeyGenerateOptions {
-    fn default() -> Self {
-        PrivateKeyGenerateOptions { compressed: true }
-    }
-}
-
-impl<Ctx: ScriptContext> GeneratableKey<Ctx> for PrivateKey {
-    type Entropy = [u8; secp256k1::constants::SECRET_KEY_SIZE];
-
-    type Options = PrivateKeyGenerateOptions;
-    type Error = bip32::Error;
-
-    fn generate_with_entropy(
-        options: Self::Options,
-        entropy: Self::Entropy,
-    ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
-        // pick a arbitrary network here, but say that we support all of them
-        let inner = secp256k1::SecretKey::from_slice(&entropy)?;
-        let private_key = PrivateKey {
-            compressed: options.compressed,
-            network: Network::Bitcoin.into(),
-            inner,
-        };
-
-        Ok(GeneratedKey::new(private_key, any_network()))
-    }
-}
-
-impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> IntoDescriptorKey<Ctx>
-    for (T, bip32::DerivationPath)
-{
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        self.0.into_descriptor_key(None, self.1)
-    }
-}
-
-impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> IntoDescriptorKey<Ctx>
-    for (T, bip32::KeySource, bip32::DerivationPath)
-{
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        self.0.into_descriptor_key(Some(self.1), self.2)
-    }
-}
-
-fn expand_multi_keys<Pk: IntoDescriptorKey<Ctx>, Ctx: ScriptContext>(
-    pks: Vec<Pk>,
-    secp: &SecpCtx,
-) -> Result<(Vec<DescriptorPublicKey>, KeyMap, ValidNetworks), KeyError> {
-    let (pks, key_maps_networks): (Vec<_>, Vec<_>) = pks
-        .into_iter()
-        .map(|key| key.into_descriptor_key()?.extract(secp))
-        .collect::<Result<Vec<_>, _>>()?
-        .into_iter()
-        .map(|(a, b, c)| (a, (b, c)))
-        .unzip();
-
-    let (key_map, valid_networks) = key_maps_networks.into_iter().fold(
-        (KeyMap::default(), any_network()),
-        |(mut keys_acc, net_acc), (key, net)| {
-            keys_acc.extend(key);
-            let net_acc = merge_networks(&net_acc, &net);
-
-            (keys_acc, net_acc)
-        },
-    );
-
-    Ok((pks, key_map, valid_networks))
-}
-
-// Used internally by `bdk_wallet::fragment!` to build `pk_k()` fragments
-#[doc(hidden)]
-pub fn make_pk<Pk: IntoDescriptorKey<Ctx>, Ctx: ScriptContext>(
-    descriptor_key: Pk,
-    secp: &SecpCtx,
-) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), DescriptorError> {
-    let (key, key_map, valid_networks) = descriptor_key.into_descriptor_key()?.extract(secp)?;
-    let minisc = Miniscript::from_ast(Terminal::PkK(key))?;
-
-    minisc.check_miniscript()?;
-
-    Ok((minisc, key_map, valid_networks))
-}
-
-// Used internally by `bdk_wallet::fragment!` to build `pk_h()` fragments
-#[doc(hidden)]
-pub fn make_pkh<Pk: IntoDescriptorKey<Ctx>, Ctx: ScriptContext>(
-    descriptor_key: Pk,
-    secp: &SecpCtx,
-) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), DescriptorError> {
-    let (key, key_map, valid_networks) = descriptor_key.into_descriptor_key()?.extract(secp)?;
-    let minisc = Miniscript::from_ast(Terminal::PkH(key))?;
-
-    minisc.check_miniscript()?;
-
-    Ok((minisc, key_map, valid_networks))
-}
-
-// Used internally by `bdk_wallet::fragment!` to build `multi()` fragments
-#[doc(hidden)]
-pub fn make_multi<
-    Pk: IntoDescriptorKey<Ctx>,
-    Ctx: ScriptContext,
-    V: Fn(usize, Vec<DescriptorPublicKey>) -> Terminal<DescriptorPublicKey, Ctx>,
->(
-    thresh: usize,
-    variant: V,
-    pks: Vec<Pk>,
-    secp: &SecpCtx,
-) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), DescriptorError> {
-    let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?;
-    let minisc = Miniscript::from_ast(variant(thresh, pks))?;
-
-    minisc.check_miniscript()?;
-
-    Ok((minisc, key_map, valid_networks))
-}
-
-// Used internally by `bdk_wallet::descriptor!` to build `sortedmulti()` fragments
-#[doc(hidden)]
-pub fn make_sortedmulti<Pk, Ctx, F>(
-    thresh: usize,
-    pks: Vec<Pk>,
-    build_desc: F,
-    secp: &SecpCtx,
-) -> Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>
-where
-    Pk: IntoDescriptorKey<Ctx>,
-    Ctx: ScriptContext,
-    F: Fn(
-        usize,
-        Vec<DescriptorPublicKey>,
-    ) -> Result<(Descriptor<DescriptorPublicKey>, PhantomData<Ctx>), DescriptorError>,
-{
-    let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?;
-    let descriptor = build_desc(thresh, pks)?.0;
-
-    Ok((descriptor, key_map, valid_networks))
-}
-
-/// The "identity" conversion is used internally by some `bdk_wallet::fragment`s
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorKey<Ctx> {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        Ok(self)
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorPublicKey {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let networks = match self {
-            DescriptorPublicKey::Single(_) => any_network(),
-            DescriptorPublicKey::XPub(DescriptorXKey { xkey, .. }) if xkey.network.is_mainnet() => {
-                mainnet_network()
-            }
-            _ => test_networks(),
-        };
-
-        Ok(DescriptorKey::from_public(self, networks))
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PublicKey {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorPublicKey::Single(SinglePub {
-            key: SinglePubKey::FullKey(self),
-            origin: None,
-        })
-        .into_descriptor_key()
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for XOnlyPublicKey {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorPublicKey::Single(SinglePub {
-            key: SinglePubKey::XOnly(self),
-            origin: None,
-        })
-        .into_descriptor_key()
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorSecretKey {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        let networks = match &self {
-            DescriptorSecretKey::Single(sk) if sk.key.network.is_mainnet() => mainnet_network(),
-            DescriptorSecretKey::XPrv(DescriptorXKey { xkey, .. }) if xkey.network.is_mainnet() => {
-                mainnet_network()
-            }
-            _ => test_networks(),
-        };
-
-        Ok(DescriptorKey::from_secret(self, networks))
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for &'_ str {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorSecretKey::from_str(self)
-            .map_err(|e| KeyError::Message(e.to_string()))?
-            .into_descriptor_key()
-    }
-}
-
-impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PrivateKey {
-    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorSecretKey::Single(SinglePriv {
-            key: self,
-            origin: None,
-        })
-        .into_descriptor_key()
-    }
-}
-
-/// Errors thrown while working with [`keys`](crate::keys)
-#[derive(Debug, PartialEq)]
-pub enum KeyError {
-    /// The key cannot exist in the given script context
-    InvalidScriptContext,
-    /// The key is not valid for the given network
-    InvalidNetwork,
-    /// The key has an invalid checksum
-    InvalidChecksum,
-
-    /// Custom error message
-    Message(String),
-
-    /// BIP32 error
-    Bip32(bitcoin::bip32::Error),
-    /// Miniscript error
-    Miniscript(miniscript::Error),
-}
-
-impl From<miniscript::Error> for KeyError {
-    fn from(err: miniscript::Error) -> Self {
-        KeyError::Miniscript(err)
-    }
-}
-
-impl From<bip32::Error> for KeyError {
-    fn from(err: bip32::Error) -> Self {
-        KeyError::Bip32(err)
-    }
-}
-
-impl fmt::Display for KeyError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::InvalidScriptContext => write!(f, "Invalid script context"),
-            Self::InvalidNetwork => write!(f, "Invalid network"),
-            Self::InvalidChecksum => write!(f, "Invalid checksum"),
-            Self::Message(err) => write!(f, "{}", err),
-            Self::Bip32(err) => write!(f, "BIP32 error: {}", err),
-            Self::Miniscript(err) => write!(f, "Miniscript error: {}", err),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for KeyError {}
-
-#[cfg(test)]
-mod test {
-    use bitcoin::bip32;
-
-    use super::*;
-
-    pub const TEST_ENTROPY: [u8; 32] = [0xAA; 32];
-
-    #[test]
-    fn test_keys_generate_xprv() {
-        let generated_xprv: GeneratedKey<_, miniscript::Segwitv0> =
-            bip32::Xpriv::generate_with_entropy_default(TEST_ENTROPY).unwrap();
-
-        assert_eq!(generated_xprv.valid_networks, any_network());
-        assert_eq!(generated_xprv.to_string(), "xprv9s21ZrQH143K4Xr1cJyqTvuL2FWR8eicgY9boWqMBv8MDVUZ65AXHnzBrK1nyomu6wdcabRgmGTaAKawvhAno1V5FowGpTLVx3jxzE5uk3Q");
-    }
-
-    #[test]
-    fn test_keys_generate_wif() {
-        let generated_wif: GeneratedKey<_, miniscript::Segwitv0> =
-            bitcoin::PrivateKey::generate_with_entropy_default(TEST_ENTROPY).unwrap();
-
-        assert_eq!(generated_wif.valid_networks, any_network());
-        assert_eq!(
-            generated_wif.to_string(),
-            "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch"
-        );
-    }
-
-    #[cfg(feature = "keys-bip39")]
-    #[test]
-    fn test_keys_wif_network_bip39() {
-        let xkey: ExtendedKey = bip39::Mnemonic::parse_in(
-            bip39::Language::English,
-            "jelly crash boy whisper mouse ecology tuna soccer memory million news short",
-        )
-        .unwrap()
-        .into_extended_key()
-        .unwrap();
-        let xprv = xkey.into_xprv(Network::Testnet).unwrap();
-
-        assert_eq!(xprv.network, Network::Testnet.into());
-    }
-}
diff --git a/crates/wallet/src/lib.rs b/crates/wallet/src/lib.rs
deleted file mode 100644 (file)
index d17cc46..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#![doc = include_str!("../README.md")]
-// only enables the `doc_cfg` feature when the `docsrs` configuration attribute is defined
-#![cfg_attr(docsrs, feature(doc_cfg))]
-#![cfg_attr(
-    docsrs,
-    doc(html_logo_url = "https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png")
-)]
-#![no_std]
-#![warn(missing_docs)]
-
-#[cfg(feature = "std")]
-#[macro_use]
-extern crate std;
-
-#[doc(hidden)]
-#[macro_use]
-pub extern crate alloc;
-pub extern crate bdk_chain as chain;
-#[cfg(feature = "file_store")]
-pub extern crate bdk_file_store as file_store;
-#[cfg(feature = "keys-bip39")]
-pub extern crate bip39;
-pub extern crate bitcoin;
-pub extern crate miniscript;
-pub extern crate serde;
-pub extern crate serde_json;
-
-pub mod descriptor;
-pub mod keys;
-pub mod psbt;
-#[cfg(feature = "test-utils")]
-pub mod test_utils;
-mod types;
-mod wallet;
-
-pub(crate) use bdk_chain::collections;
-#[cfg(feature = "rusqlite")]
-pub use bdk_chain::rusqlite;
-#[cfg(feature = "rusqlite")]
-pub use bdk_chain::rusqlite_impl;
-pub use descriptor::template;
-pub use descriptor::HdKeyPaths;
-pub use signer;
-pub use signer::SignOptions;
-pub use tx_builder::*;
-pub use types::*;
-pub use wallet::*;
-
-/// Get the version of [`bdk_wallet`](crate) at runtime.
-pub fn version() -> &'static str {
-    env!("CARGO_PKG_VERSION", "unknown")
-}
diff --git a/crates/wallet/src/psbt/mod.rs b/crates/wallet/src/psbt/mod.rs
deleted file mode 100644 (file)
index 9b3aea8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Additional functions on the `rust-bitcoin` `Psbt` structure.
-
-use alloc::vec::Vec;
-use bitcoin::Amount;
-use bitcoin::FeeRate;
-use bitcoin::Psbt;
-use bitcoin::TxOut;
-
-// TODO upstream the functions here to `rust-bitcoin`?
-
-/// Trait to add functions to extract utxos and calculate fees.
-pub trait PsbtUtils {
-    /// Get the `TxOut` for the specified input index, if it doesn't exist in the PSBT `None` is returned.
-    fn get_utxo_for(&self, input_index: usize) -> Option<TxOut>;
-
-    /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in sats.
-    /// If the PSBT is missing a TxOut for an input returns None.
-    fn fee_amount(&self) -> Option<Amount>;
-
-    /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
-    /// `Psbt` is finalized and all witness/signature data is added to the
-    /// transaction.
-    /// If the PSBT is missing a TxOut for an input returns None.
-    fn fee_rate(&self) -> Option<FeeRate>;
-}
-
-impl PsbtUtils for Psbt {
-    fn get_utxo_for(&self, input_index: usize) -> Option<TxOut> {
-        let tx = &self.unsigned_tx;
-        let input = self.inputs.get(input_index)?;
-
-        match (&input.witness_utxo, &input.non_witness_utxo) {
-            (Some(_), _) => input.witness_utxo.clone(),
-            (_, Some(_)) => input.non_witness_utxo.as_ref().map(|in_tx| {
-                in_tx.output[tx.input[input_index].previous_output.vout as usize].clone()
-            }),
-            _ => None,
-        }
-    }
-
-    fn fee_amount(&self) -> Option<Amount> {
-        let tx = &self.unsigned_tx;
-        let utxos: Option<Vec<TxOut>> = (0..tx.input.len()).map(|i| self.get_utxo_for(i)).collect();
-
-        utxos.map(|inputs| {
-            let input_amount: Amount = inputs.iter().map(|i| i.value).sum();
-            let output_amount: Amount = self.unsigned_tx.output.iter().map(|o| o.value).sum();
-            input_amount
-                .checked_sub(output_amount)
-                .expect("input amount must be greater than output amount")
-        })
-    }
-
-    fn fee_rate(&self) -> Option<FeeRate> {
-        let fee_amount = self.fee_amount();
-        let weight = self.clone().extract_tx().ok()?.weight();
-        fee_amount.map(|fee| fee / weight)
-    }
-}
diff --git a/crates/wallet/src/test_utils.rs b/crates/wallet/src/test_utils.rs
deleted file mode 100644 (file)
index 7e1778f..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-//! `bdk_wallet` test utilities
-
-use alloc::string::ToString;
-use alloc::sync::Arc;
-use core::str::FromStr;
-
-use bdk_chain::{tx_graph, BlockId, ConfirmationBlockTime};
-use bitcoin::{
-    absolute, hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint,
-    Transaction, TxIn, TxOut, Txid,
-};
-
-use crate::{KeychainKind, Update, Wallet};
-
-/// Return a fake wallet that appears to be funded for testing.
-///
-/// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-/// to a foreign address and one returning 50_000 back to the wallet. The remaining 1000
-/// sats are the transaction fee.
-pub fn get_funded_wallet(descriptor: &str, change_descriptor: &str) -> (Wallet, Txid) {
-    new_funded_wallet(descriptor, Some(change_descriptor))
-}
-
-fn new_funded_wallet(descriptor: &str, change_descriptor: Option<&str>) -> (Wallet, Txid) {
-    let params = if let Some(change_desc) = change_descriptor {
-        Wallet::create(descriptor.to_string(), change_desc.to_string())
-    } else {
-        Wallet::create_single(descriptor.to_string())
-    };
-
-    let mut wallet = params
-        .network(Network::Regtest)
-        .create_wallet_no_persist()
-        .expect("descriptors must be valid");
-
-    let receive_address = wallet.peek_address(KeychainKind::External, 0).address;
-    let sendto_address = Address::from_str("bcrt1q3qtze4ys45tgdvguj66zrk4fu6hq3a3v9pfly5")
-        .expect("address")
-        .require_network(Network::Regtest)
-        .unwrap();
-
-    let tx0 = Transaction {
-        output: vec![TxOut {
-            value: Amount::from_sat(76_000),
-            script_pubkey: receive_address.script_pubkey(),
-        }],
-        ..new_tx(0)
-    };
-
-    let tx1 = Transaction {
-        input: vec![TxIn {
-            previous_output: OutPoint {
-                txid: tx0.compute_txid(),
-                vout: 0,
-            },
-            ..Default::default()
-        }],
-        output: vec![
-            TxOut {
-                value: Amount::from_sat(50_000),
-                script_pubkey: receive_address.script_pubkey(),
-            },
-            TxOut {
-                value: Amount::from_sat(25_000),
-                script_pubkey: sendto_address.script_pubkey(),
-            },
-        ],
-        ..new_tx(0)
-    };
-
-    insert_checkpoint(
-        &mut wallet,
-        BlockId {
-            height: 42,
-            hash: BlockHash::all_zeros(),
-        },
-    );
-    insert_checkpoint(
-        &mut wallet,
-        BlockId {
-            height: 1_000,
-            hash: BlockHash::all_zeros(),
-        },
-    );
-    insert_checkpoint(
-        &mut wallet,
-        BlockId {
-            height: 2_000,
-            hash: BlockHash::all_zeros(),
-        },
-    );
-
-    insert_tx(&mut wallet, tx0.clone());
-    insert_anchor(
-        &mut wallet,
-        tx0.compute_txid(),
-        ConfirmationBlockTime {
-            block_id: BlockId {
-                height: 1_000,
-                hash: BlockHash::all_zeros(),
-            },
-            confirmation_time: 100,
-        },
-    );
-
-    insert_tx(&mut wallet, tx1.clone());
-    insert_anchor(
-        &mut wallet,
-        tx1.compute_txid(),
-        ConfirmationBlockTime {
-            block_id: BlockId {
-                height: 2_000,
-                hash: BlockHash::all_zeros(),
-            },
-            confirmation_time: 200,
-        },
-    );
-
-    (wallet, tx1.compute_txid())
-}
-
-/// Return a fake wallet that appears to be funded for testing.
-///
-/// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-/// to a foreign address and one returning 50_000 back to the wallet. The remaining 1000
-/// sats are the transaction fee.
-pub fn get_funded_wallet_single(descriptor: &str) -> (Wallet, Txid) {
-    new_funded_wallet(descriptor, None)
-}
-
-/// Get funded segwit wallet
-pub fn get_funded_wallet_wpkh() -> (Wallet, Txid) {
-    let (desc, change_desc) = get_test_wpkh_and_change_desc();
-    get_funded_wallet(desc, change_desc)
-}
-
-/// `wpkh` single key descriptor
-pub fn get_test_wpkh() -> &'static str {
-    "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
-}
-
-/// `wpkh` xpriv and change descriptor
-pub fn get_test_wpkh_and_change_desc() -> (&'static str, &'static str) {
-    ("wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)",
-    "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)")
-}
-
-/// `wsh` descriptor with policy `and(pk(A),older(6))`
-pub fn get_test_single_sig_csv() -> &'static str {
-    "wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6)))"
-}
-
-/// `wsh` descriptor with policy `or(pk(A),and(pk(B),older(144)))`
-pub fn get_test_a_or_b_plus_csv() -> &'static str {
-    "wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),and_v(v:pk(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(144))))"
-}
-
-/// `wsh` descriptor with policy `and(pk(A),after(100000))`
-pub fn get_test_single_sig_cltv() -> &'static str {
-    "wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000)))"
-}
-
-/// `wsh` descriptor with policy `and(pk(A),after(1_734_230_218))`
-// the parameter passed to miniscript fragment `after` has to equal or greater than 500_000_000
-// in order to use a lock based on unix time
-pub fn get_test_single_sig_cltv_timestamp() -> &'static str {
-    "wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(1734230218)))"
-}
-
-/// taproot single key descriptor
-pub fn get_test_tr_single_sig() -> &'static str {
-    "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG)"
-}
-
-/// taproot descriptor with taptree
-pub fn get_test_tr_with_taptree() -> &'static str {
-    "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
-}
-
-/// taproot descriptor with private key taptree
-pub fn get_test_tr_with_taptree_both_priv() -> &'static str {
-    "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{pk(cPZzKuNmpuUjD1e8jUU4PVzy2b5LngbSip8mBsxf4e7rSFZVb4Uh),pk(cNaQCDwmmh4dS9LzCgVtyy1e1xjCJ21GUDHe9K98nzb689JvinGV)})"
-}
-
-/// taproot descriptor where one key appears in two script paths
-pub fn get_test_tr_repeated_key() -> &'static str {
-    "tr(b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55,{and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100)),and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(200))})"
-}
-
-/// taproot xpriv descriptor
-pub fn get_test_tr_single_sig_xprv() -> &'static str {
-    "tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)"
-}
-
-/// taproot xpriv and change descriptor
-pub fn get_test_tr_single_sig_xprv_and_change_desc() -> (&'static str, &'static str) {
-    ("tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/0/*)",
-    "tr(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/1/*)")
-}
-
-/// taproot descriptor with taptree
-pub fn get_test_tr_with_taptree_xprv() -> &'static str {
-    "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG,{pk(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
-}
-
-/// taproot descriptor with duplicate script paths
-pub fn get_test_tr_dup_keys() -> &'static str {
-    "tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG,{pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642),pk(8aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642)})"
-}
-
-/// A new empty transaction with the given locktime
-pub fn new_tx(locktime: u32) -> Transaction {
-    Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::from_consensus(locktime),
-        input: vec![],
-        output: vec![],
-    }
-}
-
-/// Construct a new [`FeeRate`] from the given raw `sat_vb` feerate. This is
-/// useful in cases where we want to create a feerate from a `f64`, as the
-/// traditional [`FeeRate::from_sat_per_vb`] method will only accept an integer.
-///
-/// **Note** this 'quick and dirty' conversion should only be used when the input
-/// parameter has units of `satoshis/vbyte` **AND** is not expected to overflow,
-/// or else the resulting value will be inaccurate.
-pub fn feerate_unchecked(sat_vb: f64) -> FeeRate {
-    // 1 sat_vb / 4wu_vb * 1000kwu_wu = 250 sat_kwu
-    let sat_kwu = (sat_vb * 250.0).ceil() as u64;
-    FeeRate::from_sat_per_kwu(sat_kwu)
-}
-
-/// Input parameter for [`receive_output`].
-pub enum ReceiveTo {
-    /// Receive tx to mempool at this `last_seen` timestamp.
-    Mempool(u64),
-    /// Receive tx to block with this anchor.
-    Block(ConfirmationBlockTime),
-}
-
-impl From<ConfirmationBlockTime> for ReceiveTo {
-    fn from(value: ConfirmationBlockTime) -> Self {
-        Self::Block(value)
-    }
-}
-
-/// Receive a tx output with the given value in the latest block
-pub fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
-    let latest_cp = wallet.latest_checkpoint();
-    let height = latest_cp.height();
-    assert!(height > 0, "cannot receive tx into genesis block");
-    receive_output(
-        wallet,
-        value,
-        ConfirmationBlockTime {
-            block_id: latest_cp.block_id(),
-            confirmation_time: 0,
-        },
-    )
-}
-
-/// Receive a tx output with the given value and chain position
-pub fn receive_output(
-    wallet: &mut Wallet,
-    value: u64,
-    receive_to: impl Into<ReceiveTo>,
-) -> OutPoint {
-    let addr = wallet.next_unused_address(KeychainKind::External).address;
-    receive_output_to_address(wallet, addr, value, receive_to)
-}
-
-/// Receive a tx output to an address with the given value and chain position
-pub fn receive_output_to_address(
-    wallet: &mut Wallet,
-    addr: Address,
-    value: u64,
-    receive_to: impl Into<ReceiveTo>,
-) -> OutPoint {
-    let tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: addr.script_pubkey(),
-            value: Amount::from_sat(value),
-        }],
-    };
-
-    let txid = tx.compute_txid();
-    insert_tx(wallet, tx);
-
-    match receive_to.into() {
-        ReceiveTo::Block(anchor) => insert_anchor(wallet, txid, anchor),
-        ReceiveTo::Mempool(last_seen) => insert_seen_at(wallet, txid, last_seen),
-    }
-
-    OutPoint { txid, vout: 0 }
-}
-
-/// Insert a checkpoint into the wallet. This can be used to extend the wallet's local chain
-/// or to insert a block that did not exist previously. Note that if replacing a block with
-/// a different one at the same height, then all later blocks are evicted as well.
-pub fn insert_checkpoint(wallet: &mut Wallet, block: BlockId) {
-    let mut cp = wallet.latest_checkpoint();
-    cp = cp.insert(block);
-    wallet
-        .apply_update(Update {
-            chain: Some(cp),
-            ..Default::default()
-        })
-        .unwrap();
-}
-
-/// Insert transaction
-pub fn insert_tx(wallet: &mut Wallet, tx: Transaction) {
-    wallet
-        .apply_update(Update {
-            tx_update: bdk_chain::TxUpdate {
-                txs: vec![Arc::new(tx)],
-                ..Default::default()
-            },
-            ..Default::default()
-        })
-        .unwrap();
-}
-
-/// Simulates confirming a tx with `txid` by applying an update to the wallet containing
-/// the given `anchor`. Note: to be considered confirmed the anchor block must exist in
-/// the current active chain.
-pub fn insert_anchor(wallet: &mut Wallet, txid: Txid, anchor: ConfirmationBlockTime) {
-    wallet
-        .apply_update(Update {
-            tx_update: tx_graph::TxUpdate {
-                anchors: [(anchor, txid)].into(),
-                ..Default::default()
-            },
-            ..Default::default()
-        })
-        .unwrap();
-}
-
-/// Marks the given `txid` seen as unconfirmed at `seen_at`
-pub fn insert_seen_at(wallet: &mut Wallet, txid: Txid, seen_at: u64) {
-    wallet
-        .apply_update(crate::Update {
-            tx_update: tx_graph::TxUpdate {
-                seen_ats: [(txid, seen_at)].into_iter().collect(),
-                ..Default::default()
-            },
-            ..Default::default()
-        })
-        .unwrap();
-}
diff --git a/crates/wallet/src/types.rs b/crates/wallet/src/types.rs
deleted file mode 100644 (file)
index 54ddfa2..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-use alloc::boxed::Box;
-use chain::{ChainPosition, ConfirmationBlockTime};
-use core::convert::AsRef;
-
-use bitcoin::transaction::{OutPoint, Sequence, TxOut};
-use bitcoin::{psbt, Weight};
-
-use serde::{Deserialize, Serialize};
-
-/// Types of keychains
-#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub enum KeychainKind {
-    /// External keychain, used for deriving recipient addresses.
-    External = 0,
-    /// Internal keychain, used for deriving change addresses.
-    Internal = 1,
-}
-
-impl KeychainKind {
-    /// Return [`KeychainKind`] as a byte
-    pub fn as_byte(&self) -> u8 {
-        match self {
-            KeychainKind::External => b'e',
-            KeychainKind::Internal => b'i',
-        }
-    }
-}
-
-impl AsRef<[u8]> for KeychainKind {
-    fn as_ref(&self) -> &[u8] {
-        match self {
-            KeychainKind::External => b"e",
-            KeychainKind::Internal => b"i",
-        }
-    }
-}
-
-/// An unspent output owned by a [`Wallet`].
-///
-/// [`Wallet`]: crate::Wallet
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
-pub struct LocalOutput {
-    /// Reference to a transaction output
-    pub outpoint: OutPoint,
-    /// Transaction output
-    pub txout: TxOut,
-    /// Type of keychain
-    pub keychain: KeychainKind,
-    /// Whether this UTXO is spent or not
-    pub is_spent: bool,
-    /// The derivation index for the script pubkey in the wallet
-    pub derivation_index: u32,
-    /// The position of the output in the blockchain.
-    pub chain_position: ChainPosition<ConfirmationBlockTime>,
-}
-
-/// A [`Utxo`] with its `satisfaction_weight`.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct WeightedUtxo {
-    /// The weight of the witness data and `scriptSig` expressed in [weight units]. This is used to
-    /// properly maintain the feerate when adding this input to a transaction during coin selection.
-    ///
-    /// [weight units]: https://en.bitcoin.it/wiki/Weight_units
-    pub satisfaction_weight: Weight,
-    /// The UTXO
-    pub utxo: Utxo,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-/// An unspent transaction output (UTXO).
-pub enum Utxo {
-    /// A UTXO owned by the local wallet.
-    Local(LocalOutput),
-    /// A UTXO owned by another wallet.
-    Foreign {
-        /// The location of the output.
-        outpoint: OutPoint,
-        /// The nSequence value to set for this input.
-        sequence: Sequence,
-        /// The information about the input we require to add it to a PSBT.
-        // Box it to stop the type being too big.
-        psbt_input: Box<psbt::Input>,
-    },
-}
-
-impl Utxo {
-    /// Get the location of the UTXO
-    pub fn outpoint(&self) -> OutPoint {
-        match &self {
-            Utxo::Local(local) => local.outpoint,
-            Utxo::Foreign { outpoint, .. } => *outpoint,
-        }
-    }
-
-    /// Get the `TxOut` of the UTXO
-    pub fn txout(&self) -> &TxOut {
-        match &self {
-            Utxo::Local(local) => &local.txout,
-            Utxo::Foreign {
-                outpoint,
-                psbt_input,
-                ..
-            } => {
-                if let Some(prev_tx) = &psbt_input.non_witness_utxo {
-                    return &prev_tx.output[outpoint.vout as usize];
-                }
-
-                if let Some(txout) = &psbt_input.witness_utxo {
-                    return txout;
-                }
-
-                unreachable!("Foreign UTXOs will always have one of these set")
-            }
-        }
-    }
-
-    /// Get the sequence number if an explicit sequence number has to be set for this input.
-    pub fn sequence(&self) -> Option<Sequence> {
-        match self {
-            Utxo::Local(_) => None,
-            Utxo::Foreign { sequence, .. } => Some(*sequence),
-        }
-    }
-}
diff --git a/crates/wallet/src/wallet/changeset.rs b/crates/wallet/src/wallet/changeset.rs
deleted file mode 100644 (file)
index 94dba2b..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-use bdk_chain::{
-    indexed_tx_graph, keychain_txout, local_chain, tx_graph, ConfirmationBlockTime, Merge,
-};
-use miniscript::{Descriptor, DescriptorPublicKey};
-
-type IndexedTxGraphChangeSet =
-    indexed_tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>;
-
-/// A changeset for [`Wallet`](crate::Wallet).
-#[derive(Default, Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
-pub struct ChangeSet {
-    /// Descriptor for recipient addresses.
-    pub descriptor: Option<Descriptor<DescriptorPublicKey>>,
-    /// Descriptor for change addresses.
-    pub change_descriptor: Option<Descriptor<DescriptorPublicKey>>,
-    /// Stores the network type of the transaction data.
-    pub network: Option<bitcoin::Network>,
-    /// Changes to the [`LocalChain`](local_chain::LocalChain).
-    pub local_chain: local_chain::ChangeSet,
-    /// Changes to [`TxGraph`](tx_graph::TxGraph).
-    pub tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime>,
-    /// Changes to [`KeychainTxOutIndex`](keychain_txout::KeychainTxOutIndex).
-    pub indexer: keychain_txout::ChangeSet,
-}
-
-impl Merge for ChangeSet {
-    /// Merge another [`ChangeSet`] into itself.
-    fn merge(&mut self, other: Self) {
-        if other.descriptor.is_some() {
-            debug_assert!(
-                self.descriptor.is_none() || self.descriptor == other.descriptor,
-                "descriptor must never change"
-            );
-            self.descriptor = other.descriptor;
-        }
-        if other.change_descriptor.is_some() {
-            debug_assert!(
-                self.change_descriptor.is_none()
-                    || self.change_descriptor == other.change_descriptor,
-                "change descriptor must never change"
-            );
-            self.change_descriptor = other.change_descriptor;
-        }
-        if other.network.is_some() {
-            debug_assert!(
-                self.network.is_none() || self.network == other.network,
-                "network must never change"
-            );
-            self.network = other.network;
-        }
-
-        Merge::merge(&mut self.local_chain, other.local_chain);
-        Merge::merge(&mut self.tx_graph, other.tx_graph);
-        Merge::merge(&mut self.indexer, other.indexer);
-    }
-
-    fn is_empty(&self) -> bool {
-        self.descriptor.is_none()
-            && self.change_descriptor.is_none()
-            && self.network.is_none()
-            && self.local_chain.is_empty()
-            && self.tx_graph.is_empty()
-            && self.indexer.is_empty()
-    }
-}
-
-#[cfg(feature = "rusqlite")]
-impl ChangeSet {
-    /// Schema name for wallet.
-    pub const WALLET_SCHEMA_NAME: &'static str = "bdk_wallet";
-    /// Name of table to store wallet descriptors and network.
-    pub const WALLET_TABLE_NAME: &'static str = "bdk_wallet";
-
-    /// Get v0 sqlite [ChangeSet] schema
-    pub fn schema_v0() -> alloc::string::String {
-        format!(
-            "CREATE TABLE {} ( \
-                id INTEGER PRIMARY KEY NOT NULL CHECK (id = 0), \
-                descriptor TEXT, \
-                change_descriptor TEXT, \
-                network TEXT \
-                ) STRICT;",
-            Self::WALLET_TABLE_NAME,
-        )
-    }
-
-    /// Initialize sqlite tables for wallet tables.
-    pub fn init_sqlite_tables(db_tx: &chain::rusqlite::Transaction) -> chain::rusqlite::Result<()> {
-        crate::rusqlite_impl::migrate_schema(
-            db_tx,
-            Self::WALLET_SCHEMA_NAME,
-            &[&Self::schema_v0()],
-        )?;
-
-        bdk_chain::local_chain::ChangeSet::init_sqlite_tables(db_tx)?;
-        bdk_chain::tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(db_tx)?;
-        bdk_chain::keychain_txout::ChangeSet::init_sqlite_tables(db_tx)?;
-
-        Ok(())
-    }
-
-    /// Recover a [`ChangeSet`] from sqlite database.
-    pub fn from_sqlite(db_tx: &chain::rusqlite::Transaction) -> chain::rusqlite::Result<Self> {
-        use chain::rusqlite::OptionalExtension;
-        use chain::Impl;
-
-        let mut changeset = Self::default();
-
-        let mut wallet_statement = db_tx.prepare(&format!(
-            "SELECT descriptor, change_descriptor, network FROM {}",
-            Self::WALLET_TABLE_NAME,
-        ))?;
-        let row = wallet_statement
-            .query_row([], |row| {
-                Ok((
-                    row.get::<_, Option<Impl<Descriptor<DescriptorPublicKey>>>>("descriptor")?,
-                    row.get::<_, Option<Impl<Descriptor<DescriptorPublicKey>>>>(
-                        "change_descriptor",
-                    )?,
-                    row.get::<_, Option<Impl<bitcoin::Network>>>("network")?,
-                ))
-            })
-            .optional()?;
-        if let Some((desc, change_desc, network)) = row {
-            changeset.descriptor = desc.map(Impl::into_inner);
-            changeset.change_descriptor = change_desc.map(Impl::into_inner);
-            changeset.network = network.map(Impl::into_inner);
-        }
-
-        changeset.local_chain = local_chain::ChangeSet::from_sqlite(db_tx)?;
-        changeset.tx_graph = tx_graph::ChangeSet::<_>::from_sqlite(db_tx)?;
-        changeset.indexer = keychain_txout::ChangeSet::from_sqlite(db_tx)?;
-
-        Ok(changeset)
-    }
-
-    /// Persist [`ChangeSet`] to sqlite database.
-    pub fn persist_to_sqlite(
-        &self,
-        db_tx: &chain::rusqlite::Transaction,
-    ) -> chain::rusqlite::Result<()> {
-        use chain::rusqlite::named_params;
-        use chain::Impl;
-
-        let mut descriptor_statement = db_tx.prepare_cached(&format!(
-            "INSERT INTO {}(id, descriptor) VALUES(:id, :descriptor) ON CONFLICT(id) DO UPDATE SET descriptor=:descriptor",
-            Self::WALLET_TABLE_NAME,
-        ))?;
-        if let Some(descriptor) = &self.descriptor {
-            descriptor_statement.execute(named_params! {
-                ":id": 0,
-                ":descriptor": Impl(descriptor.clone()),
-            })?;
-        }
-
-        let mut change_descriptor_statement = db_tx.prepare_cached(&format!(
-            "INSERT INTO {}(id, change_descriptor) VALUES(:id, :change_descriptor) ON CONFLICT(id) DO UPDATE SET change_descriptor=:change_descriptor",
-            Self::WALLET_TABLE_NAME,
-        ))?;
-        if let Some(change_descriptor) = &self.change_descriptor {
-            change_descriptor_statement.execute(named_params! {
-                ":id": 0,
-                ":change_descriptor": Impl(change_descriptor.clone()),
-            })?;
-        }
-
-        let mut network_statement = db_tx.prepare_cached(&format!(
-            "INSERT INTO {}(id, network) VALUES(:id, :network) ON CONFLICT(id) DO UPDATE SET network=:network",
-            Self::WALLET_TABLE_NAME,
-        ))?;
-        if let Some(network) = self.network {
-            network_statement.execute(named_params! {
-                ":id": 0,
-                ":network": Impl(network),
-            })?;
-        }
-
-        self.local_chain.persist_to_sqlite(db_tx)?;
-        self.tx_graph.persist_to_sqlite(db_tx)?;
-        self.indexer.persist_to_sqlite(db_tx)?;
-        Ok(())
-    }
-}
-
-impl From<local_chain::ChangeSet> for ChangeSet {
-    fn from(chain: local_chain::ChangeSet) -> Self {
-        Self {
-            local_chain: chain,
-            ..Default::default()
-        }
-    }
-}
-
-impl From<IndexedTxGraphChangeSet> for ChangeSet {
-    fn from(indexed_tx_graph: IndexedTxGraphChangeSet) -> Self {
-        Self {
-            tx_graph: indexed_tx_graph.tx_graph,
-            indexer: indexed_tx_graph.indexer,
-            ..Default::default()
-        }
-    }
-}
-
-impl From<tx_graph::ChangeSet<ConfirmationBlockTime>> for ChangeSet {
-    fn from(tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime>) -> Self {
-        Self {
-            tx_graph,
-            ..Default::default()
-        }
-    }
-}
-
-impl From<keychain_txout::ChangeSet> for ChangeSet {
-    fn from(indexer: keychain_txout::ChangeSet) -> Self {
-        Self {
-            indexer,
-            ..Default::default()
-        }
-    }
-}
diff --git a/crates/wallet/src/wallet/coin_selection.rs b/crates/wallet/src/wallet/coin_selection.rs
deleted file mode 100644 (file)
index e5654fb..0000000
+++ /dev/null
@@ -1,1701 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Coin selection
-//!
-//! This module provides the trait [`CoinSelectionAlgorithm`] that can be implemented to
-//! define custom coin selection algorithms.
-//!
-//! You can specify a custom coin selection algorithm through the [`coin_selection`] method on
-//! [`TxBuilder`]. [`DefaultCoinSelectionAlgorithm`] aliases the coin selection algorithm that will
-//! be used if it is not explicitly set.
-//!
-//! [`TxBuilder`]: super::tx_builder::TxBuilder
-//! [`coin_selection`]: super::tx_builder::TxBuilder::coin_selection
-//!
-//! ## Example
-//!
-//! ```
-//! # use std::str::FromStr;
-//! # use bitcoin::*;
-//! # use bdk_wallet::{self, ChangeSet, coin_selection::*, coin_selection};
-//! # use bdk_wallet::error::CreateTxError;
-//! # use bdk_wallet::*;
-//! # use bdk_wallet::coin_selection::decide_change;
-//! # use anyhow::Error;
-//! # use rand_core::RngCore;
-//! #[derive(Debug)]
-//! struct AlwaysSpendEverything;
-//!
-//! impl CoinSelectionAlgorithm for AlwaysSpendEverything {
-//!     fn coin_select<R: RngCore>(
-//!         &self,
-//!         required_utxos: Vec<WeightedUtxo>,
-//!         optional_utxos: Vec<WeightedUtxo>,
-//!         fee_rate: FeeRate,
-//!         target_amount: Amount,
-//!         drain_script: &Script,
-//!         rand: &mut R,
-//!     ) -> Result<CoinSelectionResult, coin_selection::InsufficientFunds> {
-//!         let mut selected_amount = Amount::ZERO;
-//!         let mut additional_weight = Weight::ZERO;
-//!         let all_utxos_selected = required_utxos
-//!             .into_iter()
-//!             .chain(optional_utxos)
-//!             .scan(
-//!                 (&mut selected_amount, &mut additional_weight),
-//!                 |(selected_amount, additional_weight), weighted_utxo| {
-//!                     **selected_amount += weighted_utxo.utxo.txout().value;
-//!                     **additional_weight += TxIn::default()
-//!                         .segwit_weight()
-//!                         .checked_add(weighted_utxo.satisfaction_weight)
-//!                         .expect("`Weight` addition should not cause an integer overflow");
-//!                     Some(weighted_utxo.utxo)
-//!                 },
-//!             )
-//!             .collect::<Vec<_>>();
-//!         let additional_fees = fee_rate * additional_weight;
-//!         let amount_needed_with_fees = additional_fees + target_amount;
-//!         if selected_amount < amount_needed_with_fees {
-//!             return Err(coin_selection::InsufficientFunds {
-//!                 needed: amount_needed_with_fees,
-//!                 available: selected_amount,
-//!             });
-//!         }
-//!
-//!         let remaining_amount = selected_amount - amount_needed_with_fees;
-//!
-//!         let excess = decide_change(remaining_amount, fee_rate, drain_script);
-//!
-//!         Ok(CoinSelectionResult {
-//!             selected: all_utxos_selected,
-//!             fee_amount: additional_fees,
-//!             excess,
-//!         })
-//!     }
-//! }
-//!
-//! # let mut wallet = doctest_wallet!();
-//! // create wallet, sync, ...
-//!
-//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
-//!     .unwrap()
-//!     .require_network(Network::Testnet)
-//!     .unwrap();
-//! let psbt = {
-//!     let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything);
-//!     builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
-//!     builder.finish()?
-//! };
-//!
-//! // inspect, sign, broadcast, ...
-//!
-//! # Ok::<(), anyhow::Error>(())
-//! ```
-
-use crate::wallet::utils::IsDust;
-use crate::Utxo;
-use crate::WeightedUtxo;
-use bitcoin::{Amount, FeeRate, SignedAmount};
-
-use alloc::vec::Vec;
-use bitcoin::consensus::encode::serialize;
-use bitcoin::TxIn;
-use bitcoin::{Script, Weight};
-
-use core::convert::TryInto;
-use core::fmt::{self, Formatter};
-use rand_core::RngCore;
-
-use super::utils::shuffle_slice;
-/// Default coin selection algorithm used by [`TxBuilder`](super::tx_builder::TxBuilder) if not
-/// overridden
-pub type DefaultCoinSelectionAlgorithm = BranchAndBoundCoinSelection<SingleRandomDraw>;
-
-/// Wallet's UTXO set is not enough to cover recipient's requested plus fee.
-///
-/// This is thrown by [`CoinSelectionAlgorithm`].
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct InsufficientFunds {
-    /// Amount needed for the transaction
-    pub needed: Amount,
-    /// Amount available for spending
-    pub available: Amount,
-}
-
-impl fmt::Display for InsufficientFunds {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "Insufficient funds: {} available of {} needed",
-            self.available, self.needed
-        )
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for InsufficientFunds {}
-
-#[derive(Debug)]
-/// Remaining amount after performing coin selection
-pub enum Excess {
-    /// It's not possible to create spendable output from excess using the current drain output
-    NoChange {
-        /// Threshold to consider amount as dust for this particular change script_pubkey
-        dust_threshold: Amount,
-        /// Exceeding amount of current selection over outgoing value and fee costs
-        remaining_amount: Amount,
-        /// The calculated fee for the drain TxOut with the selected script_pubkey
-        change_fee: Amount,
-    },
-    /// It's possible to create spendable output from excess using the current drain output
-    Change {
-        /// Effective amount available to create change after deducting the change output fee
-        amount: Amount,
-        /// The deducted change output fee
-        fee: Amount,
-    },
-}
-
-/// Result of a successful coin selection
-#[derive(Debug)]
-pub struct CoinSelectionResult {
-    /// List of outputs selected for use as inputs
-    pub selected: Vec<Utxo>,
-    /// Total fee amount for the selected utxos
-    pub fee_amount: Amount,
-    /// Remaining amount after deducing fees and outgoing outputs
-    pub excess: Excess,
-}
-
-impl CoinSelectionResult {
-    /// The total value of the inputs selected.
-    pub fn selected_amount(&self) -> Amount {
-        self.selected.iter().map(|u| u.txout().value).sum()
-    }
-
-    /// The total value of the inputs selected from the local wallet.
-    pub fn local_selected_amount(&self) -> Amount {
-        self.selected
-            .iter()
-            .filter_map(|u| match u {
-                Utxo::Local(_) => Some(u.txout().value),
-                _ => None,
-            })
-            .sum()
-    }
-}
-
-/// Trait for generalized coin selection algorithms
-///
-/// This trait can be implemented to make the [`Wallet`](super::Wallet) use a customized coin
-/// selection algorithm when it creates transactions.
-///
-/// For an example see [this module](crate::wallet::coin_selection)'s documentation.
-pub trait CoinSelectionAlgorithm: core::fmt::Debug {
-    /// Perform the coin selection
-    ///
-    /// - `required_utxos`: the utxos that must be spent regardless of `target_amount` with their
-    ///                     weight cost
-    /// - `optional_utxos`: the remaining available utxos to satisfy `target_amount` with their
-    ///                     weight cost
-    /// - `fee_rate`: fee rate to use
-    /// - `target_amount`: the outgoing amount and the fees already accumulated from adding
-    ///                    outputs and transaction’s header.
-    /// - `drain_script`: the script to use in case of change
-    /// - `rand`: random number generated used by some coin selection algorithms such as [`SingleRandomDraw`]
-    fn coin_select<R: RngCore>(
-        &self,
-        required_utxos: Vec<WeightedUtxo>,
-        optional_utxos: Vec<WeightedUtxo>,
-        fee_rate: FeeRate,
-        target_amount: Amount,
-        drain_script: &Script,
-        rand: &mut R,
-    ) -> Result<CoinSelectionResult, InsufficientFunds>;
-}
-
-/// Simple and dumb coin selection
-///
-/// This coin selection algorithm sorts the available UTXOs by value and then picks them starting
-/// from the largest ones until the required amount is reached.
-#[derive(Debug, Default, Clone, Copy)]
-pub struct LargestFirstCoinSelection;
-
-impl CoinSelectionAlgorithm for LargestFirstCoinSelection {
-    fn coin_select<R: RngCore>(
-        &self,
-        required_utxos: Vec<WeightedUtxo>,
-        mut optional_utxos: Vec<WeightedUtxo>,
-        fee_rate: FeeRate,
-        target_amount: Amount,
-        drain_script: &Script,
-        _: &mut R,
-    ) -> Result<CoinSelectionResult, InsufficientFunds> {
-        // We put the "required UTXOs" first and make sure the optional UTXOs are sorted,
-        // initially smallest to largest, before being reversed with `.rev()`.
-        let utxos = {
-            optional_utxos.sort_unstable_by_key(|wu| wu.utxo.txout().value);
-            required_utxos
-                .into_iter()
-                .map(|utxo| (true, utxo))
-                .chain(optional_utxos.into_iter().rev().map(|utxo| (false, utxo)))
-        };
-
-        select_sorted_utxos(utxos, fee_rate, target_amount, drain_script)
-    }
-}
-
-/// OldestFirstCoinSelection always picks the utxo with the smallest blockheight to add to the selected coins next
-///
-/// This coin selection algorithm sorts the available UTXOs by blockheight and then picks them starting
-/// from the oldest ones until the required amount is reached.
-#[derive(Debug, Default, Clone, Copy)]
-pub struct OldestFirstCoinSelection;
-
-impl CoinSelectionAlgorithm for OldestFirstCoinSelection {
-    fn coin_select<R: RngCore>(
-        &self,
-        required_utxos: Vec<WeightedUtxo>,
-        mut optional_utxos: Vec<WeightedUtxo>,
-        fee_rate: FeeRate,
-        target_amount: Amount,
-        drain_script: &Script,
-        _: &mut R,
-    ) -> Result<CoinSelectionResult, InsufficientFunds> {
-        // We put the "required UTXOs" first and make sure the optional UTXOs are sorted from
-        // oldest to newest according to blocktime
-        // For utxo that doesn't exist in DB, they will have lowest priority to be selected
-        let utxos = {
-            optional_utxos.sort_unstable_by_key(|wu| match &wu.utxo {
-                Utxo::Local(local) => Some(local.chain_position),
-                Utxo::Foreign { .. } => None,
-            });
-
-            required_utxos
-                .into_iter()
-                .map(|utxo| (true, utxo))
-                .chain(optional_utxos.into_iter().map(|utxo| (false, utxo)))
-        };
-
-        select_sorted_utxos(utxos, fee_rate, target_amount, drain_script)
-    }
-}
-
-/// Decide if change can be created
-///
-/// - `remaining_amount`: the amount in which the selected coins exceed the target amount
-/// - `fee_rate`: required fee rate for the current selection
-/// - `drain_script`: script to consider change creation
-pub fn decide_change(remaining_amount: Amount, fee_rate: FeeRate, drain_script: &Script) -> Excess {
-    // drain_output_len = size(len(script_pubkey)) + len(script_pubkey) + size(output_value)
-    let drain_output_len = serialize(drain_script).len() + 8usize;
-    let change_fee =
-        fee_rate * Weight::from_vb(drain_output_len as u64).expect("overflow occurred");
-    let drain_val = remaining_amount.checked_sub(change_fee).unwrap_or_default();
-
-    if drain_val.is_dust(drain_script) {
-        let dust_threshold = drain_script.minimal_non_dust();
-        Excess::NoChange {
-            dust_threshold,
-            change_fee,
-            remaining_amount,
-        }
-    } else {
-        Excess::Change {
-            amount: drain_val,
-            fee: change_fee,
-        }
-    }
-}
-
-fn select_sorted_utxos(
-    utxos: impl Iterator<Item = (bool, WeightedUtxo)>,
-    fee_rate: FeeRate,
-    target_amount: Amount,
-    drain_script: &Script,
-) -> Result<CoinSelectionResult, InsufficientFunds> {
-    let mut selected_amount = Amount::ZERO;
-    let mut fee_amount = Amount::ZERO;
-    let selected = utxos
-        .scan(
-            (&mut selected_amount, &mut fee_amount),
-            |(selected_amount, fee_amount), (must_use, weighted_utxo)| {
-                if must_use || **selected_amount < target_amount + **fee_amount {
-                    **fee_amount += fee_rate
-                        * TxIn::default()
-                            .segwit_weight()
-                            .checked_add(weighted_utxo.satisfaction_weight)
-                            .expect("`Weight` addition should not cause an integer overflow");
-                    **selected_amount += weighted_utxo.utxo.txout().value;
-                    Some(weighted_utxo.utxo)
-                } else {
-                    None
-                }
-            },
-        )
-        .collect::<Vec<_>>();
-
-    let amount_needed_with_fees = target_amount + fee_amount;
-    if selected_amount < amount_needed_with_fees {
-        return Err(InsufficientFunds {
-            needed: amount_needed_with_fees,
-            available: selected_amount,
-        });
-    }
-
-    let remaining_amount = selected_amount - amount_needed_with_fees;
-
-    let excess = decide_change(remaining_amount, fee_rate, drain_script);
-
-    Ok(CoinSelectionResult {
-        selected,
-        fee_amount,
-        excess,
-    })
-}
-
-#[derive(Debug, Clone)]
-// Adds fee information to an UTXO.
-struct OutputGroup {
-    weighted_utxo: WeightedUtxo,
-    // Amount of fees for spending a certain utxo, calculated using a certain FeeRate
-    fee: Amount,
-    // The effective value of the UTXO, i.e., the utxo value minus the fee for spending it
-    effective_value: SignedAmount,
-}
-
-impl OutputGroup {
-    fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
-        let fee = fee_rate
-            * TxIn::default()
-                .segwit_weight()
-                .checked_add(weighted_utxo.satisfaction_weight)
-                .expect("`Weight` addition should not cause an integer overflow");
-        let effective_value = weighted_utxo
-            .utxo
-            .txout()
-            .value
-            .to_signed()
-            .expect("signed amount")
-            - fee.to_signed().expect("signed amount");
-        OutputGroup {
-            weighted_utxo,
-            fee,
-            effective_value,
-        }
-    }
-}
-
-/// Branch and bound coin selection
-///
-/// Code adapted from Bitcoin Core's implementation and from Mark Erhardt Master's Thesis: <http://murch.one/wp-content/uploads/2016/11/erhardt2016coinselection.pdf>
-#[derive(Debug, Clone)]
-pub struct BranchAndBoundCoinSelection<Cs = SingleRandomDraw> {
-    size_of_change: u64,
-    fallback_algorithm: Cs,
-}
-
-/// Error returned by branch and bound coin selection.
-#[derive(Debug)]
-enum BnbError {
-    /// Branch and bound coin selection tries to avoid needing a change by finding the right inputs for
-    /// the desired outputs plus fee, if there is not such combination this error is thrown
-    NoExactMatch,
-    /// Branch and bound coin selection possible attempts with sufficiently big UTXO set could grow
-    /// exponentially, thus a limit is set, and when hit, this error is thrown
-    TotalTriesExceeded,
-}
-
-impl<Cs: Default> Default for BranchAndBoundCoinSelection<Cs> {
-    fn default() -> Self {
-        Self {
-            // P2WPKH cost of change -> value (8 bytes) + script len (1 bytes) + script (22 bytes)
-            size_of_change: 8 + 1 + 22,
-            fallback_algorithm: Cs::default(),
-        }
-    }
-}
-
-impl<Cs> BranchAndBoundCoinSelection<Cs> {
-    /// Create new instance with a target `size_of_change` and `fallback_algorithm`.
-    pub fn new(size_of_change: u64, fallback_algorithm: Cs) -> Self {
-        Self {
-            size_of_change,
-            fallback_algorithm,
-        }
-    }
-}
-
-const BNB_TOTAL_TRIES: usize = 100_000;
-
-impl<Cs: CoinSelectionAlgorithm> CoinSelectionAlgorithm for BranchAndBoundCoinSelection<Cs> {
-    fn coin_select<R: RngCore>(
-        &self,
-        required_utxos: Vec<WeightedUtxo>,
-        optional_utxos: Vec<WeightedUtxo>,
-        fee_rate: FeeRate,
-        target_amount: Amount,
-        drain_script: &Script,
-        rand: &mut R,
-    ) -> Result<CoinSelectionResult, InsufficientFunds> {
-        // Mapping every (UTXO, usize) to an output group
-        let required_ogs: Vec<OutputGroup> = required_utxos
-            .iter()
-            .map(|u| OutputGroup::new(u.clone(), fee_rate))
-            .collect();
-
-        // Mapping every (UTXO, usize) to an output group, filtering UTXOs with a negative
-        // effective value
-        let optional_ogs: Vec<OutputGroup> = optional_utxos
-            .iter()
-            .map(|u| OutputGroup::new(u.clone(), fee_rate))
-            .filter(|u| u.effective_value.is_positive())
-            .collect();
-
-        let curr_value = required_ogs
-            .iter()
-            .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-        let curr_available_value = optional_ogs
-            .iter()
-            .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-        let cost_of_change = (Weight::from_vb(self.size_of_change).expect("overflow occurred")
-            * fee_rate)
-            .to_signed()
-            .expect("signed amount");
-
-        // `curr_value` and `curr_available_value` are both the sum of *effective_values* of
-        // the UTXOs. For the optional UTXOs (curr_available_value) we filter out UTXOs with
-        // negative effective value, so it will always be positive.
-        //
-        // Since we are required to spend the required UTXOs (curr_value) we have to consider
-        // all their effective values, even when negative, which means that curr_value could
-        // be negative as well.
-        //
-        // If the sum of curr_value and curr_available_value is negative or lower than our target,
-        // we can immediately exit with an error, as it's guaranteed we will never find a solution
-        // if we actually run the BnB.
-        let total_value: Result<Amount, _> = (curr_available_value + curr_value).try_into();
-        match total_value {
-            Ok(v) if v >= target_amount => {}
-            _ => {
-                // Assume we spend all the UTXOs we can (all the required + all the optional with
-                // positive effective value), sum their value and their fee cost.
-                let (utxo_fees, utxo_value) = required_ogs.iter().chain(optional_ogs.iter()).fold(
-                    (Amount::ZERO, Amount::ZERO),
-                    |(mut fees, mut value), utxo| {
-                        fees += utxo.fee;
-                        value += utxo.weighted_utxo.utxo.txout().value;
-                        (fees, value)
-                    },
-                );
-
-                // Add to the target the fee cost of the UTXOs
-                return Err(InsufficientFunds {
-                    needed: target_amount + utxo_fees,
-                    available: utxo_value,
-                });
-            }
-        }
-
-        let signed_target_amount = target_amount
-            .try_into()
-            .expect("Bitcoin amount to fit into i64");
-
-        if curr_value > signed_target_amount {
-            // remaining_amount can't be negative as that would mean the
-            // selection wasn't successful
-            // target_amount = amount_needed + (fee_amount - vin_fees)
-            let remaining_amount = (curr_value - signed_target_amount)
-                .to_unsigned()
-                .expect("remaining amount can't be negative");
-
-            let excess = decide_change(remaining_amount, fee_rate, drain_script);
-
-            return Ok(calculate_cs_result(vec![], required_ogs, excess));
-        }
-
-        match self.bnb(
-            required_ogs,
-            optional_ogs,
-            curr_value,
-            curr_available_value,
-            signed_target_amount,
-            cost_of_change,
-            drain_script,
-            fee_rate,
-        ) {
-            Ok(r) => Ok(r),
-            Err(_) => self.fallback_algorithm.coin_select(
-                required_utxos,
-                optional_utxos,
-                fee_rate,
-                target_amount,
-                drain_script,
-                rand,
-            ),
-        }
-    }
-}
-
-impl<Cs> BranchAndBoundCoinSelection<Cs> {
-    // TODO: make this more Rust-onic :)
-    // (And perhaps refactor with less arguments?)
-    #[allow(clippy::too_many_arguments)]
-    fn bnb(
-        &self,
-        required_utxos: Vec<OutputGroup>,
-        mut optional_utxos: Vec<OutputGroup>,
-        mut curr_value: SignedAmount,
-        mut curr_available_value: SignedAmount,
-        target_amount: SignedAmount,
-        cost_of_change: SignedAmount,
-        drain_script: &Script,
-        fee_rate: FeeRate,
-    ) -> Result<CoinSelectionResult, BnbError> {
-        // current_selection[i] will contain true if we are using optional_utxos[i],
-        // false otherwise. Note that current_selection.len() could be less than
-        // optional_utxos.len(), it just means that we still haven't decided if we should keep
-        // certain optional_utxos or not.
-        let mut current_selection: Vec<bool> = Vec::with_capacity(optional_utxos.len());
-
-        // Sort the utxo_pool
-        optional_utxos.sort_unstable_by_key(|a| a.effective_value);
-        optional_utxos.reverse();
-
-        // Contains the best selection we found
-        let mut best_selection = Vec::new();
-        let mut best_selection_value = None;
-
-        // Depth First search loop for choosing the UTXOs
-        for _ in 0..BNB_TOTAL_TRIES {
-            // Conditions for starting a backtrack
-            let mut backtrack = false;
-            // Cannot possibly reach target with the amount remaining in the curr_available_value,
-            // or the selected value is out of range.
-            // Go back and try other branch
-            if curr_value + curr_available_value < target_amount
-                || curr_value > target_amount + cost_of_change
-            {
-                backtrack = true;
-            } else if curr_value >= target_amount {
-                // Selected value is within range, there's no point in going forward. Start
-                // backtracking
-                backtrack = true;
-
-                // If we found a solution better than the previous one, or if there wasn't previous
-                // solution, update the best solution
-                if best_selection_value.is_none() || curr_value < best_selection_value.unwrap() {
-                    best_selection.clone_from(&current_selection);
-                    best_selection_value = Some(curr_value);
-                }
-
-                // If we found a perfect match, break here
-                if curr_value == target_amount {
-                    break;
-                }
-            }
-
-            // Backtracking, moving backwards
-            if backtrack {
-                // Walk backwards to find the last included UTXO that still needs to have its omission branch traversed.
-                while let Some(false) = current_selection.last() {
-                    current_selection.pop();
-                    curr_available_value += optional_utxos[current_selection.len()].effective_value;
-                }
-
-                if current_selection.last_mut().is_none() {
-                    // We have walked back to the first utxo and no branch is untraversed. All solutions searched
-                    // If best selection is empty, then there's no exact match
-                    if best_selection.is_empty() {
-                        return Err(BnbError::NoExactMatch);
-                    }
-                    break;
-                }
-
-                if let Some(c) = current_selection.last_mut() {
-                    // Output was included on previous iterations, try excluding now.
-                    *c = false;
-                }
-
-                let utxo = &optional_utxos[current_selection.len() - 1];
-                curr_value -= utxo.effective_value;
-            } else {
-                // Moving forwards, continuing down this branch
-                let utxo = &optional_utxos[current_selection.len()];
-
-                // Remove this utxo from the curr_available_value utxo amount
-                curr_available_value -= utxo.effective_value;
-
-                // Inclusion branch first (Largest First Exploration)
-                current_selection.push(true);
-                curr_value += utxo.effective_value;
-            }
-        }
-
-        // Check for solution
-        if best_selection.is_empty() {
-            return Err(BnbError::TotalTriesExceeded);
-        }
-
-        // Set output set
-        let selected_utxos = optional_utxos
-            .into_iter()
-            .zip(best_selection)
-            .filter_map(|(optional, is_in_best)| if is_in_best { Some(optional) } else { None })
-            .collect::<Vec<OutputGroup>>();
-
-        let selected_amount = best_selection_value.unwrap();
-
-        // remaining_amount can't be negative as that would mean the
-        // selection wasn't successful
-        // target_amount = amount_needed + (fee_amount - vin_fees)
-        let remaining_amount = (selected_amount - target_amount)
-            .to_unsigned()
-            .expect("valid unsigned");
-
-        let excess = decide_change(remaining_amount, fee_rate, drain_script);
-
-        Ok(calculate_cs_result(selected_utxos, required_utxos, excess))
-    }
-}
-
-/// Pull UTXOs at random until we have enough to meet the target.
-#[derive(Debug, Clone, Copy, Default)]
-pub struct SingleRandomDraw;
-
-impl CoinSelectionAlgorithm for SingleRandomDraw {
-    fn coin_select<R: RngCore>(
-        &self,
-        required_utxos: Vec<WeightedUtxo>,
-        mut optional_utxos: Vec<WeightedUtxo>,
-        fee_rate: FeeRate,
-        target_amount: Amount,
-        drain_script: &Script,
-        rand: &mut R,
-    ) -> Result<CoinSelectionResult, InsufficientFunds> {
-        // We put the required UTXOs first and then the randomize optional UTXOs to take as needed
-        let utxos = {
-            shuffle_slice(&mut optional_utxos, rand);
-
-            required_utxos
-                .into_iter()
-                .map(|utxo| (true, utxo))
-                .chain(optional_utxos.into_iter().map(|utxo| (false, utxo)))
-        };
-
-        // select required UTXOs and then random optional UTXOs.
-        select_sorted_utxos(utxos, fee_rate, target_amount, drain_script)
-    }
-}
-
-fn calculate_cs_result(
-    mut selected_utxos: Vec<OutputGroup>,
-    mut required_utxos: Vec<OutputGroup>,
-    excess: Excess,
-) -> CoinSelectionResult {
-    selected_utxos.append(&mut required_utxos);
-    let fee_amount = selected_utxos.iter().map(|u| u.fee).sum();
-    let selected = selected_utxos
-        .into_iter()
-        .map(|u| u.weighted_utxo.utxo)
-        .collect::<Vec<_>>();
-
-    CoinSelectionResult {
-        selected,
-        fee_amount,
-        excess,
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use assert_matches::assert_matches;
-    use bitcoin::hashes::Hash;
-    use bitcoin::OutPoint;
-    use chain::{ChainPosition, ConfirmationBlockTime};
-    use core::str::FromStr;
-    use rand::rngs::StdRng;
-
-    use bitcoin::{Amount, BlockHash, ScriptBuf, TxIn, TxOut};
-
-    use super::*;
-    use crate::types::*;
-
-    use rand::prelude::SliceRandom;
-    use rand::{thread_rng, Rng, RngCore, SeedableRng};
-
-    // signature len (1WU) + signature and sighash (72WU)
-    // + pubkey len (1WU) + pubkey (33WU)
-    const P2WPKH_SATISFACTION_SIZE: usize = 1 + 72 + 1 + 33;
-
-    const FEE_AMOUNT: Amount = Amount::from_sat(50);
-
-    fn unconfirmed_utxo(value: Amount, index: u32, last_seen: u64) -> WeightedUtxo {
-        utxo(
-            value,
-            index,
-            ChainPosition::Unconfirmed {
-                last_seen: Some(last_seen),
-            },
-        )
-    }
-
-    fn confirmed_utxo(
-        value: Amount,
-        index: u32,
-        confirmation_height: u32,
-        confirmation_time: u64,
-    ) -> WeightedUtxo {
-        utxo(
-            value,
-            index,
-            ChainPosition::Confirmed {
-                anchor: ConfirmationBlockTime {
-                    block_id: chain::BlockId {
-                        height: confirmation_height,
-                        hash: bitcoin::BlockHash::all_zeros(),
-                    },
-                    confirmation_time,
-                },
-                transitively: None,
-            },
-        )
-    }
-
-    fn utxo(
-        value: Amount,
-        index: u32,
-        chain_position: ChainPosition<ConfirmationBlockTime>,
-    ) -> WeightedUtxo {
-        assert!(index < 10);
-        let outpoint = OutPoint::from_str(&format!(
-            "000000000000000000000000000000000000000000000000000000000000000{}:0",
-            index
-        ))
-        .unwrap();
-        WeightedUtxo {
-            satisfaction_weight: Weight::from_wu_usize(P2WPKH_SATISFACTION_SIZE),
-            utxo: Utxo::Local(LocalOutput {
-                outpoint,
-                txout: TxOut {
-                    value,
-                    script_pubkey: ScriptBuf::new(),
-                },
-                keychain: KeychainKind::External,
-                is_spent: false,
-                derivation_index: 42,
-                chain_position,
-            }),
-        }
-    }
-
-    fn get_test_utxos() -> Vec<WeightedUtxo> {
-        vec![
-            unconfirmed_utxo(Amount::from_sat(100_000), 0, 0),
-            unconfirmed_utxo(FEE_AMOUNT - Amount::from_sat(40), 1, 0),
-            unconfirmed_utxo(Amount::from_sat(200_000), 2, 0),
-        ]
-    }
-
-    fn get_oldest_first_test_utxos() -> Vec<WeightedUtxo> {
-        // ensure utxos are from different tx
-        let utxo1 = confirmed_utxo(Amount::from_sat(120_000), 1, 1, 1231006505);
-        let utxo2 = confirmed_utxo(Amount::from_sat(80_000), 2, 2, 1231006505);
-        let utxo3 = confirmed_utxo(Amount::from_sat(300_000), 3, 3, 1231006505);
-        vec![utxo1, utxo2, utxo3]
-    }
-
-    fn generate_random_utxos(rng: &mut StdRng, utxos_number: usize) -> Vec<WeightedUtxo> {
-        let mut res = Vec::new();
-        for i in 0..utxos_number {
-            res.push(WeightedUtxo {
-                satisfaction_weight: Weight::from_wu_usize(P2WPKH_SATISFACTION_SIZE),
-                utxo: Utxo::Local(LocalOutput {
-                    outpoint: OutPoint::from_str(&format!(
-                        "ebd9813ecebc57ff8f30797de7c205e3c7498ca950ea4341ee51a685ff2fa30a:{}",
-                        i
-                    ))
-                    .unwrap(),
-                    txout: TxOut {
-                        value: Amount::from_sat(rng.gen_range(0..200000000)),
-                        script_pubkey: ScriptBuf::new(),
-                    },
-                    keychain: KeychainKind::External,
-                    is_spent: false,
-                    derivation_index: rng.next_u32(),
-                    chain_position: if rng.gen_bool(0.5) {
-                        ChainPosition::Confirmed {
-                            anchor: ConfirmationBlockTime {
-                                block_id: chain::BlockId {
-                                    height: rng.next_u32(),
-                                    hash: BlockHash::all_zeros(),
-                                },
-                                confirmation_time: rng.next_u64(),
-                            },
-                            transitively: None,
-                        }
-                    } else {
-                        ChainPosition::Unconfirmed { last_seen: Some(0) }
-                    },
-                }),
-            });
-        }
-        res
-    }
-
-    fn generate_same_value_utxos(utxos_value: Amount, utxos_number: usize) -> Vec<WeightedUtxo> {
-        (0..utxos_number)
-            .map(|i| WeightedUtxo {
-                satisfaction_weight: Weight::from_wu_usize(P2WPKH_SATISFACTION_SIZE),
-                utxo: Utxo::Local(LocalOutput {
-                    outpoint: OutPoint::from_str(&format!(
-                        "ebd9813ecebc57ff8f30797de7c205e3c7498ca950ea4341ee51a685ff2fa30a:{}",
-                        i
-                    ))
-                    .unwrap(),
-                    txout: TxOut {
-                        value: utxos_value,
-                        script_pubkey: ScriptBuf::new(),
-                    },
-                    keychain: KeychainKind::External,
-                    is_spent: false,
-                    derivation_index: 42,
-                    chain_position: ChainPosition::Unconfirmed { last_seen: Some(0) },
-                }),
-            })
-            .collect()
-    }
-
-    fn sum_random_utxos(mut rng: &mut StdRng, utxos: &mut [WeightedUtxo]) -> Amount {
-        let utxos_picked_len = rng.gen_range(2..utxos.len() / 2);
-        utxos.shuffle(&mut rng);
-        utxos[..utxos_picked_len]
-            .iter()
-            .map(|u| u.utxo.txout().value)
-            .sum()
-    }
-
-    fn calc_target_amount(utxos: &[WeightedUtxo], fee_rate: FeeRate) -> Amount {
-        utxos
-            .iter()
-            .cloned()
-            .map(|utxo| OutputGroup::new(utxo, fee_rate).effective_value)
-            .sum::<SignedAmount>()
-            .to_unsigned()
-            .expect("unsigned amount")
-    }
-
-    #[test]
-    fn test_largest_first_coin_selection_success() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(250_000) + FEE_AMOUNT;
-
-        let result = LargestFirstCoinSelection
-            .coin_select(
-                utxos,
-                vec![],
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 3);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300_010));
-        assert_eq!(result.fee_amount, Amount::from_sat(204));
-    }
-
-    #[test]
-    fn test_largest_first_coin_selection_use_all() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(20_000) + FEE_AMOUNT;
-
-        let result = LargestFirstCoinSelection
-            .coin_select(
-                utxos,
-                vec![],
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 3);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300_010));
-        assert_eq!(result.fee_amount, Amount::from_sat(204));
-    }
-
-    #[test]
-    fn test_largest_first_coin_selection_use_only_necessary() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(20_000) + FEE_AMOUNT;
-
-        let result = LargestFirstCoinSelection
-            .coin_select(
-                vec![],
-                utxos,
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 1);
-        assert_eq!(result.selected_amount(), Amount::from_sat(200_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(68));
-    }
-
-    #[test]
-    fn test_largest_first_coin_selection_insufficient_funds() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(500_000) + FEE_AMOUNT;
-
-        let result = LargestFirstCoinSelection.coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_largest_first_coin_selection_insufficient_funds_high_fees() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(250_000) + FEE_AMOUNT;
-
-        let result = LargestFirstCoinSelection.coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1000),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_oldest_first_coin_selection_success() {
-        let utxos = get_oldest_first_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(180_000) + FEE_AMOUNT;
-
-        let result = OldestFirstCoinSelection
-            .coin_select(
-                vec![],
-                utxos,
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 2);
-        assert_eq!(result.selected_amount(), Amount::from_sat(200_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(136));
-    }
-
-    #[test]
-    fn test_oldest_first_coin_selection_use_all() {
-        let utxos = get_oldest_first_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(20_000) + FEE_AMOUNT;
-
-        let result = OldestFirstCoinSelection
-            .coin_select(
-                utxos,
-                vec![],
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 3);
-        assert_eq!(result.selected_amount(), Amount::from_sat(500_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(204));
-    }
-
-    #[test]
-    fn test_oldest_first_coin_selection_use_only_necessary() {
-        let utxos = get_oldest_first_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(20_000) + FEE_AMOUNT;
-
-        let result = OldestFirstCoinSelection
-            .coin_select(
-                vec![],
-                utxos,
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 1);
-        assert_eq!(result.selected_amount(), Amount::from_sat(120_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(68));
-    }
-
-    #[test]
-    fn test_oldest_first_coin_selection_insufficient_funds() {
-        let utxos = get_oldest_first_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(600_000) + FEE_AMOUNT;
-
-        let result = OldestFirstCoinSelection.coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_oldest_first_coin_selection_insufficient_funds_high_fees() {
-        let utxos = get_oldest_first_test_utxos();
-
-        let target_amount =
-            utxos.iter().map(|wu| wu.utxo.txout().value).sum::<Amount>() - Amount::from_sat(50);
-        let drain_script = ScriptBuf::default();
-
-        let result = OldestFirstCoinSelection.coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1000),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_success() {
-        // In this case bnb won't find a suitable match and single random draw will
-        // select three outputs
-        let utxos = generate_same_value_utxos(Amount::from_sat(100_000), 20);
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(250_000) + FEE_AMOUNT;
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-            .coin_select(
-                vec![],
-                utxos,
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 3);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(204));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_required_are_enough() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(20_000) + FEE_AMOUNT;
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-            .coin_select(
-                utxos.clone(),
-                utxos,
-                FeeRate::from_sat_per_vb_unchecked(1),
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 3);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300_010));
-        assert_eq!(result.fee_amount, Amount::from_sat(204));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_optional_are_enough() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let fee_rate = FeeRate::BROADCAST_MIN;
-        // first and third utxo's effective value
-        let target_amount = calc_target_amount(&[utxos[0].clone(), utxos[2].clone()], fee_rate);
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-            .coin_select(
-                vec![],
-                utxos,
-                fee_rate,
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 2);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300000));
-        assert_eq!(result.fee_amount, Amount::from_sat(136));
-    }
-
-    #[test]
-    fn test_single_random_draw_function_success() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let mut utxos = generate_random_utxos(&mut rng, 300);
-        let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-        let drain_script = ScriptBuf::default();
-
-        let result = SingleRandomDraw.coin_select(
-            vec![],
-            utxos,
-            fee_rate,
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-
-        assert!(
-            matches!(result, Ok(CoinSelectionResult {selected, fee_amount, ..})
-                if selected.iter().map(|u| u.txout().value).sum::<Amount>() > target_amount
-                && fee_amount == Amount::from_sat(selected.len() as u64 * 68)
-            )
-        );
-    }
-
-    #[test]
-    fn test_single_random_draw_function_error() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-
-        // 100_000, 10, 200_000
-        let utxos = get_test_utxos();
-        let target_amount = Amount::from_sat(300_000) + FEE_AMOUNT;
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-        let drain_script = ScriptBuf::default();
-
-        let result = SingleRandomDraw.coin_select(
-            vec![],
-            utxos,
-            fee_rate,
-            target_amount,
-            &drain_script,
-            &mut rng,
-        );
-
-        assert!(matches!(result, Err(InsufficientFunds {needed, available})
-                if needed == Amount::from_sat(300_254) && available == Amount::from_sat(300_010)));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_required_not_enough() {
-        let utxos = get_test_utxos();
-
-        let required = vec![utxos[0].clone()];
-        let mut optional = utxos[1..].to_vec();
-        optional.push(utxo(
-            Amount::from_sat(500_000),
-            3,
-            ChainPosition::<ConfirmationBlockTime>::Unconfirmed { last_seen: Some(0) },
-        ));
-
-        // Defensive assertions, for sanity and in case someone changes the test utxos vector.
-        let amount = required
-            .iter()
-            .map(|u| u.utxo.txout().value)
-            .sum::<Amount>();
-        assert_eq!(amount, Amount::from_sat(100_000));
-        let amount = optional
-            .iter()
-            .map(|u| u.utxo.txout().value)
-            .sum::<Amount>();
-        assert!(amount > Amount::from_sat(150_000));
-        let drain_script = ScriptBuf::default();
-
-        let fee_rate = FeeRate::BROADCAST_MIN;
-        // first and third utxo's effective value
-        let target_amount = calc_target_amount(&[utxos[0].clone(), utxos[2].clone()], fee_rate);
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-            .coin_select(
-                required,
-                optional,
-                fee_rate,
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 2);
-        assert_eq!(result.selected_amount(), Amount::from_sat(300_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(136));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_insufficient_funds() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(500_000) + FEE_AMOUNT;
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_insufficient_funds_high_fees() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let target_amount = Amount::from_sat(250_000) + FEE_AMOUNT;
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(1000),
-            target_amount,
-            &drain_script,
-            &mut thread_rng(),
-        );
-        assert!(matches!(result, Err(InsufficientFunds { .. })));
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_check_fee_rate() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-        let fee_rate = FeeRate::BROADCAST_MIN;
-        // first utxo's effective value
-        let target_amount = calc_target_amount(&utxos[0..1], fee_rate);
-
-        let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-            .coin_select(
-                vec![],
-                utxos,
-                fee_rate,
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-
-        assert_eq!(result.selected.len(), 1);
-        assert_eq!(result.selected_amount(), Amount::from_sat(100_000));
-        let input_weight =
-            TxIn::default().segwit_weight().to_wu() + P2WPKH_SATISFACTION_SIZE as u64;
-        // the final fee rate should be exactly the same as the fee rate given
-        let result_feerate = result.fee_amount / Weight::from_wu(input_weight);
-        assert_eq!(result_feerate, fee_rate);
-    }
-
-    #[test]
-    fn test_bnb_coin_selection_exact_match() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-
-        for _i in 0..200 {
-            let mut optional_utxos = generate_random_utxos(&mut rng, 16);
-            let target_amount = sum_random_utxos(&mut rng, &mut optional_utxos);
-            let drain_script = ScriptBuf::default();
-            let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-                .coin_select(
-                    vec![],
-                    optional_utxos,
-                    FeeRate::ZERO,
-                    target_amount,
-                    &drain_script,
-                    &mut thread_rng(),
-                )
-                .unwrap();
-            assert_eq!(result.selected_amount(), target_amount);
-        }
-    }
-
-    #[test]
-    fn test_bnb_function_no_exact_match() {
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
-        let utxos: Vec<OutputGroup> = get_test_utxos()
-            .into_iter()
-            .map(|u| OutputGroup::new(u, fee_rate))
-            .collect();
-
-        let curr_available_value = utxos
-            .iter()
-            .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-        let size_of_change = 31;
-        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate)
-            .to_signed()
-            .unwrap();
-
-        let drain_script = ScriptBuf::default();
-        let target_amount = SignedAmount::from_sat(20_000) + FEE_AMOUNT.to_signed().unwrap();
-        let result = BranchAndBoundCoinSelection::new(size_of_change, SingleRandomDraw).bnb(
-            vec![],
-            utxos,
-            SignedAmount::ZERO,
-            curr_available_value,
-            target_amount,
-            cost_of_change,
-            &drain_script,
-            fee_rate,
-        );
-        assert!(matches!(result, Err(BnbError::NoExactMatch)));
-    }
-
-    #[test]
-    fn test_bnb_function_tries_exceeded() {
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(10);
-        let utxos: Vec<OutputGroup> = generate_same_value_utxos(Amount::from_sat(100_000), 100_000)
-            .into_iter()
-            .map(|u| OutputGroup::new(u, fee_rate))
-            .collect();
-
-        let curr_available_value = utxos
-            .iter()
-            .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-        let size_of_change = 31;
-        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate)
-            .to_signed()
-            .unwrap();
-        let target_amount = SignedAmount::from_sat(20_000) + FEE_AMOUNT.to_signed().unwrap();
-
-        let drain_script = ScriptBuf::default();
-
-        let result = BranchAndBoundCoinSelection::new(size_of_change, SingleRandomDraw).bnb(
-            vec![],
-            utxos,
-            SignedAmount::ZERO,
-            curr_available_value,
-            target_amount,
-            cost_of_change,
-            &drain_script,
-            fee_rate,
-        );
-        assert!(matches!(result, Err(BnbError::TotalTriesExceeded)));
-    }
-
-    // The match won't be exact but still in the range
-    #[test]
-    fn test_bnb_function_almost_exact_match_with_fees() {
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-        let size_of_change = 31;
-        let cost_of_change = (Weight::from_vb_unchecked(size_of_change) * fee_rate)
-            .to_signed()
-            .unwrap();
-
-        let utxos: Vec<_> = generate_same_value_utxos(Amount::from_sat(50_000), 10)
-            .into_iter()
-            .map(|u| OutputGroup::new(u, fee_rate))
-            .collect();
-
-        let curr_value = SignedAmount::ZERO;
-
-        let curr_available_value = utxos
-            .iter()
-            .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-        // 2*(value of 1 utxo)  - 2*(1 utxo fees with 1.0sat/vbyte fee rate) -
-        // cost_of_change + 5.
-        let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.to_sat() + 5;
-        let target_amount = SignedAmount::from_sat(target_amount);
-
-        let drain_script = ScriptBuf::default();
-
-        let result = BranchAndBoundCoinSelection::new(size_of_change, SingleRandomDraw)
-            .bnb(
-                vec![],
-                utxos,
-                curr_value,
-                curr_available_value,
-                target_amount,
-                cost_of_change,
-                &drain_script,
-                fee_rate,
-            )
-            .unwrap();
-        assert_eq!(result.selected_amount(), Amount::from_sat(100_000));
-        assert_eq!(result.fee_amount, Amount::from_sat(136));
-    }
-
-    // TODO: bnb() function should be optimized, and this test should be done with more utxos
-    #[test]
-    fn test_bnb_function_exact_match_more_utxos() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let fee_rate = FeeRate::ZERO;
-
-        for _ in 0..200 {
-            let optional_utxos: Vec<_> = generate_random_utxos(&mut rng, 40)
-                .into_iter()
-                .map(|u| OutputGroup::new(u, fee_rate))
-                .collect();
-
-            let curr_value = SignedAmount::ZERO;
-
-            let curr_available_value = optional_utxos
-                .iter()
-                .fold(SignedAmount::ZERO, |acc, x| acc + x.effective_value);
-
-            let target_amount =
-                optional_utxos[3].effective_value + optional_utxos[23].effective_value;
-
-            let drain_script = ScriptBuf::default();
-
-            let result = BranchAndBoundCoinSelection::<SingleRandomDraw>::default()
-                .bnb(
-                    vec![],
-                    optional_utxos,
-                    curr_value,
-                    curr_available_value,
-                    target_amount,
-                    SignedAmount::ZERO,
-                    &drain_script,
-                    fee_rate,
-                )
-                .unwrap();
-            assert_eq!(
-                result.selected_amount(),
-                target_amount.to_unsigned().unwrap()
-            );
-        }
-    }
-
-    #[test]
-    fn test_bnb_exclude_negative_effective_value() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-
-        let selection = BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-            vec![],
-            utxos,
-            FeeRate::from_sat_per_vb_unchecked(10),
-            Amount::from_sat(500_000),
-            &drain_script,
-            &mut thread_rng(),
-        );
-
-        assert_matches!(
-            selection,
-            Err(InsufficientFunds {
-                available,
-                ..
-            }) if available.to_sat() == 300_000
-        );
-    }
-
-    #[test]
-    fn test_bnb_include_negative_effective_value_when_required() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-
-        let (required, optional) = utxos.into_iter().partition(
-            |u| matches!(u, WeightedUtxo { utxo, .. } if utxo.txout().value.to_sat() < 1000),
-        );
-
-        let selection = BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-            required,
-            optional,
-            FeeRate::from_sat_per_vb_unchecked(10),
-            Amount::from_sat(500_000),
-            &drain_script,
-            &mut thread_rng(),
-        );
-
-        assert_matches!(
-            selection,
-            Err(InsufficientFunds {
-                available,
-                ..
-            }) if available.to_sat() == 300_010
-        );
-    }
-
-    #[test]
-    fn test_bnb_sum_of_effective_value_negative() {
-        let utxos = get_test_utxos();
-        let drain_script = ScriptBuf::default();
-
-        let selection = BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-            utxos,
-            vec![],
-            FeeRate::from_sat_per_vb_unchecked(10_000),
-            Amount::from_sat(500_000),
-            &drain_script,
-            &mut thread_rng(),
-        );
-
-        assert_matches!(
-            selection,
-            Err(InsufficientFunds {
-                available,
-                ..
-            }) if available.to_sat() == 300_010
-        );
-    }
-
-    #[test]
-    fn test_bnb_fallback_algorithm() {
-        // utxo value
-        // 120k + 80k + 300k
-        let optional_utxos = get_oldest_first_test_utxos();
-        let feerate = FeeRate::BROADCAST_MIN;
-        let target_amount = Amount::from_sat(190_000);
-        let drain_script = ScriptBuf::new();
-        // bnb won't find exact match and should select oldest first
-        let bnb_with_oldest_first =
-            BranchAndBoundCoinSelection::new(8 + 1 + 22, OldestFirstCoinSelection);
-        let res = bnb_with_oldest_first
-            .coin_select(
-                vec![],
-                optional_utxos,
-                feerate,
-                target_amount,
-                &drain_script,
-                &mut thread_rng(),
-            )
-            .unwrap();
-        assert_eq!(res.selected_amount(), Amount::from_sat(200_000));
-    }
-
-    #[test]
-    fn test_deterministic_coin_selection_picks_same_utxos() {
-        enum CoinSelectionAlgo {
-            BranchAndBound,
-            OldestFirst,
-            LargestFirst,
-        }
-
-        struct TestCase<'a> {
-            name: &'a str,
-            coin_selection_algo: CoinSelectionAlgo,
-            exp_vouts: &'a [u32],
-        }
-
-        let test_cases = [
-            TestCase {
-                name: "branch and bound",
-                coin_selection_algo: CoinSelectionAlgo::BranchAndBound,
-                // note: we expect these to be sorted largest first, which indicates
-                // BnB succeeded with no fallback
-                exp_vouts: &[29, 28, 27],
-            },
-            TestCase {
-                name: "oldest first",
-                coin_selection_algo: CoinSelectionAlgo::OldestFirst,
-                exp_vouts: &[0, 1, 2],
-            },
-            TestCase {
-                name: "largest first",
-                coin_selection_algo: CoinSelectionAlgo::LargestFirst,
-                exp_vouts: &[29, 28, 27],
-            },
-        ];
-
-        let optional = generate_same_value_utxos(Amount::from_sat(100_000), 30);
-        let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-        let target_amount = calc_target_amount(&optional[0..3], fee_rate);
-        assert_eq!(target_amount, Amount::from_sat(299_796));
-        let drain_script = ScriptBuf::default();
-
-        for tc in test_cases {
-            let optional = optional.clone();
-
-            let result = match tc.coin_selection_algo {
-                CoinSelectionAlgo::BranchAndBound => {
-                    BranchAndBoundCoinSelection::<SingleRandomDraw>::default().coin_select(
-                        vec![],
-                        optional,
-                        fee_rate,
-                        target_amount,
-                        &drain_script,
-                        &mut thread_rng(),
-                    )
-                }
-                CoinSelectionAlgo::OldestFirst => OldestFirstCoinSelection.coin_select(
-                    vec![],
-                    optional,
-                    fee_rate,
-                    target_amount,
-                    &drain_script,
-                    &mut thread_rng(),
-                ),
-                CoinSelectionAlgo::LargestFirst => LargestFirstCoinSelection.coin_select(
-                    vec![],
-                    optional,
-                    fee_rate,
-                    target_amount,
-                    &drain_script,
-                    &mut thread_rng(),
-                ),
-            };
-
-            assert!(result.is_ok(), "coin_select failed {}", tc.name);
-            let result = result.unwrap();
-            assert!(matches!(result.excess, Excess::NoChange { .. },));
-            assert_eq!(
-                result.selected.len(),
-                3,
-                "wrong selected len for {}",
-                tc.name
-            );
-            assert_eq!(
-                result.selected_amount(),
-                Amount::from_sat(300_000),
-                "wrong selected amount for {}",
-                tc.name
-            );
-            assert_eq!(
-                result.fee_amount,
-                Amount::from_sat(204),
-                "wrong fee amount for {}",
-                tc.name
-            );
-            let vouts = result
-                .selected
-                .iter()
-                .map(|utxo| utxo.outpoint().vout)
-                .collect::<Vec<u32>>();
-            assert_eq!(vouts, tc.exp_vouts, "wrong selected vouts for {}", tc.name);
-        }
-    }
-}
diff --git a/crates/wallet/src/wallet/error.rs b/crates/wallet/src/wallet/error.rs
deleted file mode 100644 (file)
index adce5b1..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Errors that can be thrown by the [`Wallet`](crate::wallet::Wallet)
-
-use crate::descriptor::policy::PolicyError;
-use crate::descriptor::DescriptorError;
-use crate::wallet::coin_selection;
-use crate::{descriptor, KeychainKind};
-use alloc::string::String;
-use bitcoin::{absolute, psbt, Amount, OutPoint, Sequence, Txid};
-use core::fmt;
-
-/// Errors returned by miniscript when updating inconsistent PSBTs
-#[derive(Debug, Clone)]
-pub enum MiniscriptPsbtError {
-    /// Descriptor key conversion error
-    Conversion(miniscript::descriptor::ConversionError),
-    /// Return error type for PsbtExt::update_input_with_descriptor
-    UtxoUpdate(miniscript::psbt::UtxoUpdateError),
-    /// Return error type for PsbtExt::update_output_with_descriptor
-    OutputUpdate(miniscript::psbt::OutputUpdateError),
-}
-
-impl fmt::Display for MiniscriptPsbtError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Conversion(err) => write!(f, "Conversion error: {}", err),
-            Self::UtxoUpdate(err) => write!(f, "UTXO update error: {}", err),
-            Self::OutputUpdate(err) => write!(f, "Output update error: {}", err),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for MiniscriptPsbtError {}
-
-#[derive(Debug)]
-/// Error returned from [`TxBuilder::finish`]
-///
-/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
-pub enum CreateTxError {
-    /// There was a problem with the descriptors passed in
-    Descriptor(DescriptorError),
-    /// There was a problem while extracting and manipulating policies
-    Policy(PolicyError),
-    /// Spending policy is not compatible with this [`KeychainKind`]
-    SpendingPolicyRequired(KeychainKind),
-    /// Requested invalid transaction version '0'
-    Version0,
-    /// Requested transaction version `1`, but at least `2` is needed to use OP_CSV
-    Version1Csv,
-    /// Requested `LockTime` is less than is required to spend from this script
-    LockTime {
-        /// Requested `LockTime`
-        requested: absolute::LockTime,
-        /// Required `LockTime`
-        required: absolute::LockTime,
-    },
-    /// Cannot enable RBF with `Sequence` given a required OP_CSV
-    RbfSequenceCsv {
-        /// Given RBF `Sequence`
-        sequence: Sequence,
-        /// Required OP_CSV `Sequence`
-        csv: Sequence,
-    },
-    /// When bumping a tx the absolute fee requested is lower than replaced tx absolute fee
-    FeeTooLow {
-        /// Required fee absolute value [`Amount`]
-        required: Amount,
-    },
-    /// When bumping a tx the fee rate requested is lower than required
-    FeeRateTooLow {
-        /// Required fee rate
-        required: bitcoin::FeeRate,
-    },
-    /// `manually_selected_only` option is selected but no utxo has been passed
-    NoUtxosSelected,
-    /// Output created is under the dust limit, 546 satoshis
-    OutputBelowDustLimit(usize),
-    /// There was an error with coin selection
-    CoinSelection(coin_selection::InsufficientFunds),
-    /// Cannot build a tx without recipients
-    NoRecipients,
-    /// Partially signed bitcoin transaction error
-    Psbt(psbt::Error),
-    /// In order to use the [`TxBuilder::add_global_xpubs`] option every extended
-    /// key in the descriptor must either be a master key itself (having depth = 0) or have an
-    /// explicit origin provided
-    ///
-    /// [`TxBuilder::add_global_xpubs`]: crate::wallet::tx_builder::TxBuilder::add_global_xpubs
-    MissingKeyOrigin(String),
-    /// Happens when trying to spend an UTXO that is not in the internal database
-    UnknownUtxo,
-    /// Missing non_witness_utxo on foreign utxo for given `OutPoint`
-    MissingNonWitnessUtxo(OutPoint),
-    /// Miniscript PSBT error
-    MiniscriptPsbt(MiniscriptPsbtError),
-}
-
-impl fmt::Display for CreateTxError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Descriptor(e) => e.fmt(f),
-            Self::Policy(e) => e.fmt(f),
-            CreateTxError::SpendingPolicyRequired(keychain_kind) => {
-                write!(f, "Spending policy required: {:?}", keychain_kind)
-            }
-            CreateTxError::Version0 => {
-                write!(f, "Invalid version `0`")
-            }
-            CreateTxError::Version1Csv => {
-                write!(
-                    f,
-                    "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
-                )
-            }
-            CreateTxError::LockTime {
-                requested,
-                required,
-            } => {
-                write!(f, "TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script", required, requested)
-            }
-            CreateTxError::RbfSequenceCsv { sequence, csv } => {
-                write!(
-                    f,
-                    "Cannot enable RBF with nSequence `{:?}` given a required OP_CSV of `{:?}`",
-                    sequence, csv
-                )
-            }
-            CreateTxError::FeeTooLow { required } => {
-                write!(f, "Fee to low: required {}", required.display_dynamic())
-            }
-            CreateTxError::FeeRateTooLow { required } => {
-                write!(
-                    f,
-                    // Note: alternate fmt as sat/vb (ceil) available in bitcoin-0.31
-                    //"Fee rate too low: required {required:#}"
-                    "Fee rate too low: required {} sat/vb",
-                    crate::floating_rate!(required)
-                )
-            }
-            CreateTxError::NoUtxosSelected => {
-                write!(f, "No UTXO selected")
-            }
-            CreateTxError::OutputBelowDustLimit(limit) => {
-                write!(f, "Output below the dust limit: {}", limit)
-            }
-            CreateTxError::CoinSelection(e) => e.fmt(f),
-            CreateTxError::NoRecipients => {
-                write!(f, "Cannot build tx without recipients")
-            }
-            CreateTxError::Psbt(e) => e.fmt(f),
-            CreateTxError::MissingKeyOrigin(err) => {
-                write!(f, "Missing key origin: {}", err)
-            }
-            CreateTxError::UnknownUtxo => {
-                write!(f, "UTXO not found in the internal database")
-            }
-            CreateTxError::MissingNonWitnessUtxo(outpoint) => {
-                write!(f, "Missing non_witness_utxo on foreign utxo {}", outpoint)
-            }
-            CreateTxError::MiniscriptPsbt(err) => {
-                write!(f, "Miniscript PSBT error: {}", err)
-            }
-        }
-    }
-}
-
-impl From<descriptor::error::Error> for CreateTxError {
-    fn from(err: descriptor::error::Error) -> Self {
-        CreateTxError::Descriptor(err)
-    }
-}
-
-impl From<PolicyError> for CreateTxError {
-    fn from(err: PolicyError) -> Self {
-        CreateTxError::Policy(err)
-    }
-}
-
-impl From<MiniscriptPsbtError> for CreateTxError {
-    fn from(err: MiniscriptPsbtError) -> Self {
-        CreateTxError::MiniscriptPsbt(err)
-    }
-}
-
-impl From<psbt::Error> for CreateTxError {
-    fn from(err: psbt::Error) -> Self {
-        CreateTxError::Psbt(err)
-    }
-}
-
-impl From<coin_selection::InsufficientFunds> for CreateTxError {
-    fn from(err: coin_selection::InsufficientFunds) -> Self {
-        CreateTxError::CoinSelection(err)
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for CreateTxError {}
-
-#[derive(Debug)]
-/// Error returned from [`Wallet::build_fee_bump`]
-///
-/// [`Wallet::build_fee_bump`]: super::Wallet::build_fee_bump
-pub enum BuildFeeBumpError {
-    /// Happens when trying to spend an UTXO that is not in the internal database
-    UnknownUtxo(OutPoint),
-    /// Thrown when a tx is not found in the internal database
-    TransactionNotFound(Txid),
-    /// Happens when trying to bump a transaction that is already confirmed
-    TransactionConfirmed(Txid),
-    /// Trying to replace a tx that has a sequence >= `0xFFFFFFFE`
-    IrreplaceableTransaction(Txid),
-    /// Node doesn't have data to estimate a fee rate
-    FeeRateUnavailable,
-}
-
-impl fmt::Display for BuildFeeBumpError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::UnknownUtxo(outpoint) => write!(
-                f,
-                "UTXO not found in the internal database with txid: {}, vout: {}",
-                outpoint.txid, outpoint.vout
-            ),
-            Self::TransactionNotFound(txid) => {
-                write!(
-                    f,
-                    "Transaction not found in the internal database with txid: {}",
-                    txid
-                )
-            }
-            Self::TransactionConfirmed(txid) => {
-                write!(f, "Transaction already confirmed with txid: {}", txid)
-            }
-            Self::IrreplaceableTransaction(txid) => {
-                write!(f, "Transaction can't be replaced with txid: {}", txid)
-            }
-            Self::FeeRateUnavailable => write!(f, "Fee rate unavailable"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for BuildFeeBumpError {}
diff --git a/crates/wallet/src/wallet/export.rs b/crates/wallet/src/wallet/export.rs
deleted file mode 100644 (file)
index cbbee2e..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Wallet export
-//!
-//! This modules implements the wallet export format used by [FullyNoded](https://github.com/Fonta1n3/FullyNoded/blob/10b7808c8b929b171cca537fb50522d015168ac9/Docs/Wallets/Wallet-Export-Spec.md).
-//!
-//! ## Examples
-//!
-//! ### Import from JSON
-//!
-//! ```
-//! # use std::str::FromStr;
-//! # use bitcoin::*;
-//! # use bdk_wallet::export::*;
-//! # use bdk_wallet::*;
-//! let import = r#"{
-//!     "descriptor": "wpkh([c258d2e4\/84h\/1h\/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe\/0\/*)",
-//!     "blockheight":1782088,
-//!     "label":"testnet"
-//! }"#;
-//!
-//! let import = FullyNodedExport::from_str(import)?;
-//! let wallet = Wallet::create(
-//!     import.descriptor(),
-//!     import.change_descriptor().expect("change descriptor"),
-//! )
-//! .network(Network::Testnet)
-//! .create_wallet_no_persist()?;
-//! # Ok::<_, Box<dyn std::error::Error>>(())
-//! ```
-//!
-//! ### Export a `Wallet`
-//! ```
-//! # use bitcoin::*;
-//! # use bdk_wallet::export::*;
-//! # use bdk_wallet::*;
-//! let wallet = Wallet::create(
-//!     "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
-//!     "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)",
-//! )
-//! .network(Network::Testnet)
-//! .create_wallet_no_persist()?;
-//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true).unwrap();
-//!
-//! println!("Exported: {}", export.to_string());
-//! # Ok::<_, Box<dyn std::error::Error>>(())
-//! ```
-
-use alloc::string::String;
-use core::fmt;
-use core::str::FromStr;
-use serde::{Deserialize, Serialize};
-
-use miniscript::descriptor::{ShInner, WshInner};
-use miniscript::{Descriptor, ScriptContext, Terminal};
-
-use crate::types::KeychainKind;
-use crate::wallet::Wallet;
-
-/// Alias for [`FullyNodedExport`]
-#[deprecated(since = "0.18.0", note = "Please use [`FullyNodedExport`] instead")]
-pub type WalletExport = FullyNodedExport;
-
-/// Structure that contains the export of a wallet
-///
-/// For a usage example see [this module](crate::wallet::export)'s documentation.
-#[derive(Debug, Serialize, Deserialize)]
-pub struct FullyNodedExport {
-    descriptor: String,
-    /// Earliest block to rescan when looking for the wallet's transactions
-    pub blockheight: u32,
-    /// Arbitrary label for the wallet
-    pub label: String,
-}
-
-impl fmt::Display for FullyNodedExport {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", serde_json::to_string(self).unwrap())
-    }
-}
-
-impl FromStr for FullyNodedExport {
-    type Err = serde_json::Error;
-
-    fn from_str(s: &str) -> Result<Self, Self::Err> {
-        serde_json::from_str(s)
-    }
-}
-
-fn remove_checksum(s: String) -> String {
-    s.split_once('#').map(|(a, _)| String::from(a)).unwrap()
-}
-
-impl FullyNodedExport {
-    /// Export a wallet
-    ///
-    /// This function returns an error if it determines that the `wallet`'s descriptor(s) are not
-    /// supported by Bitcoin Core or don't follow the standard derivation paths defined by BIP44
-    /// and others.
-    ///
-    /// If `include_blockheight` is `true`, this function will look into the `wallet`'s database
-    /// for the oldest transaction it knows and use that as the earliest block to rescan.
-    ///
-    /// If the database is empty or `include_blockheight` is false, the `blockheight` field
-    /// returned will be `0`.
-    pub fn export_wallet(
-        wallet: &Wallet,
-        label: &str,
-        include_blockheight: bool,
-    ) -> Result<Self, &'static str> {
-        let descriptor = wallet
-            .public_descriptor(KeychainKind::External)
-            .to_string_with_secret(
-                &wallet
-                    .get_signers(KeychainKind::External)
-                    .as_key_map(wallet.secp_ctx()),
-            );
-        let descriptor = remove_checksum(descriptor);
-        Self::is_compatible_with_core(&descriptor)?;
-
-        let blockheight = if include_blockheight {
-            wallet.transactions().next().map_or(0, |canonical_tx| {
-                canonical_tx
-                    .chain_position
-                    .confirmation_height_upper_bound()
-                    .unwrap_or(0)
-            })
-        } else {
-            0
-        };
-
-        let export = FullyNodedExport {
-            descriptor,
-            label: label.into(),
-            blockheight,
-        };
-
-        let change_descriptor = {
-            let descriptor = wallet
-                .public_descriptor(KeychainKind::Internal)
-                .to_string_with_secret(
-                    &wallet
-                        .get_signers(KeychainKind::Internal)
-                        .as_key_map(wallet.secp_ctx()),
-                );
-            Some(remove_checksum(descriptor))
-        };
-
-        if export.change_descriptor() != change_descriptor {
-            return Err("Incompatible change descriptor");
-        }
-
-        Ok(export)
-    }
-
-    fn is_compatible_with_core(descriptor: &str) -> Result<(), &'static str> {
-        fn check_ms<Ctx: ScriptContext>(
-            terminal: &Terminal<String, Ctx>,
-        ) -> Result<(), &'static str> {
-            if let Terminal::Multi(_) = terminal {
-                Ok(())
-            } else {
-                Err("The descriptor contains operators not supported by Bitcoin Core")
-            }
-        }
-
-        // pkh(), wpkh(), sh(wpkh()) are always fine, as well as multi() and sortedmulti()
-        match Descriptor::<String>::from_str(descriptor).map_err(|_| "Invalid descriptor")? {
-            Descriptor::Pkh(_) | Descriptor::Wpkh(_) => Ok(()),
-            Descriptor::Sh(sh) => match sh.as_inner() {
-                ShInner::Wpkh(_) => Ok(()),
-                ShInner::SortedMulti(_) => Ok(()),
-                ShInner::Wsh(wsh) => match wsh.as_inner() {
-                    WshInner::SortedMulti(_) => Ok(()),
-                    WshInner::Ms(ms) => check_ms(&ms.node),
-                },
-                ShInner::Ms(ms) => check_ms(&ms.node),
-            },
-            Descriptor::Wsh(wsh) => match wsh.as_inner() {
-                WshInner::SortedMulti(_) => Ok(()),
-                WshInner::Ms(ms) => check_ms(&ms.node),
-            },
-            Descriptor::Tr(_) => Ok(()),
-            _ => Err("The descriptor is not compatible with Bitcoin Core"),
-        }
-    }
-
-    /// Return the external descriptor
-    pub fn descriptor(&self) -> String {
-        self.descriptor.clone()
-    }
-
-    /// Return the internal descriptor, if present
-    pub fn change_descriptor(&self) -> Option<String> {
-        let replaced = self.descriptor.replace("/0/*", "/1/*");
-
-        if replaced != self.descriptor {
-            Some(replaced)
-        } else {
-            None
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use alloc::string::ToString;
-    use core::str::FromStr;
-
-    use bdk_chain::BlockId;
-    use bitcoin::{hashes::Hash, BlockHash, Network};
-
-    use super::*;
-    use crate::test_utils::*;
-    use crate::Wallet;
-
-    fn get_test_wallet(descriptor: &str, change_descriptor: &str, network: Network) -> Wallet {
-        let mut wallet = Wallet::create(descriptor.to_string(), change_descriptor.to_string())
-            .network(network)
-            .create_wallet_no_persist()
-            .expect("must create wallet");
-        let block = BlockId {
-            height: 5000,
-            hash: BlockHash::all_zeros(),
-        };
-        insert_checkpoint(&mut wallet, block);
-        receive_output_in_latest_block(&mut wallet, 10_000);
-
-        wallet
-    }
-
-    #[test]
-    fn test_export_bip44() {
-        let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
-        let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
-
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
-        let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-
-        assert_eq!(export.descriptor(), descriptor);
-        assert_eq!(export.change_descriptor(), Some(change_descriptor.into()));
-        assert_eq!(export.blockheight, 5000);
-        assert_eq!(export.label, "Test Label");
-    }
-
-    #[test]
-    #[should_panic(expected = "Incompatible change descriptor")]
-    fn test_export_no_change() {
-        // The wallet's change descriptor has no wildcard. It should be impossible to
-        // export, because exporting this kind of external descriptor normally implies the
-        // existence of a compatible internal descriptor
-
-        let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
-        let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/0)";
-
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
-        FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-    }
-
-    #[test]
-    #[should_panic(expected = "Incompatible change descriptor")]
-    fn test_export_incompatible_change() {
-        // This wallet has a change descriptor, but the derivation path is not in the "standard"
-        // bip44/49/etc format
-
-        let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
-        let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/50'/0'/1/*)";
-
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
-        FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-    }
-
-    #[test]
-    fn test_export_multi() {
-        let descriptor = "wsh(multi(2,\
-                                [73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*,\
-                                [f9f62194/48'/0'/0'/2']tpubDDp3ZSH1yCwusRppH7zgSxq2t1VEUyXSeEp8E5aFS8m43MknUjiF1bSLo3CGWAxbDyhF1XowA5ukPzyJZjznYk3kYi6oe7QxtX2euvKWsk4/0/*,\
-                                [c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/0/*\
-                          ))";
-        let change_descriptor = "wsh(multi(2,\
-                                       [73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/1/*,\
-                                       [f9f62194/48'/0'/0'/2']tpubDDp3ZSH1yCwusRppH7zgSxq2t1VEUyXSeEp8E5aFS8m43MknUjiF1bSLo3CGWAxbDyhF1XowA5ukPzyJZjznYk3kYi6oe7QxtX2euvKWsk4/1/*,\
-                                       [c98b1535/48'/0'/0'/2']tpubDCDi5W4sP6zSnzJeowy8rQDVhBdRARaPhK1axABi8V1661wEPeanpEXj4ZLAUEoikVtoWcyK26TKKJSecSfeKxwHCcRrge9k1ybuiL71z4a/1/*\
-                                 ))";
-
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Testnet);
-        let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-
-        assert_eq!(export.descriptor(), descriptor);
-        assert_eq!(export.change_descriptor(), Some(change_descriptor.into()));
-        assert_eq!(export.blockheight, 5000);
-        assert_eq!(export.label, "Test Label");
-    }
-
-    #[test]
-    fn test_export_tr() {
-        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
-        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Testnet);
-        let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-        assert_eq!(export.descriptor(), descriptor);
-        assert_eq!(export.change_descriptor(), Some(change_descriptor.into()));
-        assert_eq!(export.blockheight, 5000);
-        assert_eq!(export.label, "Test Label");
-    }
-
-    #[test]
-    fn test_export_to_json() {
-        let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
-        let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
-
-        let wallet = get_test_wallet(descriptor, change_descriptor, Network::Bitcoin);
-        let export = FullyNodedExport::export_wallet(&wallet, "Test Label", true).unwrap();
-
-        assert_eq!(export.to_string(), "{\"descriptor\":\"wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44\'/0\'/0\'/0/*)\",\"blockheight\":5000,\"label\":\"Test Label\"}");
-    }
-
-    #[test]
-    fn test_export_from_json() {
-        let descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/0/*)";
-        let change_descriptor = "wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44'/0'/0'/1/*)";
-
-        let import_str = "{\"descriptor\":\"wpkh(xprv9s21ZrQH143K4CTb63EaMxja1YiTnSEWKMbn23uoEnAzxjdUJRQkazCAtzxGm4LSoTSVTptoV9RbchnKPW9HxKtZumdyxyikZFDLhogJ5Uj/44\'/0\'/0\'/0/*)\",\"blockheight\":5000,\"label\":\"Test Label\"}";
-        let export = FullyNodedExport::from_str(import_str).unwrap();
-
-        assert_eq!(export.descriptor(), descriptor);
-        assert_eq!(export.change_descriptor(), Some(change_descriptor.into()));
-        assert_eq!(export.blockheight, 5000);
-        assert_eq!(export.label, "Test Label");
-    }
-}
diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs
deleted file mode 100644 (file)
index e4dc6d0..0000000
+++ /dev/null
@@ -1,2631 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Wallet
-//!
-//! This module defines the [`Wallet`].
-
-use alloc::{
-    boxed::Box,
-    string::{String, ToString},
-    sync::Arc,
-    vec::Vec,
-};
-use core::{cmp::Ordering, fmt, mem, ops::Deref};
-
-use bdk_chain::{
-    indexed_tx_graph,
-    indexer::keychain_txout::KeychainTxOutIndex,
-    local_chain::{ApplyHeaderError, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
-    spk_client::{
-        FullScanRequest, FullScanRequestBuilder, FullScanResponse, SyncRequest, SyncRequestBuilder,
-        SyncResponse,
-    },
-    tx_graph::{CalculateFeeError, CanonicalTx, TxGraph, TxUpdate},
-    BlockId, ChainPosition, ConfirmationBlockTime, DescriptorExt, FullTxOut, Indexed,
-    IndexedTxGraph, Indexer, Merge,
-};
-use bitcoin::{
-    absolute,
-    consensus::encode::serialize,
-    constants::genesis_block,
-    psbt,
-    secp256k1::Secp256k1,
-    sighash::{EcdsaSighashType, TapSighashType},
-    transaction, Address, Amount, Block, BlockHash, FeeRate, Network, OutPoint, Psbt, ScriptBuf,
-    Sequence, Transaction, TxOut, Txid, Weight, Witness,
-};
-use miniscript::{
-    descriptor::KeyMap,
-    psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier},
-};
-use rand_core::RngCore;
-
-mod changeset;
-pub mod coin_selection;
-pub mod error;
-pub mod export;
-mod params;
-mod persisted;
-pub mod signer;
-pub mod tx_builder;
-pub(crate) mod utils;
-
-use crate::collections::{BTreeMap, HashMap, HashSet};
-use crate::descriptor::{
-    check_wallet_descriptor, error::Error as DescriptorError, policy::BuildSatisfaction,
-    DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
-    Policy, XKeyUtils,
-};
-use crate::psbt::PsbtUtils;
-use crate::types::*;
-use crate::wallet::{
-    coin_selection::{DefaultCoinSelectionAlgorithm, Excess, InsufficientFunds},
-    error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError},
-    signer::{SignOptions, SignerError, SignerOrdering, SignersContainer, TransactionSigner},
-    tx_builder::{FeePolicy, TxBuilder, TxParams},
-    utils::{check_nsequence_rbf, After, Older, SecpCtx},
-};
-
-// re-exports
-pub use bdk_chain::Balance;
-pub use changeset::ChangeSet;
-pub use params::*;
-pub use persisted::*;
-pub use utils::IsDust;
-
-/// A Bitcoin wallet
-///
-/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
-/// Its main components are:
-///
-/// 1. output *descriptors* from which it can derive addresses.
-/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
-///
-/// The user is responsible for loading and writing wallet changes which are represented as
-/// [`ChangeSet`]s (see [`take_staged`]). Also see individual functions and example for instructions
-/// on when [`Wallet`] state needs to be persisted.
-///
-/// The `Wallet` descriptor (external) and change descriptor (internal) must not derive the same
-/// script pubkeys. See [`KeychainTxOutIndex::insert_descriptor()`] for more details.
-///
-/// [`signer`]: crate::signer
-/// [`take_staged`]: Wallet::take_staged
-#[derive(Debug)]
-pub struct Wallet {
-    signers: Arc<SignersContainer>,
-    change_signers: Arc<SignersContainer>,
-    chain: LocalChain,
-    indexed_graph: IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>,
-    stage: ChangeSet,
-    network: Network,
-    secp: SecpCtx,
-}
-
-/// An update to [`Wallet`].
-///
-/// It updates [`KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`LocalChain`] atomically.
-#[derive(Debug, Clone, Default)]
-pub struct Update {
-    /// Contains the last active derivation indices per keychain (`K`), which is used to update the
-    /// [`KeychainTxOutIndex`].
-    pub last_active_indices: BTreeMap<KeychainKind, u32>,
-
-    /// Update for the wallet's internal [`TxGraph`].
-    pub tx_update: TxUpdate<ConfirmationBlockTime>,
-
-    /// Update for the wallet's internal [`LocalChain`].
-    pub chain: Option<CheckPoint>,
-}
-
-impl From<FullScanResponse<KeychainKind>> for Update {
-    fn from(value: FullScanResponse<KeychainKind>) -> Self {
-        Self {
-            last_active_indices: value.last_active_indices,
-            tx_update: value.tx_update,
-            chain: value.chain_update,
-        }
-    }
-}
-
-impl From<SyncResponse> for Update {
-    fn from(value: SyncResponse) -> Self {
-        Self {
-            last_active_indices: BTreeMap::new(),
-            tx_update: value.tx_update,
-            chain: value.chain_update,
-        }
-    }
-}
-
-/// A derived address and the index it was found at.
-/// For convenience this automatically derefs to `Address`
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct AddressInfo {
-    /// Child index of this address
-    pub index: u32,
-    /// Address
-    pub address: Address,
-    /// Type of keychain
-    pub keychain: KeychainKind,
-}
-
-impl Deref for AddressInfo {
-    type Target = Address;
-
-    fn deref(&self) -> &Self::Target {
-        &self.address
-    }
-}
-
-impl fmt::Display for AddressInfo {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}", self.address)
-    }
-}
-
-/// The error type when loading a [`Wallet`] from a [`ChangeSet`].
-#[derive(Debug, PartialEq)]
-pub enum LoadError {
-    /// There was a problem with the passed-in descriptor(s).
-    Descriptor(crate::descriptor::DescriptorError),
-    /// Data loaded from persistence is missing network type.
-    MissingNetwork,
-    /// Data loaded from persistence is missing genesis hash.
-    MissingGenesis,
-    /// Data loaded from persistence is missing descriptor.
-    MissingDescriptor(KeychainKind),
-    /// Data loaded is unexpected.
-    Mismatch(LoadMismatch),
-}
-
-impl fmt::Display for LoadError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            LoadError::Descriptor(e) => e.fmt(f),
-            LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
-            LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
-            LoadError::MissingDescriptor(k) => {
-                write!(f, "loaded data is missing descriptor for keychain {k:?}")
-            }
-            LoadError::Mismatch(mismatch) => write!(f, "data mismatch: {mismatch:?}"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for LoadError {}
-
-/// Represents a mismatch with what is loaded and what is expected from [`LoadParams`].
-#[derive(Debug, PartialEq)]
-pub enum LoadMismatch {
-    /// Network does not match.
-    Network {
-        /// The network that is loaded.
-        loaded: Network,
-        /// The expected network.
-        expected: Network,
-    },
-    /// Genesis hash does not match.
-    Genesis {
-        /// The genesis hash that is loaded.
-        loaded: BlockHash,
-        /// The expected genesis hash.
-        expected: BlockHash,
-    },
-    /// Descriptor's [`DescriptorId`](bdk_chain::DescriptorId) does not match.
-    Descriptor {
-        /// Keychain identifying the descriptor.
-        keychain: KeychainKind,
-        /// The loaded descriptor.
-        loaded: Option<ExtendedDescriptor>,
-        /// The expected descriptor.
-        expected: Option<ExtendedDescriptor>,
-    },
-}
-
-impl From<LoadMismatch> for LoadError {
-    fn from(mismatch: LoadMismatch) -> Self {
-        Self::Mismatch(mismatch)
-    }
-}
-
-impl<E> From<LoadMismatch> for LoadWithPersistError<E> {
-    fn from(mismatch: LoadMismatch) -> Self {
-        Self::InvalidChangeSet(LoadError::Mismatch(mismatch))
-    }
-}
-
-/// An error that may occur when applying a block to [`Wallet`].
-#[derive(Debug)]
-pub enum ApplyBlockError {
-    /// Occurs when the update chain cannot connect with original chain.
-    CannotConnect(CannotConnectError),
-    /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
-    UnexpectedConnectedToHash {
-        /// Block hash of `connected_to`.
-        connected_to_hash: BlockHash,
-        /// Expected block hash of `connected_to`, as derived from `block`.
-        expected_hash: BlockHash,
-    },
-}
-
-impl fmt::Display for ApplyBlockError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            ApplyBlockError::CannotConnect(err) => err.fmt(f),
-            ApplyBlockError::UnexpectedConnectedToHash {
-                expected_hash: block_hash,
-                connected_to_hash: checkpoint_hash,
-            } => write!(
-                f,
-                "`connected_to` hash {} differs from the expected hash {} (which is derived from `block`)",
-                checkpoint_hash, block_hash
-            ),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for ApplyBlockError {}
-
-/// A `CanonicalTx` managed by a `Wallet`.
-pub type WalletTx<'a> = CanonicalTx<'a, Arc<Transaction>, ConfirmationBlockTime>;
-
-impl Wallet {
-    /// Build a new single descriptor [`Wallet`].
-    ///
-    /// If you have previously created a wallet, use [`load`](Self::load) instead.
-    ///
-    /// # Note
-    ///
-    /// Only use this method when creating a wallet designed to be used with a single
-    /// descriptor and keychain. Otherwise the recommended way to construct a new wallet is
-    /// by using [`Wallet::create`]. It's worth noting that not all features are available
-    /// with single descriptor wallets, for example setting a [`change_policy`] on [`TxBuilder`]
-    /// and related methods such as [`do_not_spend_change`]. This is because all payments are
-    /// received on the external keychain (including change), and without a change keychain
-    /// BDK lacks enough information to distinguish between change and outside payments.
-    ///
-    /// Additionally because this wallet has no internal (change) keychain, all methods that
-    /// require a [`KeychainKind`] as input, e.g. [`reveal_next_address`] should only be called
-    /// using the [`External`] variant. In most cases passing [`Internal`] is treated as the
-    /// equivalent of [`External`] but this behavior must not be relied on.
-    ///
-    /// # Example
-    ///
-    /// ```rust
-    /// # use bdk_wallet::Wallet;
-    /// # use bitcoin::Network;
-    /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-    /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
-    /// # let file_path = temp_dir.path().join("store.db");
-    /// // Create a wallet that is persisted to SQLite database.
-    /// use bdk_wallet::rusqlite::Connection;
-    /// let mut conn = Connection::open(file_path)?;
-    /// let wallet = Wallet::create_single(EXTERNAL_DESC)
-    ///     .network(Network::Testnet)
-    ///     .create_wallet(&mut conn)?;
-    /// # Ok::<_, anyhow::Error>(())
-    /// ```
-    /// [`change_policy`]: TxBuilder::change_policy
-    /// [`do_not_spend_change`]: TxBuilder::do_not_spend_change
-    /// [`External`]: KeychainKind::External
-    /// [`Internal`]: KeychainKind::Internal
-    /// [`reveal_next_address`]: Self::reveal_next_address
-    pub fn create_single<D>(descriptor: D) -> CreateParams
-    where
-        D: IntoWalletDescriptor + Send + Clone + 'static,
-    {
-        CreateParams::new_single(descriptor)
-    }
-
-    /// Build a new [`Wallet`].
-    ///
-    /// If you have previously created a wallet, use [`load`](Self::load) instead.
-    ///
-    /// # Synopsis
-    ///
-    /// ```rust
-    /// # use bdk_wallet::Wallet;
-    /// # use bitcoin::Network;
-    /// # fn main() -> anyhow::Result<()> {
-    /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-    /// # const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-    /// // Create a non-persisted wallet.
-    /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
-    ///     .network(Network::Testnet)
-    ///     .create_wallet_no_persist()?;
-    ///
-    /// // Create a wallet that is persisted to SQLite database.
-    /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
-    /// # let file_path = temp_dir.path().join("store.db");
-    /// use bdk_wallet::rusqlite::Connection;
-    /// let mut conn = Connection::open(file_path)?;
-    /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
-    ///     .network(Network::Testnet)
-    ///     .create_wallet(&mut conn)?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    pub fn create<D>(descriptor: D, change_descriptor: D) -> CreateParams
-    where
-        D: IntoWalletDescriptor + Send + Clone + 'static,
-    {
-        CreateParams::new(descriptor, change_descriptor)
-    }
-
-    /// Create a new [`Wallet`] with given `params`.
-    ///
-    /// Refer to [`Wallet::create`] for more.
-    pub fn create_with_params(params: CreateParams) -> Result<Self, DescriptorError> {
-        let secp = SecpCtx::new();
-        let network = params.network;
-        let genesis_hash = params
-            .genesis_hash
-            .unwrap_or(genesis_block(network).block_hash());
-        let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
-
-        let (descriptor, mut descriptor_keymap) = (params.descriptor)(&secp, network)?;
-        check_wallet_descriptor(&descriptor)?;
-        descriptor_keymap.extend(params.descriptor_keymap);
-
-        let signers = Arc::new(SignersContainer::build(
-            descriptor_keymap,
-            &descriptor,
-            &secp,
-        ));
-
-        let (change_descriptor, change_signers) = match params.change_descriptor {
-            Some(make_desc) => {
-                let (change_descriptor, mut internal_keymap) = make_desc(&secp, network)?;
-                check_wallet_descriptor(&change_descriptor)?;
-                internal_keymap.extend(params.change_descriptor_keymap);
-                let change_signers = Arc::new(SignersContainer::build(
-                    internal_keymap,
-                    &change_descriptor,
-                    &secp,
-                ));
-                (Some(change_descriptor), change_signers)
-            }
-            None => (None, Arc::new(SignersContainer::new())),
-        };
-
-        let index = create_indexer(descriptor, change_descriptor, params.lookahead)?;
-
-        let descriptor = index.get_descriptor(KeychainKind::External).cloned();
-        let change_descriptor = index.get_descriptor(KeychainKind::Internal).cloned();
-        let indexed_graph = IndexedTxGraph::new(index);
-        let indexed_graph_changeset = indexed_graph.initial_changeset();
-
-        let stage = ChangeSet {
-            descriptor,
-            change_descriptor,
-            local_chain: chain_changeset,
-            tx_graph: indexed_graph_changeset.tx_graph,
-            indexer: indexed_graph_changeset.indexer,
-            network: Some(network),
-        };
-
-        Ok(Wallet {
-            signers,
-            change_signers,
-            network,
-            chain,
-            indexed_graph,
-            stage,
-            secp,
-        })
-    }
-
-    /// Build [`Wallet`] by loading from persistence or [`ChangeSet`].
-    ///
-    /// Note that the descriptor secret keys are not persisted to the db. You can add
-    /// signers after-the-fact with [`Wallet::add_signer`] or [`Wallet::set_keymap`]. You
-    /// can also add keys when building the wallet by using [`LoadParams::keymap`]. Finally
-    /// you can check the wallet's descriptors are what you expect with [`LoadParams::descriptor`]
-    /// which will try to populate signers if [`LoadParams::extract_keys`] is enabled.
-    ///
-    /// # Synopsis
-    ///
-    /// ```rust,no_run
-    /// # use bdk_wallet::{Wallet, ChangeSet, KeychainKind};
-    /// # use bitcoin::{BlockHash, Network, hashes::Hash};
-    /// # fn main() -> anyhow::Result<()> {
-    /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-    /// # const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-    /// # let changeset = ChangeSet::default();
-    /// // Load a wallet from changeset (no persistence).
-    /// let wallet = Wallet::load()
-    ///     .load_wallet_no_persist(changeset)?
-    ///     .expect("must have data to load wallet");
-    ///
-    /// // Load a wallet that is persisted to SQLite database.
-    /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
-    /// # let file_path = temp_dir.path().join("store.db");
-    /// # let external_keymap = Default::default();
-    /// # let internal_keymap = Default::default();
-    /// # let genesis_hash = BlockHash::all_zeros();
-    /// let mut conn = bdk_wallet::rusqlite::Connection::open(file_path)?;
-    /// let mut wallet = Wallet::load()
-    ///     // check loaded descriptors matches these values and extract private keys
-    ///     .descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
-    ///     .descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
-    ///     .extract_keys()
-    ///     // you can also manually add private keys
-    ///     .keymap(KeychainKind::External, external_keymap)
-    ///     .keymap(KeychainKind::Internal, internal_keymap)
-    ///     // ensure loaded wallet's genesis hash matches this value
-    ///     .check_genesis_hash(genesis_hash)
-    ///     // set a lookahead for our indexer
-    ///     .lookahead(101)
-    ///     .load_wallet(&mut conn)?
-    ///     .expect("must have data to load wallet");
-    /// # Ok(())
-    /// # }
-    /// ```
-    pub fn load() -> LoadParams {
-        LoadParams::new()
-    }
-
-    /// Load [`Wallet`] from the given previously persisted [`ChangeSet`] and `params`.
-    ///
-    /// Refer to [`Wallet::load`] for more.
-    pub fn load_with_params(
-        changeset: ChangeSet,
-        params: LoadParams,
-    ) -> Result<Option<Self>, LoadError> {
-        if changeset.is_empty() {
-            return Ok(None);
-        }
-        let secp = Secp256k1::new();
-        let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
-        let chain = LocalChain::from_changeset(changeset.local_chain)
-            .map_err(|_| LoadError::MissingGenesis)?;
-
-        if let Some(exp_network) = params.check_network {
-            if network != exp_network {
-                return Err(LoadError::Mismatch(LoadMismatch::Network {
-                    loaded: network,
-                    expected: exp_network,
-                }));
-            }
-        }
-        if let Some(exp_genesis_hash) = params.check_genesis_hash {
-            if chain.genesis_hash() != exp_genesis_hash {
-                return Err(LoadError::Mismatch(LoadMismatch::Genesis {
-                    loaded: chain.genesis_hash(),
-                    expected: exp_genesis_hash,
-                }));
-            }
-        }
-
-        let descriptor = changeset
-            .descriptor
-            .ok_or(LoadError::MissingDescriptor(KeychainKind::External))?;
-        check_wallet_descriptor(&descriptor).map_err(LoadError::Descriptor)?;
-        let mut external_keymap = params.descriptor_keymap;
-
-        if let Some(expected) = params.check_descriptor {
-            if let Some(make_desc) = expected {
-                let (exp_desc, keymap) =
-                    make_desc(&secp, network).map_err(LoadError::Descriptor)?;
-                if descriptor.descriptor_id() != exp_desc.descriptor_id() {
-                    return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
-                        keychain: KeychainKind::External,
-                        loaded: Some(descriptor),
-                        expected: Some(exp_desc),
-                    }));
-                }
-                if params.extract_keys {
-                    external_keymap.extend(keymap);
-                }
-            } else {
-                return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
-                    keychain: KeychainKind::External,
-                    loaded: Some(descriptor),
-                    expected: None,
-                }));
-            }
-        }
-        let signers = Arc::new(SignersContainer::build(external_keymap, &descriptor, &secp));
-
-        let mut change_descriptor = None;
-        let mut internal_keymap = params.change_descriptor_keymap;
-
-        match (changeset.change_descriptor, params.check_change_descriptor) {
-            // empty signer
-            (None, None) => {}
-            (None, Some(expect)) => {
-                // expected desc but none loaded
-                if let Some(make_desc) = expect {
-                    let (exp_desc, _) = make_desc(&secp, network).map_err(LoadError::Descriptor)?;
-                    return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
-                        keychain: KeychainKind::Internal,
-                        loaded: None,
-                        expected: Some(exp_desc),
-                    }));
-                }
-            }
-            // nothing expected
-            (Some(desc), None) => {
-                check_wallet_descriptor(&desc).map_err(LoadError::Descriptor)?;
-                change_descriptor = Some(desc);
-            }
-            (Some(desc), Some(expect)) => match expect {
-                // expected none for existing
-                None => {
-                    return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
-                        keychain: KeychainKind::Internal,
-                        loaded: Some(desc),
-                        expected: None,
-                    }))
-                }
-                // parameters must match
-                Some(make_desc) => {
-                    check_wallet_descriptor(&desc).map_err(LoadError::Descriptor)?;
-                    let (exp_desc, keymap) =
-                        make_desc(&secp, network).map_err(LoadError::Descriptor)?;
-                    if desc.descriptor_id() != exp_desc.descriptor_id() {
-                        return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
-                            keychain: KeychainKind::Internal,
-                            loaded: Some(desc),
-                            expected: Some(exp_desc),
-                        }));
-                    }
-                    if params.extract_keys {
-                        internal_keymap.extend(keymap);
-                    }
-                    change_descriptor = Some(desc);
-                }
-            },
-        }
-
-        let change_signers = match change_descriptor {
-            Some(ref change_descriptor) => Arc::new(SignersContainer::build(
-                internal_keymap,
-                change_descriptor,
-                &secp,
-            )),
-            None => Arc::new(SignersContainer::new()),
-        };
-
-        let index = create_indexer(descriptor, change_descriptor, params.lookahead)
-            .map_err(LoadError::Descriptor)?;
-
-        let mut indexed_graph = IndexedTxGraph::new(index);
-        indexed_graph.apply_changeset(changeset.indexer.into());
-        indexed_graph.apply_changeset(changeset.tx_graph.into());
-
-        let stage = ChangeSet::default();
-
-        Ok(Some(Wallet {
-            signers,
-            change_signers,
-            chain,
-            indexed_graph,
-            stage,
-            network,
-            secp,
-        }))
-    }
-
-    /// Get the Bitcoin network the wallet is using.
-    pub fn network(&self) -> Network {
-        self.network
-    }
-
-    /// Iterator over all keychains in this wallet
-    pub fn keychains(&self) -> impl Iterator<Item = (KeychainKind, &ExtendedDescriptor)> {
-        self.indexed_graph.index.keychains()
-    }
-
-    /// Peek an address of the given `keychain` at `index` without revealing it.
-    ///
-    /// For non-wildcard descriptors this returns the same address at every provided index.
-    ///
-    /// # Panics
-    ///
-    /// This panics when the caller requests for an address of derivation index greater than the
-    /// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
-    pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
-        let keychain = self.map_keychain(keychain);
-        let mut spk_iter = self
-            .indexed_graph
-            .index
-            .unbounded_spk_iter(keychain)
-            .expect("keychain must exist");
-        if !spk_iter.descriptor().has_wildcard() {
-            index = 0;
-        }
-        let (index, spk) = spk_iter
-            .nth(index as usize)
-            .expect("derivation index is out of bounds");
-
-        AddressInfo {
-            index,
-            address: Address::from_script(&spk, self.network).expect("must have address form"),
-            keychain,
-        }
-    }
-
-    /// Attempt to reveal the next address of the given `keychain`.
-    ///
-    /// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
-    /// contain a wildcard or every address is already revealed up to the maximum derivation
-    /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
-    /// then the last revealed address will be returned.
-    ///
-    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
-    /// calls to this method before closing the wallet. For example:
-    ///
-    /// ```rust,no_run
-    /// # use bdk_wallet::{LoadParams, ChangeSet, KeychainKind};
-    /// use bdk_chain::rusqlite::Connection;
-    /// let mut conn = Connection::open_in_memory().expect("must open connection");
-    /// let mut wallet = LoadParams::new()
-    ///     .load_wallet(&mut conn)
-    ///     .expect("database is okay")
-    ///     .expect("database has data");
-    /// let next_address = wallet.reveal_next_address(KeychainKind::External);
-    /// wallet.persist(&mut conn).expect("write is okay");
-    ///
-    /// // Now it's safe to show the user their next address!
-    /// println!("Next address: {}", next_address.address);
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo {
-        let keychain = self.map_keychain(keychain);
-        let index = &mut self.indexed_graph.index;
-        let stage = &mut self.stage;
-
-        let ((index, spk), index_changeset) = index
-            .reveal_next_spk(keychain)
-            .expect("keychain must exist");
-
-        stage.merge(index_changeset.into());
-
-        AddressInfo {
-            index,
-            address: Address::from_script(spk.as_script(), self.network)
-                .expect("must have address form"),
-            keychain,
-        }
-    }
-
-    /// Reveal addresses up to and including the target `index` and return an iterator
-    /// of newly revealed addresses.
-    ///
-    /// If the target `index` is unreachable, we make a best effort to reveal up to the last
-    /// possible index. If all addresses up to the given `index` are already revealed, then
-    /// no new addresses are returned.
-    ///
-    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
-    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
-    pub fn reveal_addresses_to(
-        &mut self,
-        keychain: KeychainKind,
-        index: u32,
-    ) -> impl Iterator<Item = AddressInfo> + '_ {
-        let keychain = self.map_keychain(keychain);
-        let (spks, index_changeset) = self
-            .indexed_graph
-            .index
-            .reveal_to_target(keychain, index)
-            .expect("keychain must exist");
-
-        self.stage.merge(index_changeset.into());
-
-        spks.into_iter().map(move |(index, spk)| AddressInfo {
-            index,
-            address: Address::from_script(&spk, self.network).expect("must have address form"),
-            keychain,
-        })
-    }
-
-    /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
-    /// derivation index that hasn't been used in a transaction.
-    ///
-    /// This will attempt to reveal a new address if all previously revealed addresses have
-    /// been used, in which case the returned address will be the same as calling [`Wallet::reveal_next_address`].
-    ///
-    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
-    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
-    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo {
-        let keychain = self.map_keychain(keychain);
-        let index = &mut self.indexed_graph.index;
-
-        let ((index, spk), index_changeset) = index
-            .next_unused_spk(keychain)
-            .expect("keychain must exist");
-
-        self.stage
-            .merge(indexed_tx_graph::ChangeSet::from(index_changeset).into());
-
-        AddressInfo {
-            index,
-            address: Address::from_script(spk.as_script(), self.network)
-                .expect("must have address form"),
-            keychain,
-        }
-    }
-
-    /// Marks an address used of the given `keychain` at `index`.
-    ///
-    /// Returns whether the given index was present and then removed from the unused set.
-    pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
-        self.indexed_graph.index.mark_used(keychain, index)
-    }
-
-    /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted
-    /// back into the unused set.
-    ///
-    /// Since this is only a superficial marker, it will have no effect if the address at the given
-    /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the
-    /// derived spk.
-    ///
-    /// [`mark_used`]: Self::mark_used
-    pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
-        self.indexed_graph.index.unmark_used(keychain, index)
-    }
-
-    /// List addresses that are revealed but unused.
-    ///
-    /// Note if the returned iterator is empty you can reveal more addresses
-    /// by using [`reveal_next_address`](Self::reveal_next_address) or
-    /// [`reveal_addresses_to`](Self::reveal_addresses_to).
-    pub fn list_unused_addresses(
-        &self,
-        keychain: KeychainKind,
-    ) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
-        self.indexed_graph
-            .index
-            .unused_keychain_spks(self.map_keychain(keychain))
-            .map(move |(index, spk)| AddressInfo {
-                index,
-                address: Address::from_script(spk.as_script(), self.network)
-                    .expect("must have address form"),
-                keychain,
-            })
-    }
-
-    /// Return whether or not a `script` is part of this wallet (either internal or external)
-    pub fn is_mine(&self, script: ScriptBuf) -> bool {
-        self.indexed_graph.index.index_of_spk(script).is_some()
-    }
-
-    /// Finds how the wallet derived the script pubkey `spk`.
-    ///
-    /// Will only return `Some(_)` if the wallet has given out the spk.
-    pub fn derivation_of_spk(&self, spk: ScriptBuf) -> Option<(KeychainKind, u32)> {
-        self.indexed_graph.index.index_of_spk(spk).cloned()
-    }
-
-    /// Return the list of unspent outputs of this wallet
-    pub fn list_unspent(&self) -> impl Iterator<Item = LocalOutput> + '_ {
-        self.indexed_graph
-            .graph()
-            .filter_chain_unspents(
-                &self.chain,
-                self.chain.tip().block_id(),
-                self.indexed_graph.index.outpoints().iter().cloned(),
-            )
-            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
-    }
-
-    /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
-    ///
-    /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
-    pub fn list_output(&self) -> impl Iterator<Item = LocalOutput> + '_ {
-        self.indexed_graph
-            .graph()
-            .filter_chain_txouts(
-                &self.chain,
-                self.chain.tip().block_id(),
-                self.indexed_graph.index.outpoints().iter().cloned(),
-            )
-            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
-    }
-
-    /// Get all the checkpoints the wallet is currently storing indexed by height.
-    pub fn checkpoints(&self) -> CheckPointIter {
-        self.chain.iter_checkpoints()
-    }
-
-    /// Returns the latest checkpoint.
-    pub fn latest_checkpoint(&self) -> CheckPoint {
-        self.chain.tip()
-    }
-
-    /// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
-    ///
-    /// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
-    /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
-    /// electrum server) which will go through each address until it reaches a *stop gap*.
-    ///
-    /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
-    /// script pubkeys the wallet is storing internally).
-    pub fn all_unbounded_spk_iters(
-        &self,
-    ) -> BTreeMap<KeychainKind, impl Iterator<Item = Indexed<ScriptBuf>> + Clone> {
-        self.indexed_graph.index.all_unbounded_spk_iters()
-    }
-
-    /// Get an unbounded script pubkey iterator for the given `keychain`.
-    ///
-    /// See [`all_unbounded_spk_iters`] for more documentation
-    ///
-    /// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
-    pub fn unbounded_spk_iter(
-        &self,
-        keychain: KeychainKind,
-    ) -> impl Iterator<Item = Indexed<ScriptBuf>> + Clone {
-        self.indexed_graph
-            .index
-            .unbounded_spk_iter(self.map_keychain(keychain))
-            .expect("keychain must exist")
-    }
-
-    /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
-    /// wallet's database.
-    pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
-        let ((keychain, index), _) = self.indexed_graph.index.txout(op)?;
-        self.indexed_graph
-            .graph()
-            .filter_chain_unspents(
-                &self.chain,
-                self.chain.tip().block_id(),
-                core::iter::once(((), op)),
-            )
-            .map(|(_, full_txo)| new_local_utxo(keychain, index, full_txo))
-            .next()
-    }
-
-    /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
-    ///
-    /// This is used for providing a previous output's value so that we can use [`calculate_fee`]
-    /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
-    /// not be returned in [`list_unspent`] or [`list_output`].
-    ///
-    /// **WARNINGS:** This should only be used to add `TxOut`s that the wallet does not own. Only
-    /// insert `TxOut`s that you trust the values for!
-    ///
-    /// You must persist the changes resulting from one or more calls to this method if you need
-    /// the inserted `TxOut` data to be reloaded after closing the wallet.
-    /// See [`Wallet::reveal_next_address`].
-    ///
-    /// [`calculate_fee`]: Self::calculate_fee
-    /// [`calculate_fee_rate`]: Self::calculate_fee_rate
-    /// [`list_unspent`]: Self::list_unspent
-    /// [`list_output`]: Self::list_output
-    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
-        let additions = self.indexed_graph.insert_txout(outpoint, txout);
-        self.stage.merge(additions.into());
-    }
-
-    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
-    ///
-    /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
-    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
-    ///
-    /// Note `tx` does not have to be in the graph for this to work.
-    ///
-    /// # Examples
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Txid;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let txid:Txid = todo!();
-    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
-    /// let fee = wallet.calculate_fee(&tx).expect("fee");
-    /// ```
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Psbt;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let mut psbt: Psbt = todo!();
-    /// let tx = &psbt.clone().extract_tx().expect("tx");
-    /// let fee = wallet.calculate_fee(tx).expect("fee");
-    /// ```
-    /// [`insert_txout`]: Self::insert_txout
-    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
-        self.indexed_graph.graph().calculate_fee(tx)
-    }
-
-    /// Calculate the [`FeeRate`] for a given transaction.
-    ///
-    /// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you must
-    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
-    ///
-    /// Note `tx` does not have to be in the graph for this to work.
-    ///
-    /// # Examples
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Txid;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let txid:Txid = todo!();
-    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
-    /// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
-    /// ```
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Psbt;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let mut psbt: Psbt = todo!();
-    /// let tx = &psbt.clone().extract_tx().expect("tx");
-    /// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
-    /// ```
-    /// [`insert_txout`]: Self::insert_txout
-    pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
-        self.calculate_fee(tx).map(|fee| fee / tx.weight())
-    }
-
-    /// Compute the `tx`'s sent and received [`Amount`]s.
-    ///
-    /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
-    /// that spend from previous txouts tracked by this wallet. Received is the summation
-    /// of this tx's outputs that send to script pubkeys tracked by this wallet.
-    ///
-    /// # Examples
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Txid;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let txid:Txid = todo!();
-    /// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
-    /// let (sent, received) = wallet.sent_and_received(&tx);
-    /// ```
-    ///
-    /// ```rust, no_run
-    /// # use bitcoin::Psbt;
-    /// # use bdk_wallet::Wallet;
-    /// # let mut wallet: Wallet = todo!();
-    /// # let mut psbt: Psbt = todo!();
-    /// let tx = &psbt.clone().extract_tx().expect("tx");
-    /// let (sent, received) = wallet.sent_and_received(tx);
-    /// ```
-    pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) {
-        self.indexed_graph.index.sent_and_received(tx, ..)
-    }
-
-    /// Get a single transaction from the wallet as a [`WalletTx`] (if the transaction exists).
-    ///
-    /// `WalletTx` contains the full transaction alongside meta-data such as:
-    /// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
-    ///   in the best chain.
-    /// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
-    ///   confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
-    ///   confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
-    ///   the transaction was last seen in the mempool is provided.
-    ///
-    /// ```rust, no_run
-    /// use bdk_chain::Anchor;
-    /// use bdk_wallet::{chain::ChainPosition, Wallet};
-    /// # let wallet: Wallet = todo!();
-    /// # let my_txid: bitcoin::Txid = todo!();
-    ///
-    /// let wallet_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
-    ///
-    /// // get reference to full transaction
-    /// println!("my tx: {:#?}", wallet_tx.tx_node.tx);
-    ///
-    /// // list all transaction anchors
-    /// for anchor in wallet_tx.tx_node.anchors {
-    ///     println!(
-    ///         "tx is anchored by block of hash {}",
-    ///         anchor.anchor_block().hash
-    ///     );
-    /// }
-    ///
-    /// // get confirmation status of transaction
-    /// match wallet_tx.chain_position {
-    ///     ChainPosition::Confirmed {
-    ///         anchor,
-    ///         transitively: None,
-    ///     } => println!(
-    ///         "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
-    ///         anchor.block_id.height, anchor.block_id.height, anchor.block_id.hash,
-    ///     ),
-    ///     ChainPosition::Confirmed {
-    ///         anchor,
-    ///         transitively: Some(_),
-    ///     } => println!(
-    ///         "tx is an ancestor of a tx anchored in {}:{}",
-    ///         anchor.block_id.height, anchor.block_id.hash,
-    ///     ),
-    ///     ChainPosition::Unconfirmed { last_seen } => println!(
-    ///         "tx is last seen at {:?}, it is unconfirmed as it is not anchored in the best chain",
-    ///         last_seen,
-    ///     ),
-    /// }
-    /// ```
-    ///
-    /// [`Anchor`]: bdk_chain::Anchor
-    pub fn get_tx(&self, txid: Txid) -> Option<WalletTx> {
-        let graph = self.indexed_graph.graph();
-        graph
-            .list_canonical_txs(&self.chain, self.chain.tip().block_id())
-            .find(|tx| tx.tx_node.txid == txid)
-    }
-
-    /// Iterate over relevant and canonical transactions in the wallet.
-    ///
-    /// A transaction is relevant when it spends from or spends to at least one tracked output. A
-    /// transaction is canonical when it is confirmed in the best chain, or does not conflict
-    /// with any transaction confirmed in the best chain.
-    ///
-    /// To iterate over all transactions, including those that are irrelevant and not canonical, use
-    /// [`TxGraph::full_txs`].
-    ///
-    /// To iterate over all canonical transactions, including those that are irrelevant, use
-    /// [`TxGraph::list_canonical_txs`].
-    pub fn transactions(&self) -> impl Iterator<Item = WalletTx> + '_ {
-        let tx_graph = self.indexed_graph.graph();
-        let tx_index = &self.indexed_graph.index;
-        tx_graph
-            .list_canonical_txs(&self.chain, self.chain.tip().block_id())
-            .filter(|c_tx| tx_index.is_tx_relevant(&c_tx.tx_node.tx))
-    }
-
-    /// Array of relevant and canonical transactions in the wallet sorted with a comparator
-    /// function.
-    ///
-    /// This is a helper method equivalent to collecting the result of [`Wallet::transactions`]
-    /// into a [`Vec`] and then sorting it.
-    ///
-    /// # Example
-    ///
-    /// ```rust,no_run
-    /// # use bdk_wallet::{LoadParams, Wallet, WalletTx};
-    /// # let mut wallet:Wallet = todo!();
-    /// // Transactions by chain position: first unconfirmed then descending by confirmed height.
-    /// let sorted_txs: Vec<WalletTx> =
-    ///     wallet.transactions_sort_by(|tx1, tx2| tx2.chain_position.cmp(&tx1.chain_position));
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    pub fn transactions_sort_by<F>(&self, compare: F) -> Vec<WalletTx>
-    where
-        F: FnMut(&WalletTx, &WalletTx) -> Ordering,
-    {
-        let mut txs: Vec<WalletTx> = self.transactions().collect();
-        txs.sort_unstable_by(compare);
-        txs
-    }
-
-    /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
-    /// values.
-    pub fn balance(&self) -> Balance {
-        self.indexed_graph.graph().balance(
-            &self.chain,
-            self.chain.tip().block_id(),
-            self.indexed_graph.index.outpoints().iter().cloned(),
-            |&(k, _), _| k == KeychainKind::Internal,
-        )
-    }
-
-    /// Add an external signer
-    ///
-    /// See [the `signer` module](signer) for an example.
-    pub fn add_signer(
-        &mut self,
-        keychain: KeychainKind,
-        ordering: SignerOrdering,
-        signer: Arc<dyn TransactionSigner>,
-    ) {
-        let signers = match keychain {
-            KeychainKind::External => Arc::make_mut(&mut self.signers),
-            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
-        };
-
-        signers.add_external(signer.id(&self.secp), ordering, signer);
-    }
-
-    /// Set the keymap for a given keychain.
-    ///
-    /// Note this does nothing if the given keychain has no descriptor because we won't
-    /// know the context (segwit, taproot, etc) in which to create signatures.
-    pub fn set_keymap(&mut self, keychain: KeychainKind, keymap: KeyMap) {
-        let wallet_signers = match keychain {
-            KeychainKind::External => Arc::make_mut(&mut self.signers),
-            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
-        };
-        if let Some(descriptor) = self.indexed_graph.index.get_descriptor(keychain) {
-            *wallet_signers = SignersContainer::build(keymap, descriptor, &self.secp)
-        }
-    }
-
-    /// Set the keymap for each keychain.
-    pub fn set_keymaps(&mut self, keymaps: impl IntoIterator<Item = (KeychainKind, KeyMap)>) {
-        for (keychain, keymap) in keymaps {
-            self.set_keymap(keychain, keymap);
-        }
-    }
-
-    /// Get the signers
-    ///
-    /// ## Example
-    ///
-    /// ```
-    /// # use bdk_wallet::{Wallet, KeychainKind};
-    /// # use bdk_wallet::bitcoin::Network;
-    /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
-    /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
-    /// let wallet = Wallet::create(descriptor, change_descriptor)
-    ///     .network(Network::Testnet)
-    ///     .create_wallet_no_persist()?;
-    /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
-    ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
-    ///     println!("secret_key: {}", secret_key);
-    /// }
-    ///
-    /// Ok::<(), Box<dyn std::error::Error>>(())
-    /// ```
-    pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
-        match keychain {
-            KeychainKind::External => Arc::clone(&self.signers),
-            KeychainKind::Internal => Arc::clone(&self.change_signers),
-        }
-    }
-
-    /// Start building a transaction.
-    ///
-    /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
-    ///
-    /// ## Example
-    ///
-    /// ```
-    /// # use std::str::FromStr;
-    /// # use bitcoin::*;
-    /// # use bdk_wallet::*;
-    /// # use bdk_wallet::ChangeSet;
-    /// # use bdk_wallet::error::CreateTxError;
-    /// # use anyhow::Error;
-    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-    /// # let mut wallet = doctest_wallet!();
-    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
-    /// let psbt = {
-    ///    let mut builder =  wallet.build_tx();
-    ///    builder
-    ///        .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
-    ///    builder.finish()?
-    /// };
-    ///
-    /// // sign and broadcast ...
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    ///
-    /// [`TxBuilder`]: crate::TxBuilder
-    pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm> {
-        TxBuilder {
-            wallet: self,
-            params: TxParams::default(),
-            coin_selection: DefaultCoinSelectionAlgorithm::default(),
-        }
-    }
-
-    pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
-        &mut self,
-        coin_selection: Cs,
-        params: TxParams,
-        rng: &mut impl RngCore,
-    ) -> Result<Psbt, CreateTxError> {
-        let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
-        let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
-        let internal_descriptor = keychains.get(&KeychainKind::Internal);
-
-        let external_policy = external_descriptor
-            .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
-            .unwrap();
-        let internal_policy = internal_descriptor
-            .map(|desc| {
-                Ok::<_, CreateTxError>(
-                    desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
-                        .unwrap(),
-                )
-            })
-            .transpose()?;
-
-        // The policy allows spending external outputs, but it requires a policy path that hasn't been
-        // provided
-        if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
-            && external_policy.requires_path()
-            && params.external_policy_path.is_none()
-        {
-            return Err(CreateTxError::SpendingPolicyRequired(
-                KeychainKind::External,
-            ));
-        };
-        // Same for the internal_policy path
-        if let Some(internal_policy) = &internal_policy {
-            if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
-                && internal_policy.requires_path()
-                && params.internal_policy_path.is_none()
-            {
-                return Err(CreateTxError::SpendingPolicyRequired(
-                    KeychainKind::Internal,
-                ));
-            };
-        }
-
-        let external_requirements = external_policy.get_condition(
-            params
-                .external_policy_path
-                .as_ref()
-                .unwrap_or(&BTreeMap::new()),
-        )?;
-        let internal_requirements = internal_policy
-            .map(|policy| {
-                Ok::<_, CreateTxError>(
-                    policy.get_condition(
-                        params
-                            .internal_policy_path
-                            .as_ref()
-                            .unwrap_or(&BTreeMap::new()),
-                    )?,
-                )
-            })
-            .transpose()?;
-
-        let requirements =
-            external_requirements.merge(&internal_requirements.unwrap_or_default())?;
-
-        let version = match params.version {
-            Some(transaction::Version(0)) => return Err(CreateTxError::Version0),
-            Some(transaction::Version::ONE) if requirements.csv.is_some() => {
-                return Err(CreateTxError::Version1Csv)
-            }
-            Some(v) => v,
-            None => transaction::Version::TWO,
-        };
-
-        // We use a match here instead of a unwrap_or_else as it's way more readable :)
-        let current_height = match params.current_height {
-            // If they didn't tell us the current height, we assume it's the latest sync height.
-            None => {
-                let tip_height = self.chain.tip().height();
-                absolute::LockTime::from_height(tip_height).expect("invalid height")
-            }
-            Some(h) => h,
-        };
-
-        let lock_time = match params.locktime {
-            // When no nLockTime is specified, we try to prevent fee sniping, if possible
-            None => {
-                // Fee sniping can be partially prevented by setting the timelock
-                // to current_height. If we don't know the current_height,
-                // we default to 0.
-                let fee_sniping_height = current_height;
-
-                // We choose the biggest between the required nlocktime and the fee sniping
-                // height
-                match requirements.timelock {
-                    // No requirement, just use the fee_sniping_height
-                    None => fee_sniping_height,
-                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
-                    Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => {
-                        fee_sniping_height
-                    }
-                    // There's a time-based requirement or a block-based requirement greater
-                    // than the fee_sniping_height use that value
-                    Some(value) => value,
-                }
-            }
-            // Specific nLockTime required and we have no constraints, so just set to that value
-            Some(x) if requirements.timelock.is_none() => x,
-            // Specific nLockTime required and it's compatible with the constraints
-            Some(x)
-                if requirements.timelock.unwrap().is_same_unit(x)
-                    && x >= requirements.timelock.unwrap() =>
-            {
-                x
-            }
-            // Invalid nLockTime required
-            Some(x) => {
-                return Err(CreateTxError::LockTime {
-                    requested: x,
-                    required: requirements.timelock.unwrap(),
-                })
-            }
-        };
-
-        // nSequence value for inputs
-        // When not explicitly specified, defaults to 0xFFFFFFFD,
-        // meaning RBF signaling is enabled
-        let n_sequence = match (params.sequence, requirements.csv) {
-            // Enable RBF by default
-            (None, None) => Sequence::ENABLE_RBF_NO_LOCKTIME,
-            // None requested, use required
-            (None, Some(csv)) => csv,
-            // Requested sequence is incompatible with requirements
-            (Some(sequence), Some(csv)) if !check_nsequence_rbf(sequence, csv) => {
-                return Err(CreateTxError::RbfSequenceCsv { sequence, csv })
-            }
-            // Use requested nSequence value
-            (Some(sequence), _) => sequence,
-        };
-
-        let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
-            //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
-            FeePolicy::FeeAmount(fee) => {
-                if let Some(previous_fee) = params.bumping_fee {
-                    if fee < previous_fee.absolute {
-                        return Err(CreateTxError::FeeTooLow {
-                            required: previous_fee.absolute,
-                        });
-                    }
-                }
-                (FeeRate::ZERO, fee)
-            }
-            FeePolicy::FeeRate(rate) => {
-                if let Some(previous_fee) = params.bumping_fee {
-                    let required_feerate = FeeRate::from_sat_per_kwu(
-                        previous_fee.rate.to_sat_per_kwu()
-                            + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
-                    );
-                    if rate < required_feerate {
-                        return Err(CreateTxError::FeeRateTooLow {
-                            required: required_feerate,
-                        });
-                    }
-                }
-                (rate, Amount::ZERO)
-            }
-        };
-
-        let mut tx = Transaction {
-            version,
-            lock_time,
-            input: vec![],
-            output: vec![],
-        };
-
-        if params.manually_selected_only && params.utxos.is_empty() {
-            return Err(CreateTxError::NoUtxosSelected);
-        }
-
-        let mut outgoing = Amount::ZERO;
-        let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
-
-        for (index, (script_pubkey, value)) in recipients.enumerate() {
-            if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() {
-                return Err(CreateTxError::OutputBelowDustLimit(index));
-            }
-
-            let new_out = TxOut {
-                script_pubkey: script_pubkey.clone(),
-                value,
-            };
-
-            tx.output.push(new_out);
-
-            outgoing += value;
-        }
-
-        fee_amount += fee_rate * tx.weight();
-
-        let (required_utxos, optional_utxos) = {
-            // NOTE: manual selection overrides unspendable
-            let mut required: Vec<WeightedUtxo> = params.utxos.values().cloned().collect();
-            let optional = self.filter_utxos(&params, current_height.to_consensus_u32());
-
-            // if drain_wallet is true, all UTxOs are required
-            if params.drain_wallet {
-                required.extend(optional);
-                (required, vec![])
-            } else {
-                (required, optional)
-            }
-        };
-
-        // get drain script
-        let mut drain_index = Option::<(KeychainKind, u32)>::None;
-        let drain_script = match params.drain_to {
-            Some(ref drain_recipient) => drain_recipient.clone(),
-            None => {
-                let change_keychain = self.map_keychain(KeychainKind::Internal);
-                let (index, spk) = self
-                    .indexed_graph
-                    .index
-                    .unused_keychain_spks(change_keychain)
-                    .next()
-                    .unwrap_or_else(|| {
-                        let (next_index, _) = self
-                            .indexed_graph
-                            .index
-                            .next_index(change_keychain)
-                            .expect("keychain must exist");
-                        let spk = self
-                            .peek_address(change_keychain, next_index)
-                            .script_pubkey();
-                        (next_index, spk)
-                    });
-                drain_index = Some((change_keychain, index));
-                spk
-            }
-        };
-
-        let coin_selection = coin_selection
-            .coin_select(
-                required_utxos,
-                optional_utxos,
-                fee_rate,
-                outgoing + fee_amount,
-                &drain_script,
-                rng,
-            )
-            .map_err(CreateTxError::CoinSelection)?;
-
-        let excess = &coin_selection.excess;
-        tx.input = coin_selection
-            .selected
-            .iter()
-            .map(|u| bitcoin::TxIn {
-                previous_output: u.outpoint(),
-                script_sig: ScriptBuf::default(),
-                sequence: u.sequence().unwrap_or(n_sequence),
-                witness: Witness::new(),
-            })
-            .collect();
-
-        if tx.output.is_empty() {
-            // Uh oh, our transaction has no outputs.
-            // We allow this when:
-            // - We have a drain_to address and the utxos we must spend (this happens,
-            // for example, when we RBF)
-            // - We have a drain_to address and drain_wallet set
-            // Otherwise, we don't know who we should send the funds to, and how much
-            // we should send!
-            if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
-                if let Excess::NoChange {
-                    dust_threshold,
-                    remaining_amount,
-                    change_fee,
-                } = excess
-                {
-                    return Err(CreateTxError::CoinSelection(InsufficientFunds {
-                        needed: *dust_threshold,
-                        available: remaining_amount
-                            .checked_sub(*change_fee)
-                            .unwrap_or_default(),
-                    }));
-                }
-            } else {
-                return Err(CreateTxError::NoRecipients);
-            }
-        }
-
-        // if there's change, create and add a change output
-        if let Excess::Change { amount, .. } = excess {
-            // create drain output
-            let drain_output = TxOut {
-                value: *amount,
-                script_pubkey: drain_script,
-            };
-
-            // TODO: We should pay attention when adding a new output: this might increase
-            // the length of the "number of vouts" parameter by 2 bytes, potentially making
-            // our feerate too low
-            tx.output.push(drain_output);
-        }
-
-        // sort input/outputs according to the chosen algorithm
-        params.ordering.sort_tx_with_aux_rand(&mut tx, rng);
-
-        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
-
-        // recording changes to the change keychain
-        if let (Excess::Change { .. }, Some((keychain, index))) = (excess, drain_index) {
-            let (_, index_changeset) = self
-                .indexed_graph
-                .index
-                .reveal_to_target(keychain, index)
-                .expect("must not be None");
-            self.stage.merge(index_changeset.into());
-            self.mark_used(keychain, index);
-        }
-
-        Ok(psbt)
-    }
-
-    /// Bump the fee of a transaction previously created with this wallet.
-    ///
-    /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
-    /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
-    /// pre-populated with the inputs and outputs of the original transaction.
-    ///
-    /// ## Example
-    ///
-    /// ```no_run
-    /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
-    /// # use std::str::FromStr;
-    /// # use bitcoin::*;
-    /// # use bdk_wallet::*;
-    /// # use bdk_wallet::ChangeSet;
-    /// # use bdk_wallet::error::CreateTxError;
-    /// # use anyhow::Error;
-    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-    /// # let mut wallet = doctest_wallet!();
-    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
-    /// let mut psbt = {
-    ///     let mut builder = wallet.build_tx();
-    ///     builder
-    ///         .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
-    ///     builder.finish()?
-    /// };
-    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
-    /// let tx = psbt.clone().extract_tx().expect("tx");
-    /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
-    /// let mut psbt =  {
-    ///     let mut builder = wallet.build_fee_bump(tx.compute_txid())?;
-    ///     builder
-    ///         .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
-    ///     builder.finish()?
-    /// };
-    ///
-    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
-    /// let fee_bumped_tx = psbt.extract_tx();
-    /// // broadcast fee_bumped_tx to replace original
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    // TODO: support for merging multiple transactions while bumping the fees
-    pub fn build_fee_bump(
-        &mut self,
-        txid: Txid,
-    ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
-        let graph = self.indexed_graph.graph();
-        let txout_index = &self.indexed_graph.index;
-        let chain_tip = self.chain.tip().block_id();
-        let chain_positions = graph
-            .list_canonical_txs(&self.chain, chain_tip)
-            .map(|canon_tx| (canon_tx.tx_node.txid, canon_tx.chain_position))
-            .collect::<HashMap<Txid, _>>();
-
-        let mut tx = graph
-            .get_tx(txid)
-            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
-            .as_ref()
-            .clone();
-
-        if chain_positions
-            .get(&txid)
-            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
-            .is_confirmed()
-        {
-            return Err(BuildFeeBumpError::TransactionConfirmed(txid));
-        }
-
-        if !tx
-            .input
-            .iter()
-            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
-        {
-            return Err(BuildFeeBumpError::IrreplaceableTransaction(
-                tx.compute_txid(),
-            ));
-        }
-
-        let fee = self
-            .calculate_fee(&tx)
-            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
-        let fee_rate = self
-            .calculate_fee_rate(&tx)
-            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
-
-        // remove the inputs from the tx and process them
-        let utxos = tx
-            .input
-            .drain(..)
-            .map(|txin| -> Result<_, BuildFeeBumpError> {
-                graph
-                    // Get previous transaction
-                    .get_tx(txin.previous_output.txid)
-                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))
-                    // Get chain position
-                    .and_then(|prev_tx| {
-                        chain_positions
-                            .get(&txin.previous_output.txid)
-                            .cloned()
-                            .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))
-                            .map(|chain_position| (prev_tx, chain_position))
-                    })
-                    .map(|(prev_tx, chain_position)| {
-                        let txout = prev_tx.output[txin.previous_output.vout as usize].clone();
-                        match txout_index.index_of_spk(txout.script_pubkey.clone()) {
-                            Some(&(keychain, derivation_index)) => (
-                                txin.previous_output,
-                                WeightedUtxo {
-                                    satisfaction_weight: self
-                                        .public_descriptor(keychain)
-                                        .max_weight_to_satisfy()
-                                        .unwrap(),
-                                    utxo: Utxo::Local(LocalOutput {
-                                        outpoint: txin.previous_output,
-                                        txout: txout.clone(),
-                                        keychain,
-                                        is_spent: true,
-                                        derivation_index,
-                                        chain_position,
-                                    }),
-                                },
-                            ),
-                            None => {
-                                let satisfaction_weight = Weight::from_wu_usize(
-                                    serialize(&txin.script_sig).len() * 4
-                                        + serialize(&txin.witness).len(),
-                                );
-
-                                (
-                                    txin.previous_output,
-                                    WeightedUtxo {
-                                        utxo: Utxo::Foreign {
-                                            outpoint: txin.previous_output,
-                                            sequence: txin.sequence,
-                                            psbt_input: Box::new(psbt::Input {
-                                                witness_utxo: txout
-                                                    .script_pubkey
-                                                    .witness_version()
-                                                    .map(|_| txout.clone()),
-                                                non_witness_utxo: Some(prev_tx.as_ref().clone()),
-                                                ..Default::default()
-                                            }),
-                                        },
-                                        satisfaction_weight,
-                                    },
-                                )
-                            }
-                        }
-                    })
-            })
-            .collect::<Result<HashMap<OutPoint, WeightedUtxo>, BuildFeeBumpError>>()?;
-
-        if tx.output.len() > 1 {
-            let mut change_index = None;
-            for (index, txout) in tx.output.iter().enumerate() {
-                let change_keychain = self.map_keychain(KeychainKind::Internal);
-                match txout_index.index_of_spk(txout.script_pubkey.clone()) {
-                    Some((keychain, _)) if *keychain == change_keychain => {
-                        change_index = Some(index)
-                    }
-                    _ => {}
-                }
-            }
-
-            if let Some(change_index) = change_index {
-                tx.output.remove(change_index);
-            }
-        }
-
-        let params = TxParams {
-            // TODO: figure out what rbf option should be?
-            version: Some(tx.version),
-            recipients: tx
-                .output
-                .into_iter()
-                .map(|txout| (txout.script_pubkey, txout.value))
-                .collect(),
-            utxos,
-            bumping_fee: Some(tx_builder::PreviousFee {
-                absolute: fee,
-                rate: fee_rate,
-            }),
-            ..Default::default()
-        };
-
-        Ok(TxBuilder {
-            wallet: self,
-            params,
-            coin_selection: DefaultCoinSelectionAlgorithm::default(),
-        })
-    }
-
-    /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
-    /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
-    ///
-    /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
-    /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
-    /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
-    /// in this library will.
-    ///
-    /// ## Example
-    ///
-    /// ```
-    /// # use std::str::FromStr;
-    /// # use bitcoin::*;
-    /// # use bdk_wallet::*;
-    /// # use bdk_wallet::ChangeSet;
-    /// # use bdk_wallet::error::CreateTxError;
-    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
-    /// # let mut wallet = doctest_wallet!();
-    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
-    /// let mut psbt = {
-    ///     let mut builder = wallet.build_tx();
-    ///     builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
-    ///     builder.finish()?
-    /// };
-    /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    /// assert!(finalized, "we should have signed all the inputs");
-    /// # Ok::<(),anyhow::Error>(())
-    pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, SignerError> {
-        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
-        // to derive our keys
-        self.update_psbt_with_descriptor(psbt)
-            .map_err(SignerError::MiniscriptPsbt)?;
-
-        // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
-        // has the `non_witness_utxo`
-        if !sign_options.trust_witness_utxo
-            && psbt
-                .inputs
-                .iter()
-                .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
-                .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
-                .any(|i| i.non_witness_utxo.is_none())
-        {
-            return Err(SignerError::MissingNonWitnessUtxo);
-        }
-
-        // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
-        // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
-        if !sign_options.allow_all_sighashes
-            && !psbt.inputs.iter().all(|i| {
-                i.sighash_type.is_none()
-                    || i.sighash_type == Some(EcdsaSighashType::All.into())
-                    || i.sighash_type == Some(TapSighashType::All.into())
-                    || i.sighash_type == Some(TapSighashType::Default.into())
-            })
-        {
-            return Err(SignerError::NonStandardSighash);
-        }
-
-        for signer in self
-            .signers
-            .signers()
-            .iter()
-            .chain(self.change_signers.signers().iter())
-        {
-            signer.sign_transaction(psbt, &sign_options, &self.secp)?;
-        }
-
-        // attempt to finalize
-        if sign_options.try_finalize {
-            self.finalize_psbt(psbt, sign_options)
-        } else {
-            Ok(false)
-        }
-    }
-
-    /// Return the spending policies for the wallet's descriptor
-    pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, DescriptorError> {
-        let signers = match keychain {
-            KeychainKind::External => &self.signers,
-            KeychainKind::Internal => &self.change_signers,
-        };
-
-        self.public_descriptor(keychain).extract_policy(
-            signers,
-            BuildSatisfaction::None,
-            &self.secp,
-        )
-    }
-
-    /// Returns the descriptor used to create addresses for a particular `keychain`.
-    ///
-    /// It's the "public" version of the wallet's descriptor, meaning a new descriptor that has
-    /// the same structure but with the all secret keys replaced by their corresponding public key.
-    /// This can be used to build a watch-only version of a wallet.
-    pub fn public_descriptor(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
-        self.indexed_graph
-            .index
-            .get_descriptor(self.map_keychain(keychain))
-            .expect("keychain must exist")
-    }
-
-    /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
-    /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
-    /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer),
-    /// and [BIP371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki)
-    /// for further information.
-    ///
-    /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
-    ///
-    /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
-    pub fn finalize_psbt(
-        &self,
-        psbt: &mut Psbt,
-        sign_options: SignOptions,
-    ) -> Result<bool, SignerError> {
-        let tx = &psbt.unsigned_tx;
-        let chain_tip = self.chain.tip().block_id();
-        let prev_txids = tx
-            .input
-            .iter()
-            .map(|txin| txin.previous_output.txid)
-            .collect::<HashSet<Txid>>();
-        let confirmation_heights = self
-            .indexed_graph
-            .graph()
-            .list_canonical_txs(&self.chain, chain_tip)
-            .filter(|canon_tx| prev_txids.contains(&canon_tx.tx_node.txid))
-            // This is for a small performance gain. Although `.filter` filters out excess txs, it
-            // will still consume the internal `CanonicalIter` entirely. Having a `.take` here
-            // allows us to stop further unnecessary canonicalization.
-            .take(prev_txids.len())
-            .map(|canon_tx| {
-                let txid = canon_tx.tx_node.txid;
-                match canon_tx.chain_position {
-                    ChainPosition::Confirmed { anchor, .. } => (txid, anchor.block_id.height),
-                    ChainPosition::Unconfirmed { .. } => (txid, u32::MAX),
-                }
-            })
-            .collect::<HashMap<Txid, u32>>();
-
-        let mut finished = true;
-
-        for (n, input) in tx.input.iter().enumerate() {
-            let psbt_input = &psbt
-                .inputs
-                .get(n)
-                .ok_or(SignerError::InputIndexOutOfRange)?;
-            if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
-                continue;
-            }
-            let confirmation_height = confirmation_heights
-                .get(&input.previous_output.txid)
-                .copied();
-            let current_height = sign_options
-                .assume_height
-                .unwrap_or_else(|| self.chain.tip().height());
-
-            // - Try to derive the descriptor by looking at the txout. If it's in our database, we
-            //   know exactly which `keychain` to use, and which derivation index it is
-            // - If that fails, try to derive it by looking at the psbt input: the complete logic
-            //   is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
-            //   `redeem_script` and `witness_script` to determine the right derivation
-            // - If that also fails, it will try it on the internal descriptor, if present
-            let desc = psbt
-                .get_utxo_for(n)
-                .and_then(|txout| self.get_descriptor_for_txout(&txout))
-                .or_else(|| {
-                    self.indexed_graph.index.keychains().find_map(|(_, desc)| {
-                        desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
-                    })
-                });
-
-            match desc {
-                Some(desc) => {
-                    let mut tmp_input = bitcoin::TxIn::default();
-                    match desc.satisfy(
-                        &mut tmp_input,
-                        (
-                            PsbtInputSatisfier::new(psbt, n),
-                            After::new(Some(current_height), false),
-                            Older::new(Some(current_height), confirmation_height, false),
-                        ),
-                    ) {
-                        Ok(_) => {
-                            // Set the UTXO fields, final script_sig and witness
-                            // and clear everything else.
-                            let psbt_input = psbt
-                                .inputs
-                                .get_mut(n)
-                                .ok_or(SignerError::InputIndexOutOfRange)?;
-                            let original = mem::take(psbt_input);
-                            psbt_input.non_witness_utxo = original.non_witness_utxo;
-                            psbt_input.witness_utxo = original.witness_utxo;
-                            if !tmp_input.script_sig.is_empty() {
-                                psbt_input.final_script_sig = Some(tmp_input.script_sig);
-                            }
-                            if !tmp_input.witness.is_empty() {
-                                psbt_input.final_script_witness = Some(tmp_input.witness);
-                            }
-                        }
-                        Err(_) => finished = false,
-                    }
-                }
-                None => finished = false,
-            }
-        }
-
-        // Clear derivation paths from outputs
-        if finished {
-            for output in &mut psbt.outputs {
-                output.bip32_derivation.clear();
-                output.tap_key_origins.clear();
-            }
-        }
-
-        Ok(finished)
-    }
-
-    /// Return the secp256k1 context used for all signing operations
-    pub fn secp_ctx(&self) -> &SecpCtx {
-        &self.secp
-    }
-
-    /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
-    /// Otherwise, it will return the index of the highest address it has derived.
-    pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
-        self.indexed_graph.index.last_revealed_index(keychain)
-    }
-
-    /// The index of the next address that you would get if you were to ask the wallet for a new address
-    pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
-        self.indexed_graph
-            .index
-            .next_index(self.map_keychain(keychain))
-            .expect("keychain must exist")
-            .0
-    }
-
-    /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
-    ///
-    /// This frees up the change address used when creating the tx for use in future transactions.
-    // TODO: Make this free up reserved utxos when that's implemented
-    pub fn cancel_tx(&mut self, tx: &Transaction) {
-        let txout_index = &mut self.indexed_graph.index;
-        for txout in &tx.output {
-            if let Some((keychain, index)) = txout_index.index_of_spk(txout.script_pubkey.clone()) {
-                // NOTE: unmark_used will **not** make something unused if it has actually been used
-                // by a tx in the tracker. It only removes the superficial marking.
-                txout_index.unmark_used(*keychain, *index);
-            }
-        }
-    }
-
-    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
-        let &(keychain, child) = self
-            .indexed_graph
-            .index
-            .index_of_spk(txout.script_pubkey.clone())?;
-        let descriptor = self.public_descriptor(keychain);
-        descriptor.at_derivation_index(child).ok()
-    }
-
-    /// Given the options returns the list of utxos that must be used to form the
-    /// transaction and any further that may be used if needed.
-    fn filter_utxos(&self, params: &TxParams, current_height: u32) -> Vec<WeightedUtxo> {
-        if params.manually_selected_only {
-            vec![]
-        // only process optional UTxOs if manually_selected_only is false
-        } else {
-            self.indexed_graph
-                .graph()
-                // get all unspent UTxOs from wallet
-                // NOTE: the UTxOs returned by the following method already belong to wallet as the
-                // call chain uses get_tx_node infallibly
-                .filter_chain_unspents(
-                    &self.chain,
-                    self.chain.tip().block_id(),
-                    self.indexed_graph.index.outpoints().iter().cloned(),
-                )
-                // only create LocalOutput if UTxO is mature
-                .filter_map(move |((k, i), full_txo)| {
-                    full_txo
-                        .is_mature(current_height)
-                        .then(|| new_local_utxo(k, i, full_txo))
-                })
-                // only process UTxOs not selected manually, they will be considered later in the chain
-                // NOTE: this avoid UTxOs in both required and optional list
-                .filter(|may_spend| !params.utxos.contains_key(&may_spend.outpoint))
-                // only add to optional UTxOs those which satisfy the change policy if we reuse change
-                .filter(|local_output| {
-                    self.keychains().count() == 1
-                        || params.change_policy.is_satisfied_by(local_output)
-                })
-                // only add to optional UTxOs those marked as spendable
-                .filter(|local_output| !params.unspendable.contains(&local_output.outpoint))
-                // if bumping fees only add to optional UTxOs those confirmed
-                .filter(|local_output| {
-                    params.bumping_fee.is_none() || local_output.chain_position.is_confirmed()
-                })
-                .map(|utxo| WeightedUtxo {
-                    satisfaction_weight: self
-                        .public_descriptor(utxo.keychain)
-                        .max_weight_to_satisfy()
-                        .unwrap(),
-                    utxo: Utxo::Local(utxo),
-                })
-                .collect()
-        }
-    }
-
-    fn complete_transaction(
-        &self,
-        tx: Transaction,
-        selected: Vec<Utxo>,
-        params: TxParams,
-    ) -> Result<Psbt, CreateTxError> {
-        let mut psbt = Psbt::from_unsigned_tx(tx)?;
-
-        if params.add_global_xpubs {
-            let all_xpubs = self
-                .keychains()
-                .flat_map(|(_, desc)| desc.get_extended_keys())
-                .collect::<Vec<_>>();
-
-            for xpub in all_xpubs {
-                let origin = match xpub.origin {
-                    Some(origin) => origin,
-                    None if xpub.xkey.depth == 0 => {
-                        (xpub.root_fingerprint(&self.secp), vec![].into())
-                    }
-                    _ => return Err(CreateTxError::MissingKeyOrigin(xpub.xkey.to_string())),
-                };
-
-                psbt.xpub.insert(xpub.xkey, origin);
-            }
-        }
-
-        let mut lookup_output = selected
-            .into_iter()
-            .map(|utxo| (utxo.outpoint(), utxo))
-            .collect::<HashMap<_, _>>();
-
-        // add metadata for the inputs
-        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
-            let utxo = match lookup_output.remove(&input.previous_output) {
-                Some(utxo) => utxo,
-                None => continue,
-            };
-
-            match utxo {
-                Utxo::Local(utxo) => {
-                    *psbt_input =
-                        match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
-                            Ok(psbt_input) => psbt_input,
-                            Err(e) => match e {
-                                CreateTxError::UnknownUtxo => psbt::Input {
-                                    sighash_type: params.sighash,
-                                    ..psbt::Input::default()
-                                },
-                                _ => return Err(e),
-                            },
-                        }
-                }
-                Utxo::Foreign {
-                    outpoint,
-                    psbt_input: foreign_psbt_input,
-                    ..
-                } => {
-                    let is_taproot = foreign_psbt_input
-                        .witness_utxo
-                        .as_ref()
-                        .map(|txout| txout.script_pubkey.is_p2tr())
-                        .unwrap_or(false);
-                    if !is_taproot
-                        && !params.only_witness_utxo
-                        && foreign_psbt_input.non_witness_utxo.is_none()
-                    {
-                        return Err(CreateTxError::MissingNonWitnessUtxo(outpoint));
-                    }
-                    *psbt_input = *foreign_psbt_input;
-                }
-            }
-        }
-
-        self.update_psbt_with_descriptor(&mut psbt)?;
-
-        Ok(psbt)
-    }
-
-    /// get the corresponding PSBT Input for a LocalUtxo
-    pub fn get_psbt_input(
-        &self,
-        utxo: LocalOutput,
-        sighash_type: Option<psbt::PsbtSighashType>,
-        only_witness_utxo: bool,
-    ) -> Result<psbt::Input, CreateTxError> {
-        // Try to find the prev_script in our db to figure out if this is internal or external,
-        // and the derivation index
-        let &(keychain, child) = self
-            .indexed_graph
-            .index
-            .index_of_spk(utxo.txout.script_pubkey)
-            .ok_or(CreateTxError::UnknownUtxo)?;
-
-        let mut psbt_input = psbt::Input {
-            sighash_type,
-            ..psbt::Input::default()
-        };
-
-        let desc = self.public_descriptor(keychain);
-        let derived_descriptor = desc
-            .at_derivation_index(child)
-            .expect("child can't be hardened");
-
-        psbt_input
-            .update_with_descriptor_unchecked(&derived_descriptor)
-            .map_err(MiniscriptPsbtError::Conversion)?;
-
-        let prev_output = utxo.outpoint;
-        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
-            if desc.is_witness() || desc.is_taproot() {
-                psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
-            }
-            if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
-                psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
-            }
-        }
-        Ok(psbt_input)
-    }
-
-    fn update_psbt_with_descriptor(&self, psbt: &mut Psbt) -> Result<(), MiniscriptPsbtError> {
-        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
-        // the input utxos and outputs
-        let utxos = (0..psbt.inputs.len())
-            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
-            .chain(
-                psbt.unsigned_tx
-                    .output
-                    .iter()
-                    .enumerate()
-                    .map(|(i, out)| (false, i, out.clone())),
-            )
-            .collect::<Vec<_>>();
-
-        // Try to figure out the keychain and derivation for every input and output
-        for (is_input, index, out) in utxos.into_iter() {
-            if let Some(&(keychain, child)) =
-                self.indexed_graph.index.index_of_spk(out.script_pubkey)
-            {
-                let desc = self.public_descriptor(keychain);
-                let desc = desc
-                    .at_derivation_index(child)
-                    .expect("child can't be hardened");
-
-                if is_input {
-                    psbt.update_input_with_descriptor(index, &desc)
-                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
-                } else {
-                    psbt.update_output_with_descriptor(index, &desc)
-                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
-                }
-            }
-        }
-
-        Ok(())
-    }
-
-    /// Return the checksum of the public descriptor associated to `keychain`
-    ///
-    /// Internally calls [`Self::public_descriptor`] to fetch the right descriptor
-    pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
-        self.public_descriptor(keychain)
-            .to_string()
-            .split_once('#')
-            .unwrap()
-            .1
-            .to_string()
-    }
-
-    /// Applies an update to the wallet and stages the changes (but does not persist them).
-    ///
-    /// Usually you create an `update` by interacting with some blockchain data source and inserting
-    /// transactions related to your wallet into it.
-    ///
-    /// After applying updates you should persist the staged wallet changes. For an example of how
-    /// to persist staged wallet changes see [`Wallet::reveal_next_address`].
-    #[cfg(feature = "std")]
-    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
-    pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
-        use std::time::*;
-        let now = SystemTime::now()
-            .duration_since(UNIX_EPOCH)
-            .expect("time now must surpass epoch anchor");
-        self.apply_update_at(update, now.as_secs())
-    }
-
-    /// Applies an `update` alongside a `seen_at` timestamp and stages the changes.
-    ///
-    /// `seen_at` represents when the update is seen (in unix seconds). It is used to determine the
-    /// `last_seen`s for all transactions in the update which have no corresponding anchor(s). The
-    /// `last_seen` value is used internally to determine precedence of conflicting unconfirmed
-    /// transactions (where the transaction with the lower `last_seen` value is omitted from the
-    /// canonical history).
-    ///
-    /// Use [`apply_update`](Wallet::apply_update) to have the `seen_at` value automatically set to
-    /// the current time.
-    pub fn apply_update_at(
-        &mut self,
-        update: impl Into<Update>,
-        seen_at: u64,
-    ) -> Result<(), CannotConnectError> {
-        let update = update.into();
-        let mut changeset = match update.chain {
-            Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
-            None => ChangeSet::default(),
-        };
-
-        let index_changeset = self
-            .indexed_graph
-            .index
-            .reveal_to_target_multi(&update.last_active_indices);
-        changeset.merge(index_changeset.into());
-        changeset.merge(
-            self.indexed_graph
-                .apply_update_at(update.tx_update, Some(seen_at))
-                .into(),
-        );
-        self.stage.merge(changeset);
-        Ok(())
-    }
-
-    /// Get a reference of the staged [`ChangeSet`] that is yet to be committed (if any).
-    pub fn staged(&self) -> Option<&ChangeSet> {
-        if self.stage.is_empty() {
-            None
-        } else {
-            Some(&self.stage)
-        }
-    }
-
-    /// Get a mutable reference of the staged [`ChangeSet`] that is yet to be committed (if any).
-    pub fn staged_mut(&mut self) -> Option<&mut ChangeSet> {
-        if self.stage.is_empty() {
-            None
-        } else {
-            Some(&mut self.stage)
-        }
-    }
-
-    /// Take the staged [`ChangeSet`] to be persisted now (if any).
-    pub fn take_staged(&mut self) -> Option<ChangeSet> {
-        self.stage.take()
-    }
-
-    /// Get a reference to the inner [`TxGraph`].
-    pub fn tx_graph(&self) -> &TxGraph<ConfirmationBlockTime> {
-        self.indexed_graph.graph()
-    }
-
-    /// Get a reference to the inner [`KeychainTxOutIndex`].
-    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
-        &self.indexed_graph.index
-    }
-
-    /// Get a reference to the inner [`LocalChain`].
-    pub fn local_chain(&self) -> &LocalChain {
-        &self.chain
-    }
-
-    /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
-    /// `prev_blockhash` of the block's header.
-    ///
-    /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
-    /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
-    ///
-    /// [`apply_block_connected_to`]: Self::apply_block_connected_to
-    pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
-        let connected_to = match height.checked_sub(1) {
-            Some(prev_height) => BlockId {
-                height: prev_height,
-                hash: block.header.prev_blockhash,
-            },
-            None => BlockId {
-                height,
-                hash: block.block_hash(),
-            },
-        };
-        self.apply_block_connected_to(block, height, connected_to)
-            .map_err(|err| match err {
-                ApplyHeaderError::InconsistentBlocks => {
-                    unreachable!("connected_to is derived from the block so must be consistent")
-                }
-                ApplyHeaderError::CannotConnect(err) => err,
-            })
-    }
-
-    /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
-    /// block to the internal chain.
-    ///
-    /// The `connected_to` parameter informs the wallet how this block connects to the internal
-    /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
-    /// internal [`TxGraph`].
-    ///
-    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
-    /// if you need the inserted block data to be reloaded after closing the wallet.
-    /// See [`Wallet::reveal_next_address`].
-    pub fn apply_block_connected_to(
-        &mut self,
-        block: &Block,
-        height: u32,
-        connected_to: BlockId,
-    ) -> Result<(), ApplyHeaderError> {
-        let mut changeset = ChangeSet::default();
-        changeset.merge(
-            self.chain
-                .apply_header_connected_to(&block.header, height, connected_to)?
-                .into(),
-        );
-        changeset.merge(
-            self.indexed_graph
-                .apply_block_relevant(block, height)
-                .into(),
-        );
-        self.stage.merge(changeset);
-        Ok(())
-    }
-
-    /// Apply relevant unconfirmed transactions to the wallet.
-    ///
-    /// Transactions that are not relevant are filtered out.
-    ///
-    /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
-    /// when the transaction was last seen in the mempool. This is used for conflict resolution
-    /// when there is conflicting unconfirmed transactions. The transaction with the later
-    /// `last_seen` is prioritized.
-    ///
-    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
-    /// if you need the applied unconfirmed transactions to be reloaded after closing the wallet.
-    /// See [`Wallet::reveal_next_address`].
-    pub fn apply_unconfirmed_txs<T: Into<Arc<Transaction>>>(
-        &mut self,
-        unconfirmed_txs: impl IntoIterator<Item = (T, u64)>,
-    ) {
-        let indexed_graph_changeset = self
-            .indexed_graph
-            .batch_insert_relevant_unconfirmed(unconfirmed_txs);
-        self.stage.merge(indexed_graph_changeset.into());
-    }
-
-    /// Used internally to ensure that all methods requiring a [`KeychainKind`] will use a
-    /// keychain with an associated descriptor. For example in case the wallet was created
-    /// with only one keychain, passing [`KeychainKind::Internal`] here will instead return
-    /// [`KeychainKind::External`].
-    fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
-        if self.keychains().count() == 1 {
-            KeychainKind::External
-        } else {
-            keychain
-        }
-    }
-}
-
-/// Methods to construct sync/full-scan requests for spk-based chain sources.
-impl Wallet {
-    /// Create a partial [`SyncRequest`] for this wallet for all revealed spks.
-    ///
-    /// This is the first step when performing a spk-based wallet partial sync, the returned
-    /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
-    /// start a blockchain sync with a spk based blockchain client.
-    pub fn start_sync_with_revealed_spks(&self) -> SyncRequestBuilder<(KeychainKind, u32)> {
-        use bdk_chain::keychain_txout::SyncRequestBuilderExt;
-        SyncRequest::builder()
-            .chain_tip(self.chain.tip())
-            .revealed_spks_from_indexer(&self.indexed_graph.index, ..)
-    }
-
-    /// Create a [`FullScanRequest] for this wallet.
-    ///
-    /// This is the first step when performing a spk-based wallet full scan, the returned
-    /// [`FullScanRequest] collects iterators for the wallet's keychain script pub keys needed to
-    /// start a blockchain full scan with a spk based blockchain client.
-    ///
-    /// This operation is generally only used when importing or restoring a previously used wallet
-    /// in which the list of used scripts is not known.
-    pub fn start_full_scan(&self) -> FullScanRequestBuilder<KeychainKind> {
-        use bdk_chain::keychain_txout::FullScanRequestBuilderExt;
-        FullScanRequest::builder()
-            .chain_tip(self.chain.tip())
-            .spks_from_indexer(&self.indexed_graph.index)
-    }
-}
-
-impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime>> for Wallet {
-    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime> {
-        self.indexed_graph.graph()
-    }
-}
-
-/// Deterministically generate a unique name given the descriptors defining the wallet
-///
-/// Compatible with [`wallet_name_from_descriptor`]
-pub fn wallet_name_from_descriptor<T>(
-    descriptor: T,
-    change_descriptor: Option<T>,
-    network: Network,
-    secp: &SecpCtx,
-) -> Result<String, DescriptorError>
-where
-    T: IntoWalletDescriptor,
-{
-    //TODO check descriptors contains only public keys
-    let descriptor = descriptor
-        .into_wallet_descriptor(secp, network)?
-        .0
-        .to_string();
-    let mut wallet_name = descriptor.split_once('#').unwrap().1.to_string();
-    if let Some(change_descriptor) = change_descriptor {
-        let change_descriptor = change_descriptor
-            .into_wallet_descriptor(secp, network)?
-            .0
-            .to_string();
-        wallet_name.push_str(change_descriptor.split_once('#').unwrap().1);
-    }
-
-    Ok(wallet_name)
-}
-
-fn new_local_utxo(
-    keychain: KeychainKind,
-    derivation_index: u32,
-    full_txo: FullTxOut<ConfirmationBlockTime>,
-) -> LocalOutput {
-    LocalOutput {
-        outpoint: full_txo.outpoint,
-        txout: full_txo.txout,
-        is_spent: full_txo.spent_by.is_some(),
-        chain_position: full_txo.chain_position,
-        keychain,
-        derivation_index,
-    }
-}
-
-fn create_indexer(
-    descriptor: ExtendedDescriptor,
-    change_descriptor: Option<ExtendedDescriptor>,
-    lookahead: u32,
-) -> Result<KeychainTxOutIndex<KeychainKind>, DescriptorError> {
-    let mut indexer = KeychainTxOutIndex::<KeychainKind>::new(lookahead);
-
-    // let (descriptor, keymap) = descriptor;
-    // let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
-    assert!(indexer
-        .insert_descriptor(KeychainKind::External, descriptor)
-        .expect("first descriptor introduced must succeed"));
-
-    // let (descriptor, keymap) = change_descriptor;
-    // let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
-    if let Some(change_descriptor) = change_descriptor {
-        assert!(indexer
-            .insert_descriptor(KeychainKind::Internal, change_descriptor)
-            .map_err(|e| {
-                use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
-                match e {
-                    InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
-                        crate::descriptor::error::Error::ExternalAndInternalAreTheSame
-                    }
-                    InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
-                        unreachable!("this is the first time we're assigning internal")
-                    }
-                }
-            })?);
-    }
-
-    Ok(indexer)
-}
-
-/// Transforms a [`FeeRate`] to `f64` with unit as sat/vb.
-#[macro_export]
-#[doc(hidden)]
-macro_rules! floating_rate {
-    ($rate:expr) => {{
-        use $crate::bitcoin::constants::WITNESS_SCALE_FACTOR;
-        // sat_kwu / 250.0 -> sat_vb
-        $rate.to_sat_per_kwu() as f64 / ((1000 / WITNESS_SCALE_FACTOR) as f64)
-    }};
-}
-
-#[macro_export]
-#[doc(hidden)]
-/// Macro for getting a wallet for use in a doctest
-macro_rules! doctest_wallet {
-    () => {{
-        use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
-        use $crate::chain::{ConfirmationBlockTime, BlockId, TxGraph, tx_graph};
-        use $crate::{Update, KeychainKind, Wallet};
-        use $crate::test_utils::*;
-        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
-        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
-
-        let mut wallet = Wallet::create(descriptor, change_descriptor)
-            .network(Network::Regtest)
-            .create_wallet_no_persist()
-            .unwrap();
-        let address = wallet.peek_address(KeychainKind::External, 0).address;
-        let tx = Transaction {
-            version: transaction::Version::TWO,
-            lock_time: absolute::LockTime::ZERO,
-            input: vec![],
-            output: vec![TxOut {
-                value: Amount::from_sat(500_000),
-                script_pubkey: address.script_pubkey(),
-            }],
-        };
-        let txid = tx.compute_txid();
-        let block_id = BlockId { height: 500, hash: BlockHash::all_zeros() };
-        insert_checkpoint(&mut wallet, block_id);
-        insert_checkpoint(&mut wallet, BlockId { height: 1_000, hash: BlockHash::all_zeros() });
-        insert_tx(&mut wallet, tx);
-        let anchor = ConfirmationBlockTime {
-            confirmation_time: 50_000,
-            block_id,
-        };
-        insert_anchor(&mut wallet, txid, anchor);
-        wallet
-    }}
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use crate::test_utils::get_test_tr_single_sig_xprv_and_change_desc;
-    use crate::test_utils::insert_tx;
-
-    #[test]
-    fn not_duplicated_utxos_across_optional_and_required() {
-        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-
-        // create new wallet
-        let mut wallet = Wallet::create(external_desc, internal_desc)
-            .network(Network::Testnet)
-            .create_wallet_no_persist()
-            .unwrap();
-
-        let two_output_tx = Transaction {
-            input: vec![],
-            output: vec![
-                TxOut {
-                    script_pubkey: wallet
-                        .next_unused_address(KeychainKind::External)
-                        .script_pubkey(),
-                    value: Amount::from_sat(25_000),
-                },
-                TxOut {
-                    script_pubkey: wallet
-                        .next_unused_address(KeychainKind::External)
-                        .script_pubkey(),
-                    value: Amount::from_sat(75_000),
-                },
-            ],
-            version: transaction::Version::non_standard(0),
-            lock_time: absolute::LockTime::ZERO,
-        };
-
-        let txid = two_output_tx.compute_txid();
-        insert_tx(&mut wallet, two_output_tx);
-
-        let mut params = TxParams::default();
-        let output = wallet.get_utxo(OutPoint { txid, vout: 0 }).unwrap();
-        params.utxos.insert(
-            output.outpoint,
-            WeightedUtxo {
-                satisfaction_weight: wallet
-                    .public_descriptor(output.keychain)
-                    .max_weight_to_satisfy()
-                    .unwrap(),
-                utxo: Utxo::Local(output),
-            },
-        );
-        // enforce selection of first output in transaction
-        let received = wallet.filter_utxos(&params, wallet.latest_checkpoint().block_id().height);
-        // notice expected doesn't include the first output from two_output_tx as it should be
-        // filtered out
-        let expected = vec![wallet
-            .get_utxo(OutPoint { txid, vout: 1 })
-            .map(|utxo| WeightedUtxo {
-                satisfaction_weight: wallet
-                    .public_descriptor(utxo.keychain)
-                    .max_weight_to_satisfy()
-                    .unwrap(),
-                utxo: Utxo::Local(utxo),
-            })
-            .unwrap()];
-
-        assert_eq!(expected, received);
-    }
-}
diff --git a/crates/wallet/src/wallet/params.rs b/crates/wallet/src/wallet/params.rs
deleted file mode 100644 (file)
index 7cf3bdd..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-use alloc::boxed::Box;
-use bdk_chain::keychain_txout::DEFAULT_LOOKAHEAD;
-use bitcoin::{BlockHash, Network};
-use miniscript::descriptor::KeyMap;
-
-use crate::{
-    descriptor::{DescriptorError, ExtendedDescriptor, IntoWalletDescriptor},
-    utils::SecpCtx,
-    AsyncWalletPersister, CreateWithPersistError, KeychainKind, LoadWithPersistError, Wallet,
-    WalletPersister,
-};
-
-use super::{ChangeSet, LoadError, PersistedWallet};
-
-/// This atrocity is to avoid having type parameters on [`CreateParams`] and [`LoadParams`].
-///
-/// The better option would be to do `Box<dyn IntoWalletDescriptor>`, but we cannot due to Rust's
-/// [object safety rules](https://doc.rust-lang.org/reference/items/traits.html#object-safety).
-type DescriptorToExtract = Box<
-    dyn FnOnce(&SecpCtx, Network) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>
-        + Send
-        + 'static,
->;
-
-fn make_descriptor_to_extract<D>(descriptor: D) -> DescriptorToExtract
-where
-    D: IntoWalletDescriptor + Send + 'static,
-{
-    Box::new(|secp, network| descriptor.into_wallet_descriptor(secp, network))
-}
-
-/// Parameters for [`Wallet::create`] or [`PersistedWallet::create`].
-#[must_use]
-pub struct CreateParams {
-    pub(crate) descriptor: DescriptorToExtract,
-    pub(crate) descriptor_keymap: KeyMap,
-    pub(crate) change_descriptor: Option<DescriptorToExtract>,
-    pub(crate) change_descriptor_keymap: KeyMap,
-    pub(crate) network: Network,
-    pub(crate) genesis_hash: Option<BlockHash>,
-    pub(crate) lookahead: u32,
-}
-
-impl CreateParams {
-    /// Construct parameters with provided `descriptor`.
-    ///
-    /// Default values:
-    /// * `change_descriptor` = `None`
-    /// * `network` = [`Network::Bitcoin`]
-    /// * `genesis_hash` = `None`
-    /// * `lookahead` = [`DEFAULT_LOOKAHEAD`]
-    ///
-    /// Use this method only when building a wallet with a single descriptor. See
-    /// also [`Wallet::create_single`].
-    pub fn new_single<D: IntoWalletDescriptor + Send + 'static>(descriptor: D) -> Self {
-        Self {
-            descriptor: make_descriptor_to_extract(descriptor),
-            descriptor_keymap: KeyMap::default(),
-            change_descriptor: None,
-            change_descriptor_keymap: KeyMap::default(),
-            network: Network::Bitcoin,
-            genesis_hash: None,
-            lookahead: DEFAULT_LOOKAHEAD,
-        }
-    }
-
-    /// Construct parameters with provided `descriptor` and `change_descriptor`.
-    ///
-    /// Default values:
-    /// * `network` = [`Network::Bitcoin`]
-    /// * `genesis_hash` = `None`
-    /// * `lookahead` = [`DEFAULT_LOOKAHEAD`]
-    pub fn new<D: IntoWalletDescriptor + Send + 'static>(
-        descriptor: D,
-        change_descriptor: D,
-    ) -> Self {
-        Self {
-            descriptor: make_descriptor_to_extract(descriptor),
-            descriptor_keymap: KeyMap::default(),
-            change_descriptor: Some(make_descriptor_to_extract(change_descriptor)),
-            change_descriptor_keymap: KeyMap::default(),
-            network: Network::Bitcoin,
-            genesis_hash: None,
-            lookahead: DEFAULT_LOOKAHEAD,
-        }
-    }
-
-    /// Extend the given `keychain`'s `keymap`.
-    pub fn keymap(mut self, keychain: KeychainKind, keymap: KeyMap) -> Self {
-        match keychain {
-            KeychainKind::External => &mut self.descriptor_keymap,
-            KeychainKind::Internal => &mut self.change_descriptor_keymap,
-        }
-        .extend(keymap);
-        self
-    }
-
-    /// Set `network`.
-    pub fn network(mut self, network: Network) -> Self {
-        self.network = network;
-        self
-    }
-
-    /// Use a custom `genesis_hash`.
-    pub fn genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
-        self.genesis_hash = Some(genesis_hash);
-        self
-    }
-
-    /// Use a custom `lookahead` value.
-    ///
-    /// The `lookahead` defines a number of script pubkeys to derive over and above the last
-    /// revealed index. Without a lookahead the indexer will miss outputs you own when processing
-    /// transactions whose output script pubkeys lie beyond the last revealed index. In most cases
-    /// the default value [`DEFAULT_LOOKAHEAD`] is sufficient.
-    pub fn lookahead(mut self, lookahead: u32) -> Self {
-        self.lookahead = lookahead;
-        self
-    }
-
-    /// Create [`PersistedWallet`] with the given [`WalletPersister`].
-    pub fn create_wallet<P>(
-        self,
-        persister: &mut P,
-    ) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
-    where
-        P: WalletPersister,
-    {
-        PersistedWallet::create(persister, self)
-    }
-
-    /// Create [`PersistedWallet`] with the given [`AsyncWalletPersister`].
-    pub async fn create_wallet_async<P>(
-        self,
-        persister: &mut P,
-    ) -> Result<PersistedWallet<P>, CreateWithPersistError<P::Error>>
-    where
-        P: AsyncWalletPersister,
-    {
-        PersistedWallet::create_async(persister, self).await
-    }
-
-    /// Create [`Wallet`] without persistence.
-    pub fn create_wallet_no_persist(self) -> Result<Wallet, DescriptorError> {
-        Wallet::create_with_params(self)
-    }
-}
-
-/// Parameters for [`Wallet::load`] or [`PersistedWallet::load`].
-#[must_use]
-pub struct LoadParams {
-    pub(crate) descriptor_keymap: KeyMap,
-    pub(crate) change_descriptor_keymap: KeyMap,
-    pub(crate) lookahead: u32,
-    pub(crate) check_network: Option<Network>,
-    pub(crate) check_genesis_hash: Option<BlockHash>,
-    pub(crate) check_descriptor: Option<Option<DescriptorToExtract>>,
-    pub(crate) check_change_descriptor: Option<Option<DescriptorToExtract>>,
-    pub(crate) extract_keys: bool,
-}
-
-impl LoadParams {
-    /// Construct parameters with default values.
-    ///
-    /// Default values: `lookahead` = [`DEFAULT_LOOKAHEAD`]
-    pub fn new() -> Self {
-        Self {
-            descriptor_keymap: KeyMap::default(),
-            change_descriptor_keymap: KeyMap::default(),
-            lookahead: DEFAULT_LOOKAHEAD,
-            check_network: None,
-            check_genesis_hash: None,
-            check_descriptor: None,
-            check_change_descriptor: None,
-            extract_keys: false,
-        }
-    }
-
-    /// Extend the given `keychain`'s `keymap`.
-    pub fn keymap(mut self, keychain: KeychainKind, keymap: KeyMap) -> Self {
-        match keychain {
-            KeychainKind::External => &mut self.descriptor_keymap,
-            KeychainKind::Internal => &mut self.change_descriptor_keymap,
-        }
-        .extend(keymap);
-        self
-    }
-
-    /// Checks the `expected_descriptor` matches exactly what is loaded for `keychain`.
-    ///
-    /// # Note
-    ///
-    /// You must also specify [`extract_keys`](Self::extract_keys) if you wish to add a signer
-    /// for an expected descriptor containing secrets.
-    pub fn descriptor<D>(mut self, keychain: KeychainKind, expected_descriptor: Option<D>) -> Self
-    where
-        D: IntoWalletDescriptor + Send + 'static,
-    {
-        let expected = expected_descriptor.map(|d| make_descriptor_to_extract(d));
-        match keychain {
-            KeychainKind::External => self.check_descriptor = Some(expected),
-            KeychainKind::Internal => self.check_change_descriptor = Some(expected),
-        }
-        self
-    }
-
-    /// Checks that the given network matches the one loaded from persistence.
-    pub fn check_network(mut self, network: Network) -> Self {
-        self.check_network = Some(network);
-        self
-    }
-
-    /// Checks that the given `genesis_hash` matches the one loaded from persistence.
-    pub fn check_genesis_hash(mut self, genesis_hash: BlockHash) -> Self {
-        self.check_genesis_hash = Some(genesis_hash);
-        self
-    }
-
-    /// Use a custom `lookahead` value.
-    ///
-    /// The `lookahead` defines a number of script pubkeys to derive over and above the last
-    /// revealed index. Without a lookahead the indexer will miss outputs you own when processing
-    /// transactions whose output script pubkeys lie beyond the last revealed index. In most cases
-    /// the default value [`DEFAULT_LOOKAHEAD`] is sufficient.
-    pub fn lookahead(mut self, lookahead: u32) -> Self {
-        self.lookahead = lookahead;
-        self
-    }
-
-    /// Whether to try extracting private keys from the *provided descriptors* upon loading.
-    /// See also [`LoadParams::descriptor`].
-    pub fn extract_keys(mut self) -> Self {
-        self.extract_keys = true;
-        self
-    }
-
-    /// Load [`PersistedWallet`] with the given [`WalletPersister`].
-    pub fn load_wallet<P>(
-        self,
-        persister: &mut P,
-    ) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
-    where
-        P: WalletPersister,
-    {
-        PersistedWallet::load(persister, self)
-    }
-
-    /// Load [`PersistedWallet`] with the given [`AsyncWalletPersister`].
-    pub async fn load_wallet_async<P>(
-        self,
-        persister: &mut P,
-    ) -> Result<Option<PersistedWallet<P>>, LoadWithPersistError<P::Error>>
-    where
-        P: AsyncWalletPersister,
-    {
-        PersistedWallet::load_async(persister, self).await
-    }
-
-    /// Load [`Wallet`] without persistence.
-    pub fn load_wallet_no_persist(self, changeset: ChangeSet) -> Result<Option<Wallet>, LoadError> {
-        Wallet::load_with_params(changeset, self)
-    }
-}
-
-impl Default for LoadParams {
-    fn default() -> Self {
-        Self::new()
-    }
-}
diff --git a/crates/wallet/src/wallet/persisted.rs b/crates/wallet/src/wallet/persisted.rs
deleted file mode 100644 (file)
index 28a6ec7..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-use core::{
-    fmt,
-    future::Future,
-    marker::PhantomData,
-    ops::{Deref, DerefMut},
-    pin::Pin,
-};
-
-use alloc::boxed::Box;
-use chain::Merge;
-
-use crate::{descriptor::DescriptorError, ChangeSet, CreateParams, LoadParams, Wallet};
-
-/// Trait that persists [`PersistedWallet`].
-///
-/// For an async version, use [`AsyncWalletPersister`].
-///
-/// Associated functions of this trait should not be called directly, and the trait is designed so
-/// that associated functions are hard to find (since they are not methods!). [`WalletPersister`] is
-/// used by [`PersistedWallet`] (a light wrapper around [`Wallet`]) which enforces some level of
-/// safety. Refer to [`PersistedWallet`] for more about the safety checks.
-pub trait WalletPersister {
-    /// Error type of the persister.
-    type Error;
-
-    /// Initialize the `persister` and load all data.
-    ///
-    /// This is called by [`PersistedWallet::create`] and [`PersistedWallet::load`] to ensure
-    /// the [`WalletPersister`] is initialized and returns all data in the `persister`.
-    ///
-    /// # Implementation Details
-    ///
-    /// The database schema of the `persister` (if any), should be initialized and migrated here.
-    ///
-    /// The implementation must return all data currently stored in the `persister`. If there is no
-    /// data, return an empty changeset (using [`ChangeSet::default()`]).
-    ///
-    /// Error should only occur on database failure. Multiple calls to `initialize` should not
-    /// error. Calling `initialize` inbetween calls to `persist` should not error.
-    ///
-    /// Calling [`persist`] before the `persister` is `initialize`d may error. However, some
-    /// persister implementations may NOT require initialization at all (and not error).
-    ///
-    /// [`persist`]: WalletPersister::persist
-    fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error>;
-
-    /// Persist the given `changeset` to the `persister`.
-    ///
-    /// This method can fail if the `persister` is not [`initialize`]d.
-    ///
-    /// [`initialize`]: WalletPersister::initialize
-    fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error>;
-}
-
-type FutureResult<'a, T, E> = Pin<Box<dyn Future<Output = Result<T, E>> + Send + 'a>>;
-
-/// Async trait that persists [`PersistedWallet`].
-///
-/// For a blocking version, use [`WalletPersister`].
-///
-/// Associated functions of this trait should not be called directly, and the trait is designed so
-/// that associated functions are hard to find (since they are not methods!). [`AsyncWalletPersister`] is
-/// used by [`PersistedWallet`] (a light wrapper around [`Wallet`]) which enforces some level of
-/// safety. Refer to [`PersistedWallet`] for more about the safety checks.
-pub trait AsyncWalletPersister {
-    /// Error type of the persister.
-    type Error;
-
-    /// Initialize the `persister` and load all data.
-    ///
-    /// This is called by [`PersistedWallet::create_async`] and [`PersistedWallet::load_async`] to
-    /// ensure the [`AsyncWalletPersister`] is initialized and returns all data in the `persister`.
-    ///
-    /// # Implementation Details
-    ///
-    /// The database schema of the `persister` (if any), should be initialized and migrated here.
-    ///
-    /// The implementation must return all data currently stored in the `persister`. If there is no
-    /// data, return an empty changeset (using [`ChangeSet::default()`]).
-    ///
-    /// Error should only occur on database failure. Multiple calls to `initialize` should not
-    /// error. Calling `initialize` inbetween calls to `persist` should not error.
-    ///
-    /// Calling [`persist`] before the `persister` is `initialize`d may error. However, some
-    /// persister implementations may NOT require initialization at all (and not error).
-    ///
-    /// [`persist`]: AsyncWalletPersister::persist
-    fn initialize<'a>(persister: &'a mut Self) -> FutureResult<'a, ChangeSet, Self::Error>
-    where
-        Self: 'a;
-
-    /// Persist the given `changeset` to the `persister`.
-    ///
-    /// This method can fail if the `persister` is not [`initialize`]d.
-    ///
-    /// [`initialize`]: AsyncWalletPersister::initialize
-    fn persist<'a>(
-        persister: &'a mut Self,
-        changeset: &'a ChangeSet,
-    ) -> FutureResult<'a, (), Self::Error>
-    where
-        Self: 'a;
-}
-
-/// Represents a persisted wallet which persists into type `P`.
-///
-/// This is a light wrapper around [`Wallet`] that enforces some level of safety-checking when used
-/// with a [`WalletPersister`] or [`AsyncWalletPersister`] implementation. Safety checks assume that
-/// [`WalletPersister`] and/or [`AsyncWalletPersister`] are implemented correctly.
-///
-/// Checks include:
-///
-/// * Ensure the persister is initialized before data is persisted.
-/// * Ensure there were no previously persisted wallet data before creating a fresh wallet and
-///     persisting it.
-/// * Only clear the staged changes of [`Wallet`] after persisting succeeds.
-/// * Ensure the wallet is persisted to the same `P` type as when created/loaded. Note that this is
-///     not completely fool-proof as you can have multiple instances of the same `P` type that are
-///     connected to different databases.
-#[derive(Debug)]
-pub struct PersistedWallet<P> {
-    inner: Wallet,
-    _marker: PhantomData<fn(&mut P)>,
-}
-
-impl<P> Deref for PersistedWallet<P> {
-    type Target = Wallet;
-
-    fn deref(&self) -> &Self::Target {
-        &self.inner
-    }
-}
-
-impl<P> DerefMut for PersistedWallet<P> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.inner
-    }
-}
-
-/// Methods when `P` is a [`WalletPersister`].
-impl<P: WalletPersister> PersistedWallet<P> {
-    /// Create a new [`PersistedWallet`] with the given `persister` and `params`.
-    pub fn create(
-        persister: &mut P,
-        params: CreateParams,
-    ) -> Result<Self, CreateWithPersistError<P::Error>> {
-        let existing = P::initialize(persister).map_err(CreateWithPersistError::Persist)?;
-        if !existing.is_empty() {
-            return Err(CreateWithPersistError::DataAlreadyExists(existing));
-        }
-        let mut inner =
-            Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
-        if let Some(changeset) = inner.take_staged() {
-            P::persist(persister, &changeset).map_err(CreateWithPersistError::Persist)?;
-        }
-        Ok(Self {
-            inner,
-            _marker: PhantomData,
-        })
-    }
-
-    /// Load a previously [`PersistedWallet`] from the given `persister` and `params`.
-    pub fn load(
-        persister: &mut P,
-        params: LoadParams,
-    ) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
-        let changeset = P::initialize(persister).map_err(LoadWithPersistError::Persist)?;
-        Wallet::load_with_params(changeset, params)
-            .map(|opt| {
-                opt.map(|inner| PersistedWallet {
-                    inner,
-                    _marker: PhantomData,
-                })
-            })
-            .map_err(LoadWithPersistError::InvalidChangeSet)
-    }
-
-    /// Persist staged changes of wallet into `persister`.
-    ///
-    /// Returns whether any new changes were persisted.
-    ///
-    /// If the `persister` errors, the staged changes will not be cleared.
-    pub fn persist(&mut self, persister: &mut P) -> Result<bool, P::Error> {
-        match self.inner.staged_mut() {
-            Some(stage) => {
-                P::persist(persister, &*stage)?;
-                let _ = stage.take();
-                Ok(true)
-            }
-            None => Ok(false),
-        }
-    }
-}
-
-/// Methods when `P` is an [`AsyncWalletPersister`].
-impl<P: AsyncWalletPersister> PersistedWallet<P> {
-    /// Create a new [`PersistedWallet`] with the given async `persister` and `params`.
-    pub async fn create_async(
-        persister: &mut P,
-        params: CreateParams,
-    ) -> Result<Self, CreateWithPersistError<P::Error>> {
-        let existing = P::initialize(persister)
-            .await
-            .map_err(CreateWithPersistError::Persist)?;
-        if !existing.is_empty() {
-            return Err(CreateWithPersistError::DataAlreadyExists(existing));
-        }
-        let mut inner =
-            Wallet::create_with_params(params).map_err(CreateWithPersistError::Descriptor)?;
-        if let Some(changeset) = inner.take_staged() {
-            P::persist(persister, &changeset)
-                .await
-                .map_err(CreateWithPersistError::Persist)?;
-        }
-        Ok(Self {
-            inner,
-            _marker: PhantomData,
-        })
-    }
-
-    /// Load a previously [`PersistedWallet`] from the given async `persister` and `params`.
-    pub async fn load_async(
-        persister: &mut P,
-        params: LoadParams,
-    ) -> Result<Option<Self>, LoadWithPersistError<P::Error>> {
-        let changeset = P::initialize(persister)
-            .await
-            .map_err(LoadWithPersistError::Persist)?;
-        Wallet::load_with_params(changeset, params)
-            .map(|opt| {
-                opt.map(|inner| PersistedWallet {
-                    inner,
-                    _marker: PhantomData,
-                })
-            })
-            .map_err(LoadWithPersistError::InvalidChangeSet)
-    }
-
-    /// Persist staged changes of wallet into an async `persister`.
-    ///
-    /// Returns whether any new changes were persisted.
-    ///
-    /// If the `persister` errors, the staged changes will not be cleared.
-    pub async fn persist_async(&mut self, persister: &mut P) -> Result<bool, P::Error> {
-        match self.inner.staged_mut() {
-            Some(stage) => {
-                P::persist(persister, &*stage).await?;
-                let _ = stage.take();
-                Ok(true)
-            }
-            None => Ok(false),
-        }
-    }
-}
-
-#[cfg(feature = "rusqlite")]
-impl WalletPersister for bdk_chain::rusqlite::Transaction<'_> {
-    type Error = bdk_chain::rusqlite::Error;
-
-    fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
-        ChangeSet::init_sqlite_tables(&*persister)?;
-        ChangeSet::from_sqlite(persister)
-    }
-
-    fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
-        changeset.persist_to_sqlite(persister)
-    }
-}
-
-#[cfg(feature = "rusqlite")]
-impl WalletPersister for bdk_chain::rusqlite::Connection {
-    type Error = bdk_chain::rusqlite::Error;
-
-    fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
-        let db_tx = persister.transaction()?;
-        ChangeSet::init_sqlite_tables(&db_tx)?;
-        let changeset = ChangeSet::from_sqlite(&db_tx)?;
-        db_tx.commit()?;
-        Ok(changeset)
-    }
-
-    fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
-        let db_tx = persister.transaction()?;
-        changeset.persist_to_sqlite(&db_tx)?;
-        db_tx.commit()
-    }
-}
-
-/// Error for [`bdk_file_store`]'s implementation of [`WalletPersister`].
-#[cfg(feature = "file_store")]
-#[derive(Debug)]
-pub enum FileStoreError {
-    /// Error when loading from the store.
-    Load(bdk_file_store::AggregateChangesetsError<ChangeSet>),
-    /// Error when writing to the store.
-    Write(std::io::Error),
-}
-
-#[cfg(feature = "file_store")]
-impl core::fmt::Display for FileStoreError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        use core::fmt::Display;
-        match self {
-            FileStoreError::Load(e) => Display::fmt(e, f),
-            FileStoreError::Write(e) => Display::fmt(e, f),
-        }
-    }
-}
-
-#[cfg(feature = "file_store")]
-impl std::error::Error for FileStoreError {}
-
-#[cfg(feature = "file_store")]
-impl WalletPersister for bdk_file_store::Store<ChangeSet> {
-    type Error = FileStoreError;
-
-    fn initialize(persister: &mut Self) -> Result<ChangeSet, Self::Error> {
-        persister
-            .aggregate_changesets()
-            .map(Option::unwrap_or_default)
-            .map_err(FileStoreError::Load)
-    }
-
-    fn persist(persister: &mut Self, changeset: &ChangeSet) -> Result<(), Self::Error> {
-        persister
-            .append_changeset(changeset)
-            .map_err(FileStoreError::Write)
-    }
-}
-
-/// Error type for [`PersistedWallet::load`].
-#[derive(Debug, PartialEq)]
-pub enum LoadWithPersistError<E> {
-    /// Error from persistence.
-    Persist(E),
-    /// Occurs when the loaded changeset cannot construct [`Wallet`].
-    InvalidChangeSet(crate::LoadError),
-}
-
-impl<E: fmt::Display> fmt::Display for LoadWithPersistError<E> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Persist(err) => fmt::Display::fmt(err, f),
-            Self::InvalidChangeSet(err) => fmt::Display::fmt(&err, f),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl<E: fmt::Debug + fmt::Display> std::error::Error for LoadWithPersistError<E> {}
-
-/// Error type for [`PersistedWallet::create`].
-#[derive(Debug)]
-pub enum CreateWithPersistError<E> {
-    /// Error from persistence.
-    Persist(E),
-    /// Persister already has wallet data.
-    DataAlreadyExists(ChangeSet),
-    /// Occurs when the loaded changeset cannot construct [`Wallet`].
-    Descriptor(DescriptorError),
-}
-
-impl<E: fmt::Display> fmt::Display for CreateWithPersistError<E> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Persist(err) => fmt::Display::fmt(err, f),
-            Self::DataAlreadyExists(changeset) => write!(
-                f,
-                "Cannot create wallet in persister which already contains wallet data: {:?}",
-                changeset
-            ),
-            Self::Descriptor(err) => fmt::Display::fmt(&err, f),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl<E: fmt::Debug + fmt::Display> std::error::Error for CreateWithPersistError<E> {}
diff --git a/crates/wallet/src/wallet/signer.rs b/crates/wallet/src/wallet/signer.rs
deleted file mode 100644 (file)
index e08a434..0000000
+++ /dev/null
@@ -1,1031 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Generalized signers
-//!
-//! This module provides the ability to add customized signers to a [`Wallet`](super::Wallet)
-//! through the [`Wallet::add_signer`](super::Wallet::add_signer) function.
-//!
-//! ```
-//! # use alloc::sync::Arc;
-//! # use core::str::FromStr;
-//! # use bitcoin::secp256k1::{Secp256k1, All};
-//! # use bitcoin::*;
-//! # use bdk_wallet::signer::*;
-//! # use bdk_wallet::*;
-//! # #[derive(Debug)]
-//! # struct CustomHSM;
-//! # impl CustomHSM {
-//! #     fn hsm_sign_input(&self, _psbt: &mut Psbt, _input: usize) -> Result<(), SignerError> {
-//! #         Ok(())
-//! #     }
-//! #     fn connect() -> Self {
-//! #         CustomHSM
-//! #     }
-//! #     fn get_id(&self) -> SignerId {
-//! #         SignerId::Dummy(0)
-//! #     }
-//! # }
-//! #[derive(Debug)]
-//! struct CustomSigner {
-//!     device: CustomHSM,
-//! }
-//!
-//! impl CustomSigner {
-//!     fn connect() -> Self {
-//!         CustomSigner { device: CustomHSM::connect() }
-//!     }
-//! }
-//!
-//! impl SignerCommon for CustomSigner {
-//!     fn id(&self, _secp: &Secp256k1<All>) -> SignerId {
-//!         self.device.get_id()
-//!     }
-//! }
-//!
-//! impl InputSigner for CustomSigner {
-//!     fn sign_input(
-//!         &self,
-//!         psbt: &mut Psbt,
-//!         input_index: usize,
-//!         _sign_options: &SignOptions,
-//!         _secp: &Secp256k1<All>,
-//!     ) -> Result<(), SignerError> {
-//!         self.device.hsm_sign_input(psbt, input_index)?;
-//!
-//!         Ok(())
-//!     }
-//! }
-//!
-//! let custom_signer = CustomSigner::connect();
-//!
-//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)";
-//! let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)";
-//! let mut wallet = Wallet::create(descriptor, change_descriptor)
-//!     .network(Network::Testnet)
-//!     .create_wallet_no_persist()?;
-//! wallet.add_signer(
-//!     KeychainKind::External,
-//!     SignerOrdering(200),
-//!     Arc::new(custom_signer)
-//! );
-//!
-//! # Ok::<_, anyhow::Error>(())
-//! ```
-
-use crate::collections::BTreeMap;
-use alloc::string::String;
-use alloc::sync::Arc;
-use alloc::vec::Vec;
-use core::cmp::Ordering;
-use core::fmt;
-use core::ops::{Bound::Included, Deref};
-
-use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpriv};
-use bitcoin::hashes::hash160;
-use bitcoin::secp256k1::Message;
-use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType};
-use bitcoin::{ecdsa, psbt, sighash, taproot};
-use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1};
-use bitcoin::{PrivateKey, Psbt, PublicKey};
-
-use miniscript::descriptor::{
-    Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey,
-    InnerXKey, KeyMap, SinglePriv, SinglePubKey,
-};
-use miniscript::{SigType, ToPublicKey};
-
-use super::utils::SecpCtx;
-use crate::descriptor::{DescriptorMeta, XKeyUtils};
-use crate::psbt::PsbtUtils;
-use crate::wallet::error::MiniscriptPsbtError;
-
-/// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
-/// multiple of them
-#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
-pub enum SignerId {
-    /// Bitcoin HASH160 (RIPEMD160 after SHA256) hash of an ECDSA public key
-    PkHash(hash160::Hash),
-    /// The fingerprint of a BIP32 extended key
-    Fingerprint(Fingerprint),
-    /// Dummy identifier
-    Dummy(u64),
-}
-
-impl From<hash160::Hash> for SignerId {
-    fn from(hash: hash160::Hash) -> SignerId {
-        SignerId::PkHash(hash)
-    }
-}
-
-impl From<Fingerprint> for SignerId {
-    fn from(fing: Fingerprint) -> SignerId {
-        SignerId::Fingerprint(fing)
-    }
-}
-
-/// Signing error
-#[derive(Debug)]
-pub enum SignerError {
-    /// The private key is missing for the required public key
-    MissingKey,
-    /// The private key in use has the right fingerprint but derives differently than expected
-    InvalidKey,
-    /// The user canceled the operation
-    UserCanceled,
-    /// Input index is out of range
-    InputIndexOutOfRange,
-    /// The `non_witness_utxo` field of the transaction is required to sign this input
-    MissingNonWitnessUtxo,
-    /// The `non_witness_utxo` specified is invalid
-    InvalidNonWitnessUtxo,
-    /// The `witness_utxo` field of the transaction is required to sign this input
-    MissingWitnessUtxo,
-    /// The `witness_script` field of the transaction is required to sign this input
-    MissingWitnessScript,
-    /// The fingerprint and derivation path are missing from the psbt input
-    MissingHdKeypath,
-    /// The psbt contains a non-`SIGHASH_ALL` sighash in one of its input and the user hasn't
-    /// explicitly allowed them
-    ///
-    /// To enable signing transactions with non-standard sighashes set
-    /// [`SignOptions::allow_all_sighashes`] to `true`.
-    NonStandardSighash,
-    /// Invalid SIGHASH for the signing context in use
-    InvalidSighash,
-    /// Error while computing the hash to sign a Taproot input.
-    SighashTaproot(sighash::TaprootError),
-    /// PSBT sign error.
-    Psbt(psbt::SignError),
-    /// Miniscript PSBT error
-    MiniscriptPsbt(MiniscriptPsbtError),
-    /// To be used only by external libraries implementing [`InputSigner`] or
-    /// [`TransactionSigner`], so that they can return their own custom errors, without having to
-    /// modify [`SignerError`] in BDK.
-    External(String),
-}
-
-impl fmt::Display for SignerError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::MissingKey => write!(f, "Missing private key"),
-            Self::InvalidKey => write!(f, "The private key in use has the right fingerprint but derives differently than expected"),
-            Self::UserCanceled => write!(f, "The user canceled the operation"),
-            Self::InputIndexOutOfRange => write!(f, "Input index out of range"),
-            Self::MissingNonWitnessUtxo => write!(f, "Missing non-witness UTXO"),
-            Self::InvalidNonWitnessUtxo => write!(f, "Invalid non-witness UTXO"),
-            Self::MissingWitnessUtxo => write!(f, "Missing witness UTXO"),
-            Self::MissingWitnessScript => write!(f, "Missing witness script"),
-            Self::MissingHdKeypath => write!(f, "Missing fingerprint and derivation path"),
-            Self::NonStandardSighash => write!(f, "The psbt contains a non standard sighash"),
-            Self::InvalidSighash => write!(f, "Invalid SIGHASH for the signing context in use"),
-            Self::SighashTaproot(err) => write!(f, "Error while computing the hash to sign a Taproot input: {}", err),
-            Self::Psbt(err) => write!(f, "Error computing the sighash: {}", err),
-            Self::MiniscriptPsbt(err) => write!(f, "Miniscript PSBT error: {}", err),
-            Self::External(err) => write!(f, "{}", err),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for SignerError {}
-
-/// Signing context
-///
-/// Used by our software signers to determine the type of signatures to make
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum SignerContext {
-    /// Legacy context
-    Legacy,
-    /// Segwit v0 context (BIP 143)
-    Segwitv0,
-    /// Taproot context (BIP 340)
-    Tap {
-        /// Whether the signer can sign for the internal key or not
-        is_internal_key: bool,
-    },
-}
-
-/// Wrapper to pair a signer with its context
-#[derive(Debug, Clone)]
-pub struct SignerWrapper<S: Sized + fmt::Debug + Clone> {
-    signer: S,
-    ctx: SignerContext,
-}
-
-impl<S: Sized + fmt::Debug + Clone> SignerWrapper<S> {
-    /// Create a wrapped signer from a signer and a context
-    pub fn new(signer: S, ctx: SignerContext) -> Self {
-        SignerWrapper { signer, ctx }
-    }
-}
-
-impl<S: Sized + fmt::Debug + Clone> Deref for SignerWrapper<S> {
-    type Target = S;
-
-    fn deref(&self) -> &Self::Target {
-        &self.signer
-    }
-}
-
-/// Common signer methods
-pub trait SignerCommon: fmt::Debug + Send + Sync {
-    /// Return the [`SignerId`] for this signer
-    ///
-    /// The [`SignerId`] can be used to lookup a signer in the [`Wallet`](crate::Wallet)'s signers map or to
-    /// compare two signers.
-    fn id(&self, secp: &SecpCtx) -> SignerId;
-
-    /// Return the secret key for the signer
-    ///
-    /// This is used internally to reconstruct the original descriptor that may contain secrets.
-    /// External signers that are meant to keep key isolated should just return `None` here (which
-    /// is the default for this method, if not overridden).
-    fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
-        None
-    }
-}
-
-/// PSBT Input signer
-///
-/// This trait can be implemented to provide custom signers to the wallet. If the signer supports signing
-/// individual inputs, this trait should be implemented and BDK will provide automatically an implementation
-/// for [`TransactionSigner`].
-pub trait InputSigner: SignerCommon {
-    /// Sign a single psbt input
-    fn sign_input(
-        &self,
-        psbt: &mut Psbt,
-        input_index: usize,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError>;
-}
-
-/// PSBT signer
-///
-/// This trait can be implemented when the signer can't sign inputs individually, but signs the whole transaction
-/// at once.
-pub trait TransactionSigner: SignerCommon {
-    /// Sign all the inputs of the psbt
-    fn sign_transaction(
-        &self,
-        psbt: &mut Psbt,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError>;
-}
-
-impl<T: InputSigner> TransactionSigner for T {
-    fn sign_transaction(
-        &self,
-        psbt: &mut Psbt,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError> {
-        for input_index in 0..psbt.inputs.len() {
-            self.sign_input(psbt, input_index, sign_options, secp)?;
-        }
-
-        Ok(())
-    }
-}
-
-impl SignerCommon for SignerWrapper<DescriptorXKey<Xpriv>> {
-    fn id(&self, secp: &SecpCtx) -> SignerId {
-        SignerId::from(self.root_fingerprint(secp))
-    }
-
-    fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
-        Some(DescriptorSecretKey::XPrv(self.signer.clone()))
-    }
-}
-
-impl InputSigner for SignerWrapper<DescriptorXKey<Xpriv>> {
-    fn sign_input(
-        &self,
-        psbt: &mut Psbt,
-        input_index: usize,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError> {
-        if input_index >= psbt.inputs.len() {
-            return Err(SignerError::InputIndexOutOfRange);
-        }
-
-        if psbt.inputs[input_index].final_script_sig.is_some()
-            || psbt.inputs[input_index].final_script_witness.is_some()
-        {
-            return Ok(());
-        }
-
-        let tap_key_origins = psbt.inputs[input_index]
-            .tap_key_origins
-            .iter()
-            .map(|(pk, (_, keysource))| (SinglePubKey::XOnly(*pk), keysource));
-        let (public_key, full_path) = match psbt.inputs[input_index]
-            .bip32_derivation
-            .iter()
-            .map(|(pk, keysource)| (SinglePubKey::FullKey(PublicKey::new(*pk)), keysource))
-            .chain(tap_key_origins)
-            .find_map(|(pk, keysource)| {
-                if self.matches(keysource, secp).is_some() {
-                    Some((pk, keysource.1.clone()))
-                } else {
-                    None
-                }
-            }) {
-            Some((pk, full_path)) => (pk, full_path),
-            None => return Ok(()),
-        };
-
-        let derived_key = match self.origin.clone() {
-            Some((_fingerprint, origin_path)) => {
-                let deriv_path = DerivationPath::from(
-                    &full_path.into_iter().cloned().collect::<Vec<ChildNumber>>()
-                        [origin_path.len()..],
-                );
-                self.xkey.derive_priv(secp, &deriv_path).unwrap()
-            }
-            None => self.xkey.derive_priv(secp, &full_path).unwrap(),
-        };
-
-        let computed_pk = secp256k1::PublicKey::from_secret_key(secp, &derived_key.private_key);
-        let valid_key = match public_key {
-            SinglePubKey::FullKey(pk) if pk.inner == computed_pk => true,
-            SinglePubKey::XOnly(x_only) if XOnlyPublicKey::from(computed_pk) == x_only => true,
-            _ => false,
-        };
-        if !valid_key {
-            Err(SignerError::InvalidKey)
-        } else {
-            // HD wallets imply compressed keys
-            let priv_key = PrivateKey {
-                compressed: true,
-                network: self.xkey.network,
-                inner: derived_key.private_key,
-            };
-
-            SignerWrapper::new(priv_key, self.ctx).sign_input(psbt, input_index, sign_options, secp)
-        }
-    }
-}
-
-fn multikey_to_xkeys<K: InnerXKey + Clone>(
-    multikey: DescriptorMultiXKey<K>,
-) -> Vec<DescriptorXKey<K>> {
-    multikey
-        .derivation_paths
-        .into_paths()
-        .into_iter()
-        .map(|derivation_path| DescriptorXKey {
-            origin: multikey.origin.clone(),
-            xkey: multikey.xkey.clone(),
-            derivation_path,
-            wildcard: multikey.wildcard,
-        })
-        .collect()
-}
-
-impl SignerCommon for SignerWrapper<DescriptorMultiXKey<Xpriv>> {
-    fn id(&self, secp: &SecpCtx) -> SignerId {
-        SignerId::from(self.root_fingerprint(secp))
-    }
-
-    fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
-        Some(DescriptorSecretKey::MultiXPrv(self.signer.clone()))
-    }
-}
-
-impl InputSigner for SignerWrapper<DescriptorMultiXKey<Xpriv>> {
-    fn sign_input(
-        &self,
-        psbt: &mut Psbt,
-        input_index: usize,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError> {
-        let xkeys = multikey_to_xkeys(self.signer.clone());
-        for xkey in xkeys {
-            SignerWrapper::new(xkey, self.ctx).sign_input(psbt, input_index, sign_options, secp)?
-        }
-        Ok(())
-    }
-}
-
-impl SignerCommon for SignerWrapper<PrivateKey> {
-    fn id(&self, secp: &SecpCtx) -> SignerId {
-        SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa))
-    }
-
-    fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
-        Some(DescriptorSecretKey::Single(SinglePriv {
-            key: self.signer,
-            origin: None,
-        }))
-    }
-}
-
-impl InputSigner for SignerWrapper<PrivateKey> {
-    fn sign_input(
-        &self,
-        psbt: &mut Psbt,
-        input_index: usize,
-        sign_options: &SignOptions,
-        secp: &SecpCtx,
-    ) -> Result<(), SignerError> {
-        if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
-            return Err(SignerError::InputIndexOutOfRange);
-        }
-
-        if psbt.inputs[input_index].final_script_sig.is_some()
-            || psbt.inputs[input_index].final_script_witness.is_some()
-        {
-            return Ok(());
-        }
-
-        let pubkey = PublicKey::from_private_key(secp, self);
-
-        match self.ctx {
-            SignerContext::Tap { is_internal_key } => {
-                let x_only_pubkey = XOnlyPublicKey::from(pubkey.inner);
-
-                if let Some(psbt_internal_key) = psbt.inputs[input_index].tap_internal_key {
-                    if is_internal_key
-                        && psbt.inputs[input_index].tap_key_sig.is_none()
-                        && sign_options.sign_with_tap_internal_key
-                        && x_only_pubkey == psbt_internal_key
-                    {
-                        let (sighash, sighash_type) = compute_tap_sighash(psbt, input_index, None)?;
-                        sign_psbt_schnorr(
-                            &self.inner,
-                            x_only_pubkey,
-                            None,
-                            &mut psbt.inputs[input_index],
-                            sighash,
-                            sighash_type,
-                            secp,
-                        );
-                    }
-                }
-
-                if let Some((leaf_hashes, _)) =
-                    psbt.inputs[input_index].tap_key_origins.get(&x_only_pubkey)
-                {
-                    let leaf_hashes = leaf_hashes
-                        .iter()
-                        .filter(|lh| {
-                            // Removing the leaves we shouldn't sign for
-                            let should_sign = match &sign_options.tap_leaves_options {
-                                TapLeavesOptions::All => true,
-                                TapLeavesOptions::Include(v) => v.contains(lh),
-                                TapLeavesOptions::Exclude(v) => !v.contains(lh),
-                                TapLeavesOptions::None => false,
-                            };
-                            // Filtering out the leaves without our key
-                            should_sign
-                                && !psbt.inputs[input_index]
-                                    .tap_script_sigs
-                                    .contains_key(&(x_only_pubkey, **lh))
-                        })
-                        .cloned()
-                        .collect::<Vec<_>>();
-                    for lh in leaf_hashes {
-                        let (sighash, sighash_type) =
-                            compute_tap_sighash(psbt, input_index, Some(lh))?;
-                        sign_psbt_schnorr(
-                            &self.inner,
-                            x_only_pubkey,
-                            Some(lh),
-                            &mut psbt.inputs[input_index],
-                            sighash,
-                            sighash_type,
-                            secp,
-                        );
-                    }
-                }
-            }
-            SignerContext::Segwitv0 | SignerContext::Legacy => {
-                if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
-                    return Ok(());
-                }
-
-                let mut sighasher = sighash::SighashCache::new(psbt.unsigned_tx.clone());
-                let (msg, sighash_type) = psbt
-                    .sighash_ecdsa(input_index, &mut sighasher)
-                    .map_err(SignerError::Psbt)?;
-
-                sign_psbt_ecdsa(
-                    &self.inner,
-                    pubkey,
-                    &mut psbt.inputs[input_index],
-                    &msg,
-                    sighash_type,
-                    secp,
-                    sign_options.allow_grinding,
-                );
-            }
-        }
-
-        Ok(())
-    }
-}
-
-fn sign_psbt_ecdsa(
-    secret_key: &secp256k1::SecretKey,
-    pubkey: PublicKey,
-    psbt_input: &mut psbt::Input,
-    msg: &Message,
-    sighash_type: EcdsaSighashType,
-    secp: &SecpCtx,
-    allow_grinding: bool,
-) {
-    let signature = if allow_grinding {
-        secp.sign_ecdsa_low_r(msg, secret_key)
-    } else {
-        secp.sign_ecdsa(msg, secret_key)
-    };
-    secp.verify_ecdsa(msg, &signature, &pubkey.inner)
-        .expect("invalid or corrupted ecdsa signature");
-
-    let final_signature = ecdsa::Signature {
-        signature,
-        sighash_type,
-    };
-    psbt_input.partial_sigs.insert(pubkey, final_signature);
-}
-
-// Calling this with `leaf_hash` = `None` will sign for key-spend
-fn sign_psbt_schnorr(
-    secret_key: &secp256k1::SecretKey,
-    pubkey: XOnlyPublicKey,
-    leaf_hash: Option<taproot::TapLeafHash>,
-    psbt_input: &mut psbt::Input,
-    sighash: TapSighash,
-    sighash_type: TapSighashType,
-    secp: &SecpCtx,
-) {
-    let keypair = secp256k1::Keypair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
-    let keypair = match leaf_hash {
-        None => keypair
-            .tap_tweak(secp, psbt_input.tap_merkle_root)
-            .to_inner(),
-        Some(_) => keypair, // no tweak for script spend
-    };
-
-    let msg = &Message::from(sighash);
-    let signature = secp.sign_schnorr_no_aux_rand(msg, &keypair);
-    secp.verify_schnorr(&signature, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
-        .expect("invalid or corrupted schnorr signature");
-
-    let final_signature = taproot::Signature {
-        signature,
-        sighash_type,
-    };
-
-    if let Some(lh) = leaf_hash {
-        psbt_input
-            .tap_script_sigs
-            .insert((pubkey, lh), final_signature);
-    } else {
-        psbt_input.tap_key_sig = Some(final_signature);
-    }
-}
-
-/// Defines the order in which signers are called
-///
-/// The default value is `100`. Signers with an ordering above that will be called later,
-/// and they will thus see the partial signatures added to the transaction once they get to sign
-/// themselves.
-#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)]
-pub struct SignerOrdering(pub usize);
-
-impl Default for SignerOrdering {
-    fn default() -> Self {
-        SignerOrdering(100)
-    }
-}
-
-#[derive(Debug, Clone)]
-struct SignersContainerKey {
-    id: SignerId,
-    ordering: SignerOrdering,
-}
-
-impl From<(SignerId, SignerOrdering)> for SignersContainerKey {
-    fn from(tuple: (SignerId, SignerOrdering)) -> Self {
-        SignersContainerKey {
-            id: tuple.0,
-            ordering: tuple.1,
-        }
-    }
-}
-
-/// Container for multiple signers
-#[derive(Debug, Default, Clone)]
-pub struct SignersContainer(BTreeMap<SignersContainerKey, Arc<dyn TransactionSigner>>);
-
-impl SignersContainer {
-    /// Create a map of public keys to secret keys
-    pub fn as_key_map(&self, secp: &SecpCtx) -> KeyMap {
-        self.0
-            .values()
-            .filter_map(|signer| signer.descriptor_secret_key())
-            .filter_map(|secret| secret.to_public(secp).ok().map(|public| (public, secret)))
-            .collect()
-    }
-
-    /// Build a new signer container from a [`KeyMap`]
-    ///
-    /// Also looks at the corresponding descriptor to determine the [`SignerContext`] to attach to
-    /// the signers
-    pub fn build(
-        keymap: KeyMap,
-        descriptor: &Descriptor<DescriptorPublicKey>,
-        secp: &SecpCtx,
-    ) -> SignersContainer {
-        let mut container = SignersContainer::new();
-
-        for (pubkey, secret) in keymap {
-            let ctx = match descriptor {
-                Descriptor::Tr(tr) => SignerContext::Tap {
-                    is_internal_key: tr.internal_key() == &pubkey,
-                },
-                _ if descriptor.is_witness() => SignerContext::Segwitv0,
-                _ => SignerContext::Legacy,
-            };
-
-            match secret {
-                DescriptorSecretKey::Single(private_key) => container.add_external(
-                    SignerId::from(
-                        private_key
-                            .key
-                            .public_key(secp)
-                            .to_pubkeyhash(SigType::Ecdsa),
-                    ),
-                    SignerOrdering::default(),
-                    Arc::new(SignerWrapper::new(private_key.key, ctx)),
-                ),
-                DescriptorSecretKey::XPrv(xprv) => container.add_external(
-                    SignerId::from(xprv.root_fingerprint(secp)),
-                    SignerOrdering::default(),
-                    Arc::new(SignerWrapper::new(xprv, ctx)),
-                ),
-                DescriptorSecretKey::MultiXPrv(xprv) => container.add_external(
-                    SignerId::from(xprv.root_fingerprint(secp)),
-                    SignerOrdering::default(),
-                    Arc::new(SignerWrapper::new(xprv, ctx)),
-                ),
-            };
-        }
-
-        container
-    }
-}
-
-impl SignersContainer {
-    /// Default constructor
-    pub fn new() -> Self {
-        SignersContainer(Default::default())
-    }
-
-    /// Adds an external signer to the container for the specified id. Optionally returns the
-    /// signer that was previously in the container, if any
-    pub fn add_external(
-        &mut self,
-        id: SignerId,
-        ordering: SignerOrdering,
-        signer: Arc<dyn TransactionSigner>,
-    ) -> Option<Arc<dyn TransactionSigner>> {
-        self.0.insert((id, ordering).into(), signer)
-    }
-
-    /// Removes a signer from the container and returns it
-    pub fn remove(
-        &mut self,
-        id: SignerId,
-        ordering: SignerOrdering,
-    ) -> Option<Arc<dyn TransactionSigner>> {
-        self.0.remove(&(id, ordering).into())
-    }
-
-    /// Returns the list of identifiers of all the signers in the container
-    pub fn ids(&self) -> Vec<&SignerId> {
-        self.0
-            .keys()
-            .map(|SignersContainerKey { id, .. }| id)
-            .collect()
-    }
-
-    /// Returns the list of signers in the container, sorted by lowest to highest `ordering`
-    pub fn signers(&self) -> Vec<&Arc<dyn TransactionSigner>> {
-        self.0.values().collect()
-    }
-
-    /// Finds the signer with lowest ordering for a given id in the container.
-    pub fn find(&self, id: SignerId) -> Option<&Arc<dyn TransactionSigner>> {
-        self.0
-            .range((
-                Included(&(id.clone(), SignerOrdering(0)).into()),
-                Included(&(id.clone(), SignerOrdering(usize::MAX)).into()),
-            ))
-            .filter(|(k, _)| k.id == id)
-            .map(|(_, v)| v)
-            .next()
-    }
-}
-
-/// Options for a software signer
-///
-/// Adjust the behavior of our software signers and the way a transaction is finalized
-#[derive(Debug, Clone)]
-pub struct SignOptions {
-    /// Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been
-    /// provided
-    ///
-    /// Defaults to `false` to mitigate the "SegWit bug" which could trick the wallet into
-    /// paying a fee larger than expected.
-    ///
-    /// Some wallets, especially if relatively old, might not provide the `non_witness_utxo` for
-    /// SegWit transactions in the PSBT they generate: in those cases setting this to `true`
-    /// should correctly produce a signature, at the expense of an increased trust in the creator
-    /// of the PSBT.
-    ///
-    /// For more details see: <https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd>
-    pub trust_witness_utxo: bool,
-
-    /// Whether the wallet should assume a specific height has been reached when trying to finalize
-    /// a transaction
-    ///
-    /// The wallet will only "use" a timelock to satisfy the spending policy of an input if the
-    /// timelock height has already been reached. This option allows overriding the "current height" to let the
-    /// wallet use timelocks in the future to spend a coin.
-    pub assume_height: Option<u32>,
-
-    /// Whether the signer should use the `sighash_type` set in the PSBT when signing, no matter
-    /// what its value is
-    ///
-    /// Defaults to `false` which will only allow signing using `SIGHASH_ALL`.
-    pub allow_all_sighashes: bool,
-
-    /// Whether to try finalizing the PSBT after the inputs are signed.
-    ///
-    /// Defaults to `true` which will try finalizing PSBT after inputs are signed.
-    pub try_finalize: bool,
-
-    /// Specifies which Taproot script-spend leaves we should sign for. This option is
-    /// ignored if we're signing a non-taproot PSBT.
-    ///
-    /// Defaults to All, i.e., the wallet will sign all the leaves it has a key for.
-    pub tap_leaves_options: TapLeavesOptions,
-
-    /// Whether we should try to sign a taproot transaction with the taproot internal key
-    /// or not. This option is ignored if we're signing a non-taproot PSBT.
-    ///
-    /// Defaults to `true`, i.e., we always try to sign with the taproot internal key.
-    pub sign_with_tap_internal_key: bool,
-
-    /// Whether we should grind ECDSA signature to ensure signing with low r
-    /// or not.
-    /// Defaults to `true`, i.e., we always grind ECDSA signature to sign with low r.
-    pub allow_grinding: bool,
-}
-
-/// Customize which taproot script-path leaves the signer should sign.
-#[derive(Default, Debug, Clone, PartialEq, Eq)]
-pub enum TapLeavesOptions {
-    /// The signer will sign all the leaves it has a key for.
-    #[default]
-    All,
-    /// The signer won't sign leaves other than the ones specified. Note that it could still ignore
-    /// some of the specified leaves, if it doesn't have the right key to sign them.
-    Include(Vec<taproot::TapLeafHash>),
-    /// The signer won't sign the specified leaves.
-    Exclude(Vec<taproot::TapLeafHash>),
-    /// The signer won't sign any leaf.
-    None,
-}
-
-impl Default for SignOptions {
-    fn default() -> Self {
-        SignOptions {
-            trust_witness_utxo: false,
-            assume_height: None,
-            allow_all_sighashes: false,
-            try_finalize: true,
-            tap_leaves_options: TapLeavesOptions::default(),
-            sign_with_tap_internal_key: true,
-            allow_grinding: true,
-        }
-    }
-}
-
-/// Computes the taproot sighash.
-fn compute_tap_sighash(
-    psbt: &Psbt,
-    input_index: usize,
-    extra: Option<taproot::TapLeafHash>,
-) -> Result<(sighash::TapSighash, TapSighashType), SignerError> {
-    if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
-        return Err(SignerError::InputIndexOutOfRange);
-    }
-
-    let psbt_input = &psbt.inputs[input_index];
-
-    let sighash_type = psbt_input
-        .sighash_type
-        .unwrap_or_else(|| TapSighashType::Default.into())
-        .taproot_hash_ty()
-        .map_err(|_| SignerError::InvalidSighash)?;
-    let witness_utxos = (0..psbt.inputs.len())
-        .map(|i| psbt.get_utxo_for(i))
-        .collect::<Vec<_>>();
-    let mut all_witness_utxos = vec![];
-
-    let mut cache = sighash::SighashCache::new(&psbt.unsigned_tx);
-    let is_anyone_can_pay = psbt::PsbtSighashType::from(sighash_type).to_u32() & 0x80 != 0;
-    let prevouts = if is_anyone_can_pay {
-        sighash::Prevouts::One(
-            input_index,
-            witness_utxos[input_index]
-                .as_ref()
-                .ok_or(SignerError::MissingWitnessUtxo)?,
-        )
-    } else if witness_utxos.iter().all(Option::is_some) {
-        all_witness_utxos.extend(witness_utxos.iter().filter_map(|x| x.as_ref()));
-        sighash::Prevouts::All(&all_witness_utxos)
-    } else {
-        return Err(SignerError::MissingWitnessUtxo);
-    };
-
-    // Assume no OP_CODESEPARATOR
-    let extra = extra.map(|leaf_hash| (leaf_hash, 0xFFFFFFFF));
-
-    Ok((
-        cache
-            .taproot_signature_hash(input_index, &prevouts, None, extra, sighash_type)
-            .map_err(SignerError::SighashTaproot)?,
-        sighash_type,
-    ))
-}
-
-impl PartialOrd for SignersContainerKey {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for SignersContainerKey {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.ordering
-            .cmp(&other.ordering)
-            .then(self.id.cmp(&other.id))
-    }
-}
-
-impl PartialEq for SignersContainerKey {
-    fn eq(&self, other: &Self) -> bool {
-        self.id == other.id && self.ordering == other.ordering
-    }
-}
-
-impl Eq for SignersContainerKey {}
-
-#[cfg(test)]
-mod signers_container_tests {
-    use super::*;
-    use crate::descriptor;
-    use crate::descriptor::IntoWalletDescriptor;
-    use crate::keys::{DescriptorKey, IntoDescriptorKey};
-    use assert_matches::assert_matches;
-    use bitcoin::bip32;
-    use bitcoin::secp256k1::{All, Secp256k1};
-    use bitcoin::Network;
-    use core::str::FromStr;
-    use miniscript::ScriptContext;
-
-    fn is_equal(this: &Arc<dyn TransactionSigner>, that: &Arc<DummySigner>) -> bool {
-        let secp = Secp256k1::new();
-        this.id(&secp) == that.id(&secp)
-    }
-
-    // Signers added with the same ordering (like `Ordering::default`) created from `KeyMap`
-    // should be preserved and not overwritten.
-    // This happens usually when a set of signers is created from a descriptor with private keys.
-    #[test]
-    fn signers_with_same_ordering() {
-        let secp = Secp256k1::new();
-
-        let (prvkey1, _, _) = setup_keys(TPRV0_STR);
-        let (prvkey2, _, _) = setup_keys(TPRV1_STR);
-        let desc = descriptor!(sh(multi(2, prvkey1, prvkey2))).unwrap();
-        let (wallet_desc, keymap) = desc
-            .into_wallet_descriptor(&secp, Network::Testnet)
-            .unwrap();
-
-        let signers = SignersContainer::build(keymap, &wallet_desc, &secp);
-        assert_eq!(signers.ids().len(), 2);
-
-        let signers = signers.signers();
-        assert_eq!(signers.len(), 2);
-    }
-
-    #[test]
-    fn signers_sorted_by_ordering() {
-        let mut signers = SignersContainer::new();
-        let signer1 = Arc::new(DummySigner { number: 1 });
-        let signer2 = Arc::new(DummySigner { number: 2 });
-        let signer3 = Arc::new(DummySigner { number: 3 });
-
-        // Mixed order insertions verifies we are not inserting at head or tail.
-        signers.add_external(SignerId::Dummy(2), SignerOrdering(2), signer2.clone());
-        signers.add_external(SignerId::Dummy(1), SignerOrdering(1), signer1.clone());
-        signers.add_external(SignerId::Dummy(3), SignerOrdering(3), signer3.clone());
-
-        // Check that signers are sorted from lowest to highest ordering
-        let signers = signers.signers();
-
-        assert!(is_equal(signers[0], &signer1));
-        assert!(is_equal(signers[1], &signer2));
-        assert!(is_equal(signers[2], &signer3));
-    }
-
-    #[test]
-    fn find_signer_by_id() {
-        let mut signers = SignersContainer::new();
-        let signer1 = Arc::new(DummySigner { number: 1 });
-        let signer2 = Arc::new(DummySigner { number: 2 });
-        let signer3 = Arc::new(DummySigner { number: 3 });
-        let signer4 = Arc::new(DummySigner { number: 3 }); // Same ID as `signer3` but will use lower ordering.
-
-        let id1 = SignerId::Dummy(1);
-        let id2 = SignerId::Dummy(2);
-        let id3 = SignerId::Dummy(3);
-        let id_nonexistent = SignerId::Dummy(999);
-
-        signers.add_external(id1.clone(), SignerOrdering(1), signer1.clone());
-        signers.add_external(id2.clone(), SignerOrdering(2), signer2.clone());
-        signers.add_external(id3.clone(), SignerOrdering(3), signer3.clone());
-
-        assert_matches!(signers.find(id1), Some(signer) if is_equal(signer, &signer1));
-        assert_matches!(signers.find(id2), Some(signer) if is_equal(signer, &signer2));
-        assert_matches!(signers.find(id3.clone()), Some(signer) if is_equal(signer, &signer3));
-
-        // The `signer4` has the same ID as `signer3` but lower ordering.
-        // It should be found by `id3` instead of `signer3`.
-        signers.add_external(id3.clone(), SignerOrdering(2), signer4.clone());
-        assert_matches!(signers.find(id3), Some(signer) if is_equal(signer, &signer4));
-
-        // Can't find anything with ID that doesn't exist
-        assert_matches!(signers.find(id_nonexistent), None);
-    }
-
-    #[derive(Debug, Clone, Copy)]
-    struct DummySigner {
-        number: u64,
-    }
-
-    impl SignerCommon for DummySigner {
-        fn id(&self, _secp: &SecpCtx) -> SignerId {
-            SignerId::Dummy(self.number)
-        }
-    }
-
-    impl TransactionSigner for DummySigner {
-        fn sign_transaction(
-            &self,
-            _psbt: &mut Psbt,
-            _sign_options: &SignOptions,
-            _secp: &SecpCtx,
-        ) -> Result<(), SignerError> {
-            Ok(())
-        }
-    }
-
-    const TPRV0_STR:&str = "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf";
-    const TPRV1_STR:&str = "tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N";
-
-    const PATH: &str = "m/44'/1'/0'/0";
-
-    fn setup_keys<Ctx: ScriptContext>(
-        tprv: &str,
-    ) -> (DescriptorKey<Ctx>, DescriptorKey<Ctx>, Fingerprint) {
-        let secp: Secp256k1<All> = Secp256k1::new();
-        let path = bip32::DerivationPath::from_str(PATH).unwrap();
-        let tprv = bip32::Xpriv::from_str(tprv).unwrap();
-        let tpub = bip32::Xpub::from_priv(&secp, &tprv);
-        let fingerprint = tprv.fingerprint(&secp);
-        let prvkey = (tprv, path.clone()).into_descriptor_key().unwrap();
-        let pubkey = (tpub, path).into_descriptor_key().unwrap();
-
-        (prvkey, pubkey, fingerprint)
-    }
-}
diff --git a/crates/wallet/src/wallet/tx_builder.rs b/crates/wallet/src/wallet/tx_builder.rs
deleted file mode 100644 (file)
index 7d69376..0000000
+++ /dev/null
@@ -1,1349 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Transaction builder
-//!
-//! ## Example
-//!
-//! ```
-//! # use std::str::FromStr;
-//! # use bitcoin::*;
-//! # use bdk_wallet::*;
-//! # use bdk_wallet::ChangeSet;
-//! # use bdk_wallet::error::CreateTxError;
-//! # use anyhow::Error;
-//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
-//! # let mut wallet = doctest_wallet!();
-//! // create a TxBuilder from a wallet
-//! let mut tx_builder = wallet.build_tx();
-//!
-//! tx_builder
-//!     // Create a transaction with one output to `to_address` of 50_000 satoshi
-//!     .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
-//!     // With a custom fee rate of 5.0 satoshi/vbyte
-//!     .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"))
-//!     // Only spend non-change outputs
-//!     .do_not_spend_change();
-//! let psbt = tx_builder.finish()?;
-//! # Ok::<(), anyhow::Error>(())
-//! ```
-
-use alloc::{boxed::Box, string::String, vec::Vec};
-use core::fmt;
-
-use alloc::sync::Arc;
-
-use bitcoin::psbt::{self, Psbt};
-use bitcoin::script::PushBytes;
-use bitcoin::{
-    absolute, transaction::Version, Amount, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction,
-    TxIn, TxOut, Txid, Weight,
-};
-use rand_core::RngCore;
-
-use super::coin_selection::CoinSelectionAlgorithm;
-use super::utils::shuffle_slice;
-use super::{CreateTxError, Wallet};
-use crate::collections::{BTreeMap, HashMap, HashSet};
-use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
-
-/// A transaction builder
-///
-/// A `TxBuilder` is created by calling [`build_tx`] or [`build_fee_bump`] on a wallet. After
-/// assigning it, you set options on it until finally calling [`finish`] to consume the builder and
-/// generate the transaction.
-///
-/// Each option setting method on `TxBuilder` takes and returns `&mut self` so you can chain calls
-/// as in the following example:
-///
-/// ```
-/// # use bdk_wallet::*;
-/// # use bdk_wallet::tx_builder::*;
-/// # use bitcoin::*;
-/// # use core::str::FromStr;
-/// # use bdk_wallet::ChangeSet;
-/// # use bdk_wallet::error::CreateTxError;
-/// # use anyhow::Error;
-/// # let mut wallet = doctest_wallet!();
-/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
-/// # let addr2 = addr1.clone();
-/// // chaining
-/// let psbt1 = {
-///     let mut builder = wallet.build_tx();
-///     builder
-///         .ordering(TxOrdering::Untouched)
-///         .add_recipient(addr1.script_pubkey(), Amount::from_sat(50_000))
-///         .add_recipient(addr2.script_pubkey(), Amount::from_sat(50_000));
-///     builder.finish()?
-/// };
-///
-/// // non-chaining
-/// let psbt2 = {
-///     let mut builder = wallet.build_tx();
-///     builder.ordering(TxOrdering::Untouched);
-///     for addr in &[addr1, addr2] {
-///         builder.add_recipient(addr.script_pubkey(), Amount::from_sat(50_000));
-///     }
-///     builder.finish()?
-/// };
-///
-/// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]);
-/// # Ok::<(), anyhow::Error>(())
-/// ```
-///
-/// At the moment [`coin_selection`] is an exception to the rule as it consumes `self`.
-/// This means it is usually best to call [`coin_selection`] on the return value of `build_tx` before assigning it.
-///
-/// For further examples see [this module](super::tx_builder)'s documentation;
-///
-/// [`build_tx`]: Wallet::build_tx
-/// [`build_fee_bump`]: Wallet::build_fee_bump
-/// [`finish`]: Self::finish
-/// [`coin_selection`]: Self::coin_selection
-#[derive(Debug)]
-pub struct TxBuilder<'a, Cs> {
-    pub(crate) wallet: &'a mut Wallet,
-    pub(crate) params: TxParams,
-    pub(crate) coin_selection: Cs,
-}
-
-/// The parameters for transaction creation sans coin selection algorithm.
-//TODO: TxParams should eventually be exposed publicly.
-#[derive(Default, Debug, Clone)]
-pub(crate) struct TxParams {
-    pub(crate) recipients: Vec<(ScriptBuf, Amount)>,
-    pub(crate) drain_wallet: bool,
-    pub(crate) drain_to: Option<ScriptBuf>,
-    pub(crate) fee_policy: Option<FeePolicy>,
-    pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
-    pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
-    pub(crate) utxos: HashMap<OutPoint, WeightedUtxo>,
-    pub(crate) unspendable: HashSet<OutPoint>,
-    pub(crate) manually_selected_only: bool,
-    pub(crate) sighash: Option<psbt::PsbtSighashType>,
-    pub(crate) ordering: TxOrdering,
-    pub(crate) locktime: Option<absolute::LockTime>,
-    pub(crate) sequence: Option<Sequence>,
-    pub(crate) version: Option<Version>,
-    pub(crate) change_policy: ChangeSpendPolicy,
-    pub(crate) only_witness_utxo: bool,
-    pub(crate) add_global_xpubs: bool,
-    pub(crate) include_output_redeem_witness_script: bool,
-    pub(crate) bumping_fee: Option<PreviousFee>,
-    pub(crate) current_height: Option<absolute::LockTime>,
-    pub(crate) allow_dust: bool,
-}
-
-#[derive(Clone, Copy, Debug)]
-pub(crate) struct PreviousFee {
-    pub absolute: Amount,
-    pub rate: FeeRate,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub(crate) enum FeePolicy {
-    FeeRate(FeeRate),
-    FeeAmount(Amount),
-}
-
-impl Default for FeePolicy {
-    fn default() -> Self {
-        FeePolicy::FeeRate(FeeRate::BROADCAST_MIN)
-    }
-}
-
-// Methods supported for any CoinSelectionAlgorithm.
-impl<'a, Cs> TxBuilder<'a, Cs> {
-    /// Set a custom fee rate.
-    ///
-    /// This method sets the mining fee paid by the transaction as a rate on its size.
-    /// This means that the total fee paid is equal to `fee_rate` times the size
-    /// of the transaction. Default is 1 sat/vB in accordance with Bitcoin Core's default
-    /// relay policy.
-    ///
-    /// Note that this is really a minimum feerate -- it's possible to
-    /// overshoot it slightly since adding a change output to drain the remaining
-    /// excess might not be viable.
-    pub fn fee_rate(&mut self, fee_rate: FeeRate) -> &mut Self {
-        self.params.fee_policy = Some(FeePolicy::FeeRate(fee_rate));
-        self
-    }
-
-    /// Set an absolute fee
-    /// The fee_absolute method refers to the absolute transaction fee in [`Amount`].
-    /// If anyone sets both the `fee_absolute` method and the `fee_rate` method,
-    /// the `FeePolicy` enum will be set by whichever method was called last,
-    /// as the [`FeeRate`] and `FeeAmount` are mutually exclusive.
-    ///
-    /// Note that this is really a minimum absolute fee -- it's possible to
-    /// overshoot it slightly since adding a change output to drain the remaining
-    /// excess might not be viable.
-    pub fn fee_absolute(&mut self, fee_amount: Amount) -> &mut Self {
-        self.params.fee_policy = Some(FeePolicy::FeeAmount(fee_amount));
-        self
-    }
-
-    /// Set the policy path to use while creating the transaction for a given keychain.
-    ///
-    /// This method accepts a map where the key is the policy node id (see
-    /// [`Policy::id`](crate::descriptor::Policy::id)) and the value is the list of the indexes of
-    /// the items that are intended to be satisfied from the policy node (see
-    /// [`SatisfiableItem::Thresh::items`](crate::descriptor::policy::SatisfiableItem::Thresh::items)).
-    ///
-    /// ## Example
-    ///
-    /// An example of when the policy path is needed is the following descriptor:
-    /// `wsh(thresh(2,pk(A),sj:and_v(v:pk(B),n:older(6)),snj:and_v(v:pk(C),after(630000))))`,
-    /// derived from the miniscript policy `thresh(2,pk(A),and(pk(B),older(6)),and(pk(C),after(630000)))`.
-    /// It declares three descriptor fragments, and at the top level it uses `thresh()` to
-    /// ensure that at least two of them are satisfied. The individual fragments are:
-    ///
-    /// 1. `pk(A)`
-    /// 2. `and(pk(B),older(6))`
-    /// 3. `and(pk(C),after(630000))`
-    ///
-    /// When those conditions are combined in pairs, it's clear that the transaction needs to be created
-    /// differently depending on how the user intends to satisfy the policy afterwards:
-    ///
-    /// * If fragments `1` and `2` are used, the transaction will need to use a specific
-    ///   `n_sequence` in order to spend an `OP_CSV` branch.
-    /// * If fragments `1` and `3` are used, the transaction will need to use a specific `locktime`
-    ///   in order to spend an `OP_CLTV` branch.
-    /// * If fragments `2` and `3` are used, the transaction will need both.
-    ///
-    /// When the spending policy is represented as a tree (see
-    /// [`Wallet::policies`](super::Wallet::policies)), every node
-    /// is assigned a unique identifier that can be used in the policy path to specify which of
-    /// the node's children the user intends to satisfy: for instance, assuming the `thresh()`
-    /// root node of this example has an id of `aabbccdd`, the policy path map would look like:
-    ///
-    /// `{ "aabbccdd" => [0, 1] }`
-    ///
-    /// where the key is the node's id, and the value is a list of the children that should be
-    /// used, in no particular order.
-    ///
-    /// If a particularly complex descriptor has multiple ambiguous thresholds in its structure,
-    /// multiple entries can be added to the map, one for each node that requires an explicit path.
-    ///
-    /// ```
-    /// # use std::str::FromStr;
-    /// # use std::collections::BTreeMap;
-    /// # use bitcoin::*;
-    /// # use bdk_wallet::*;
-    /// # let to_address =
-    /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
-    ///     .unwrap()
-    ///     .assume_checked();
-    /// # let mut wallet = doctest_wallet!();
-    /// let mut path = BTreeMap::new();
-    /// path.insert("aabbccdd".to_string(), vec![0, 1]);
-    ///
-    /// let builder = wallet
-    ///     .build_tx()
-    ///     .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
-    ///     .policy_path(path, KeychainKind::External);
-    ///
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    pub fn policy_path(
-        &mut self,
-        policy_path: BTreeMap<String, Vec<usize>>,
-        keychain: KeychainKind,
-    ) -> &mut Self {
-        let to_update = match keychain {
-            KeychainKind::Internal => &mut self.params.internal_policy_path,
-            KeychainKind::External => &mut self.params.external_policy_path,
-        };
-
-        *to_update = Some(policy_path);
-        self
-    }
-
-    /// Add the list of outpoints to the internal list of UTXOs that **must** be spent.
-    ///
-    /// If an error occurs while adding any of the UTXOs then none of them are added and the error is returned.
-    ///
-    /// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
-    /// the "utxos" and the "unspendable" list, it will be spent.
-    pub fn add_utxos(&mut self, outpoints: &[OutPoint]) -> Result<&mut Self, AddUtxoError> {
-        let utxo_batch = outpoints
-            .iter()
-            .map(|outpoint| {
-                self.wallet
-                    .get_utxo(*outpoint)
-                    .ok_or(AddUtxoError::UnknownUtxo(*outpoint))
-                    .map(|output| {
-                        (
-                            *outpoint,
-                            WeightedUtxo {
-                                satisfaction_weight: self
-                                    .wallet
-                                    .public_descriptor(output.keychain)
-                                    .max_weight_to_satisfy()
-                                    .unwrap(),
-                                utxo: Utxo::Local(output),
-                            },
-                        )
-                    })
-            })
-            .collect::<Result<HashMap<OutPoint, WeightedUtxo>, AddUtxoError>>()?;
-        self.params.utxos.extend(utxo_batch);
-
-        Ok(self)
-    }
-
-    /// Add a utxo to the internal list of utxos that **must** be spent
-    ///
-    /// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
-    /// the "utxos" and the "unspendable" list, it will be spent.
-    pub fn add_utxo(&mut self, outpoint: OutPoint) -> Result<&mut Self, AddUtxoError> {
-        self.add_utxos(&[outpoint])
-    }
-
-    /// Add a foreign UTXO i.e. a UTXO not known by this wallet.
-    ///
-    /// There might be cases where the UTxO belongs to the wallet but it doesn't have knowledge of
-    /// it. This is possible if the wallet is not synced or its not being use to track
-    /// transactions. In those cases is the responsibility of the user to add any possible local
-    /// UTxOs through the [`TxBuilder::add_utxo`] method.
-    /// A manually added local UTxO will always have greater precedence than a foreign utxo. No
-    /// matter if it was added before or after the foreign UTxO.
-    ///
-    /// At a minimum to add a foreign UTXO we need:
-    ///
-    /// 1. `outpoint`: To add it to the raw transaction.
-    /// 2. `psbt_input`: To know the value.
-    /// 3. `satisfaction_weight`: To know how much weight/vbytes the input will add to the transaction for fee calculation.
-    ///
-    /// There are several security concerns about adding foreign UTXOs that application
-    /// developers should consider. First, how do you know the value of the input is correct? If a
-    /// `non_witness_utxo` is provided in the `psbt_input` then this method implicitly verifies the
-    /// value by checking it against the transaction. If only a `witness_utxo` is provided then this
-    /// method doesn't verify the value but just takes it as a given -- it is up to you to check
-    /// that whoever sent you the `input_psbt` was not lying!
-    ///
-    /// Secondly, you must somehow provide `satisfaction_weight` of the input. Depending on your
-    /// application it may be important that this be known precisely. If not, a malicious
-    /// counterparty may fool you into putting in a value that is too low, giving the transaction a
-    /// lower than expected feerate. They could also fool you into putting a value that is too high
-    /// causing you to pay a fee that is too high. The party who is broadcasting the transaction can
-    /// of course check the real input weight matches the expected weight prior to broadcasting.
-    ///
-    /// To guarantee the `max_weight_to_satisfy` is correct, you can require the party providing the
-    /// `psbt_input` provide a miniscript descriptor for the input so you can check it against the
-    /// `script_pubkey` and then ask it for the [`max_weight_to_satisfy`].
-    ///
-    /// This is an **EXPERIMENTAL** feature, API and other major changes are expected.
-    ///
-    /// In order to use [`Wallet::calculate_fee`] or [`Wallet::calculate_fee_rate`] for a transaction
-    /// created with foreign UTXO(s) you must manually insert the corresponding TxOut(s) into the tx
-    /// graph using the [`Wallet::insert_txout`] function.
-    ///
-    /// # Errors
-    ///
-    /// This method returns errors in the following circumstances:
-    ///
-    /// 1. The `psbt_input` does not contain a `witness_utxo` or `non_witness_utxo`.
-    /// 2. The data in `non_witness_utxo` does not match what is in `outpoint`.
-    ///
-    /// Note unless you set [`only_witness_utxo`] any non-taproot `psbt_input` you pass to this
-    /// method must have `non_witness_utxo` set otherwise you will get an error when [`finish`]
-    /// is called.
-    ///
-    /// [`only_witness_utxo`]: Self::only_witness_utxo
-    /// [`finish`]: Self::finish
-    /// [`max_weight_to_satisfy`]: miniscript::Descriptor::max_weight_to_satisfy
-    pub fn add_foreign_utxo(
-        &mut self,
-        outpoint: OutPoint,
-        psbt_input: psbt::Input,
-        satisfaction_weight: Weight,
-    ) -> Result<&mut Self, AddForeignUtxoError> {
-        self.add_foreign_utxo_with_sequence(
-            outpoint,
-            psbt_input,
-            satisfaction_weight,
-            Sequence::MAX,
-        )
-    }
-
-    /// Same as [add_foreign_utxo](TxBuilder::add_foreign_utxo) but allows to set the nSequence value.
-    pub fn add_foreign_utxo_with_sequence(
-        &mut self,
-        outpoint: OutPoint,
-        psbt_input: psbt::Input,
-        satisfaction_weight: Weight,
-        sequence: Sequence,
-    ) -> Result<&mut Self, AddForeignUtxoError> {
-        if psbt_input.witness_utxo.is_none() {
-            match psbt_input.non_witness_utxo.as_ref() {
-                Some(tx) => {
-                    if tx.compute_txid() != outpoint.txid {
-                        return Err(AddForeignUtxoError::InvalidTxid {
-                            input_txid: tx.compute_txid(),
-                            foreign_utxo: outpoint,
-                        });
-                    }
-                    if tx.output.len() <= outpoint.vout as usize {
-                        return Err(AddForeignUtxoError::InvalidOutpoint(outpoint));
-                    }
-                }
-                None => {
-                    return Err(AddForeignUtxoError::MissingUtxo);
-                }
-            }
-        }
-
-        if let Some(WeightedUtxo {
-            utxo: Utxo::Local { .. },
-            ..
-        }) = self.params.utxos.get(&outpoint)
-        {
-            None
-        } else {
-            self.params.utxos.insert(
-                outpoint,
-                WeightedUtxo {
-                    satisfaction_weight,
-                    utxo: Utxo::Foreign {
-                        outpoint,
-                        sequence,
-                        psbt_input: Box::new(psbt_input),
-                    },
-                },
-            )
-        };
-
-        Ok(self)
-    }
-
-    /// Only spend utxos added by [`add_utxo`].
-    ///
-    /// The wallet will **not** add additional utxos to the transaction even if they are needed to
-    /// make the transaction valid.
-    ///
-    /// [`add_utxo`]: Self::add_utxo
-    pub fn manually_selected_only(&mut self) -> &mut Self {
-        self.params.manually_selected_only = true;
-        self
-    }
-
-    /// Replace the internal list of unspendable utxos with a new list
-    ///
-    /// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
-    /// have priority over these. See the docs of the two linked methods for more details.
-    pub fn unspendable(&mut self, unspendable: Vec<OutPoint>) -> &mut Self {
-        self.params.unspendable = unspendable.into_iter().collect();
-        self
-    }
-
-    /// Add a utxo to the internal list of unspendable utxos
-    ///
-    /// It's important to note that the "must-be-spent" utxos added with [`TxBuilder::add_utxo`]
-    /// have priority over this. See the docs of the two linked methods for more details.
-    pub fn add_unspendable(&mut self, unspendable: OutPoint) -> &mut Self {
-        self.params.unspendable.insert(unspendable);
-        self
-    }
-
-    /// Sign with a specific sig hash
-    ///
-    /// **Use this option very carefully**
-    pub fn sighash(&mut self, sighash: psbt::PsbtSighashType) -> &mut Self {
-        self.params.sighash = Some(sighash);
-        self
-    }
-
-    /// Choose the ordering for inputs and outputs of the transaction
-    pub fn ordering(&mut self, ordering: TxOrdering) -> &mut Self {
-        self.params.ordering = ordering;
-        self
-    }
-
-    /// Use a specific nLockTime while creating the transaction
-    ///
-    /// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
-    pub fn nlocktime(&mut self, locktime: absolute::LockTime) -> &mut Self {
-        self.params.locktime = Some(locktime);
-        self
-    }
-
-    /// Build a transaction with a specific version
-    ///
-    /// The `version` should always be greater than `0` and greater than `1` if the wallet's
-    /// descriptors contain an "older" (OP_CSV) operator.
-    pub fn version(&mut self, version: i32) -> &mut Self {
-        self.params.version = Some(Version(version));
-        self
-    }
-
-    /// Do not spend change outputs
-    ///
-    /// This effectively adds all the change outputs to the "unspendable" list. See
-    /// [`TxBuilder::unspendable`]. This method assumes the presence of an internal
-    /// keychain, otherwise it has no effect.
-    pub fn do_not_spend_change(&mut self) -> &mut Self {
-        self.params.change_policy = ChangeSpendPolicy::ChangeForbidden;
-        self
-    }
-
-    /// Only spend change outputs
-    ///
-    /// This effectively adds all the non-change outputs to the "unspendable" list. See
-    /// [`TxBuilder::unspendable`]. This method assumes the presence of an internal
-    /// keychain, otherwise it has no effect.
-    pub fn only_spend_change(&mut self) -> &mut Self {
-        self.params.change_policy = ChangeSpendPolicy::OnlyChange;
-        self
-    }
-
-    /// Set a specific [`ChangeSpendPolicy`]. See [`TxBuilder::do_not_spend_change`] and
-    /// [`TxBuilder::only_spend_change`] for some shortcuts. This method assumes the presence
-    /// of an internal keychain, otherwise it has no effect.
-    pub fn change_policy(&mut self, change_policy: ChangeSpendPolicy) -> &mut Self {
-        self.params.change_policy = change_policy;
-        self
-    }
-
-    /// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::psbt::Input::witness_utxo) field when spending from
-    /// SegWit descriptors.
-    ///
-    /// This reduces the size of the PSBT, but some signers might reject them due to the lack of
-    /// the `non_witness_utxo`.
-    pub fn only_witness_utxo(&mut self) -> &mut Self {
-        self.params.only_witness_utxo = true;
-        self
-    }
-
-    /// Fill-in the [`psbt::Output::redeem_script`](bitcoin::psbt::Output::redeem_script) and
-    /// [`psbt::Output::witness_script`](bitcoin::psbt::Output::witness_script) fields.
-    ///
-    /// This is useful for signers which always require it, like ColdCard hardware wallets.
-    pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
-        self.params.include_output_redeem_witness_script = true;
-        self
-    }
-
-    /// Fill-in the `PSBT_GLOBAL_XPUB` field with the extended keys contained in both the external
-    /// and internal descriptors
-    ///
-    /// This is useful for offline signers that take part to a multisig. Some hardware wallets like
-    /// BitBox and ColdCard are known to require this.
-    pub fn add_global_xpubs(&mut self) -> &mut Self {
-        self.params.add_global_xpubs = true;
-        self
-    }
-
-    /// Spend all the available inputs. This respects filters like [`TxBuilder::unspendable`] and the change policy.
-    pub fn drain_wallet(&mut self) -> &mut Self {
-        self.params.drain_wallet = true;
-        self
-    }
-
-    /// Choose the coin selection algorithm
-    ///
-    /// Overrides the [`CoinSelectionAlgorithm`].
-    ///
-    /// Note that this function consumes the builder and returns it so it is usually best to put this as the first call on the builder.
-    pub fn coin_selection<P: CoinSelectionAlgorithm>(self, coin_selection: P) -> TxBuilder<'a, P> {
-        TxBuilder {
-            wallet: self.wallet,
-            params: self.params,
-            coin_selection,
-        }
-    }
-
-    /// Set an exact nSequence value
-    ///
-    /// This can cause conflicts if the wallet's descriptors contain an
-    /// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value.
-    pub fn set_exact_sequence(&mut self, n_sequence: Sequence) -> &mut Self {
-        self.params.sequence = Some(n_sequence);
-        self
-    }
-
-    /// Set the current blockchain height.
-    ///
-    /// This will be used to:
-    /// 1. Set the nLockTime for preventing fee sniping.
-    ///    **Note**: This will be ignored if you manually specify a nlocktime using [`TxBuilder::nlocktime`].
-    /// 2. Decide whether coinbase outputs are mature or not. If the coinbase outputs are not
-    ///    mature at spending height, which is `current_height` + 1, we ignore them in the coin
-    ///    selection. If you want to create a transaction that spends immature coinbase inputs,
-    ///    manually add them using [`TxBuilder::add_utxos`].
-    ///
-    /// In both cases, if you don't provide a current height, we use the last sync height.
-    pub fn current_height(&mut self, height: u32) -> &mut Self {
-        self.params.current_height =
-            Some(absolute::LockTime::from_height(height).expect("Invalid height"));
-        self
-    }
-
-    /// Set whether or not the dust limit is checked.
-    ///
-    /// **Note**: by avoiding a dust limit check you may end up with a transaction that is non-standard.
-    pub fn allow_dust(&mut self, allow_dust: bool) -> &mut Self {
-        self.params.allow_dust = allow_dust;
-        self
-    }
-
-    /// Replace the recipients already added with a new list
-    pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, Amount)>) -> &mut Self {
-        self.params.recipients = recipients;
-        self
-    }
-
-    /// Add a recipient to the internal list
-    pub fn add_recipient(
-        &mut self,
-        script_pubkey: impl Into<ScriptBuf>,
-        amount: Amount,
-    ) -> &mut Self {
-        self.params.recipients.push((script_pubkey.into(), amount));
-        self
-    }
-
-    /// Add data as an output, using OP_RETURN
-    pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self {
-        let script = ScriptBuf::new_op_return(data);
-        self.add_recipient(script, Amount::ZERO);
-        self
-    }
-
-    /// Sets the address to *drain* excess coins to.
-    ///
-    /// Usually, when there are excess coins they are sent to a change address generated by the
-    /// wallet. This option replaces the usual change address with an arbitrary `script_pubkey` of
-    /// your choosing. Just as with a change output, if the drain output is not needed (the excess
-    /// coins are too small) it will not be included in the resulting transaction. The only
-    /// difference is that it is valid to use `drain_to` without setting any ordinary recipients
-    /// with [`add_recipient`] (but it is perfectly fine to add recipients as well).
-    ///
-    /// If you choose not to set any recipients, you should provide the utxos that the
-    /// transaction should spend via [`add_utxos`].
-    ///
-    /// # Example
-    ///
-    /// `drain_to` is very useful for draining all the coins in a wallet with [`drain_wallet`] to a
-    /// single address.
-    ///
-    /// ```
-    /// # use std::str::FromStr;
-    /// # use bitcoin::*;
-    /// # use bdk_wallet::*;
-    /// # use bdk_wallet::ChangeSet;
-    /// # use bdk_wallet::error::CreateTxError;
-    /// # use anyhow::Error;
-    /// # let to_address =
-    /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
-    ///     .unwrap()
-    ///     .assume_checked();
-    /// # let mut wallet = doctest_wallet!();
-    /// let mut tx_builder = wallet.build_tx();
-    ///
-    /// tx_builder
-    ///     // Spend all outputs in this wallet.
-    ///     .drain_wallet()
-    ///     // Send the excess (which is all the coins minus the fee) to this address.
-    ///     .drain_to(to_address.script_pubkey())
-    ///     .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
-    /// let psbt = tx_builder.finish()?;
-    /// # Ok::<(), anyhow::Error>(())
-    /// ```
-    ///
-    /// [`add_recipient`]: Self::add_recipient
-    /// [`add_utxos`]: Self::add_utxos
-    /// [`drain_wallet`]: Self::drain_wallet
-    pub fn drain_to(&mut self, script_pubkey: ScriptBuf) -> &mut Self {
-        self.params.drain_to = Some(script_pubkey);
-        self
-    }
-}
-
-impl<Cs: CoinSelectionAlgorithm> TxBuilder<'_, Cs> {
-    /// Finish building the transaction.
-    ///
-    /// Uses the thread-local random number generator (rng).
-    ///
-    /// Returns a new [`Psbt`] per [`BIP174`].
-    ///
-    /// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
-    ///
-    /// **WARNING**: To avoid change address reuse you must persist the changes resulting from one
-    /// or more calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
-    #[cfg(feature = "std")]
-    pub fn finish(self) -> Result<Psbt, CreateTxError> {
-        self.finish_with_aux_rand(&mut bitcoin::key::rand::thread_rng())
-    }
-
-    /// Finish building the transaction.
-    ///
-    /// Uses a provided random number generator (rng).
-    ///
-    /// Returns a new [`Psbt`] per [`BIP174`].
-    ///
-    /// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
-    ///
-    /// **WARNING**: To avoid change address reuse you must persist the changes resulting from one
-    /// or more calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
-    pub fn finish_with_aux_rand(self, rng: &mut impl RngCore) -> Result<Psbt, CreateTxError> {
-        self.wallet.create_tx(self.coin_selection, self.params, rng)
-    }
-}
-
-#[derive(Debug)]
-/// Error returned from [`TxBuilder::add_utxo`] and [`TxBuilder::add_utxos`]
-pub enum AddUtxoError {
-    /// Happens when trying to spend an UTXO that is not in the internal database
-    UnknownUtxo(OutPoint),
-}
-
-impl fmt::Display for AddUtxoError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::UnknownUtxo(outpoint) => write!(
-                f,
-                "UTXO not found in the internal database for txid: {} with vout: {}",
-                outpoint.txid, outpoint.vout
-            ),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for AddUtxoError {}
-
-#[derive(Debug)]
-/// Error returned from [`TxBuilder::add_foreign_utxo`].
-pub enum AddForeignUtxoError {
-    /// Foreign utxo outpoint txid does not match PSBT input txid
-    InvalidTxid {
-        /// PSBT input txid
-        input_txid: Txid,
-        /// Foreign UTXO outpoint
-        foreign_utxo: OutPoint,
-    },
-    /// Requested outpoint doesn't exist in the tx (vout greater than available outputs)
-    InvalidOutpoint(OutPoint),
-    /// Foreign utxo missing witness_utxo or non_witness_utxo
-    MissingUtxo,
-}
-
-impl fmt::Display for AddForeignUtxoError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::InvalidTxid {
-                input_txid,
-                foreign_utxo,
-            } => write!(
-                f,
-                "Foreign UTXO outpoint txid: {} does not match PSBT input txid: {}",
-                foreign_utxo.txid, input_txid,
-            ),
-            Self::InvalidOutpoint(outpoint) => write!(
-                f,
-                "Requested outpoint doesn't exist for txid: {} with vout: {}",
-                outpoint.txid, outpoint.vout,
-            ),
-            Self::MissingUtxo => write!(f, "Foreign utxo missing witness_utxo or non_witness_utxo"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for AddForeignUtxoError {}
-
-type TxSort<T> = dyn (Fn(&T, &T) -> core::cmp::Ordering) + Send + Sync;
-
-/// Ordering of the transaction's inputs and outputs
-#[derive(Clone, Default)]
-pub enum TxOrdering {
-    /// Randomized (default)
-    #[default]
-    Shuffle,
-    /// Unchanged
-    Untouched,
-    /// Provide custom comparison functions for sorting
-    Custom {
-        /// Transaction inputs sort function
-        input_sort: Arc<TxSort<TxIn>>,
-        /// Transaction outputs sort function
-        output_sort: Arc<TxSort<TxOut>>,
-    },
-}
-
-impl core::fmt::Debug for TxOrdering {
-    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-        match self {
-            TxOrdering::Shuffle => write!(f, "Shuffle"),
-            TxOrdering::Untouched => write!(f, "Untouched"),
-            TxOrdering::Custom { .. } => write!(f, "Custom"),
-        }
-    }
-}
-
-impl TxOrdering {
-    /// Sort transaction inputs and outputs by [`TxOrdering`] variant.
-    ///
-    /// Uses the thread-local random number generator (rng).
-    #[cfg(feature = "std")]
-    pub fn sort_tx(&self, tx: &mut Transaction) {
-        self.sort_tx_with_aux_rand(tx, &mut bitcoin::key::rand::thread_rng())
-    }
-
-    /// Sort transaction inputs and outputs by [`TxOrdering`] variant.
-    ///
-    /// Uses a provided random number generator (rng).
-    pub fn sort_tx_with_aux_rand(&self, tx: &mut Transaction, rng: &mut impl RngCore) {
-        match self {
-            TxOrdering::Untouched => {}
-            TxOrdering::Shuffle => {
-                shuffle_slice(&mut tx.input, rng);
-                shuffle_slice(&mut tx.output, rng);
-            }
-            TxOrdering::Custom {
-                input_sort,
-                output_sort,
-            } => {
-                tx.input.sort_unstable_by(|a, b| input_sort(a, b));
-                tx.output.sort_unstable_by(|a, b| output_sort(a, b));
-            }
-        }
-    }
-}
-
-/// Policy regarding the use of change outputs when creating a transaction
-#[derive(Default, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
-pub enum ChangeSpendPolicy {
-    /// Use both change and non-change outputs (default)
-    #[default]
-    ChangeAllowed,
-    /// Only use change outputs (see [`TxBuilder::only_spend_change`])
-    OnlyChange,
-    /// Only use non-change outputs (see [`TxBuilder::do_not_spend_change`])
-    ChangeForbidden,
-}
-
-impl ChangeSpendPolicy {
-    pub(crate) fn is_satisfied_by(&self, utxo: &LocalOutput) -> bool {
-        match self {
-            ChangeSpendPolicy::ChangeAllowed => true,
-            ChangeSpendPolicy::OnlyChange => utxo.keychain == KeychainKind::Internal,
-            ChangeSpendPolicy::ChangeForbidden => utxo.keychain == KeychainKind::External,
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    const ORDERING_TEST_TX: &str = "0200000003c26f3eb7932f7acddc5ddd26602b77e7516079b03090a16e2c2f54\
-                                    85d1fd600f0100000000ffffffffc26f3eb7932f7acddc5ddd26602b77e75160\
-                                    79b03090a16e2c2f5485d1fd600f0000000000ffffffff571fb3e02278217852\
-                                    dd5d299947e2b7354a639adc32ec1fa7b82cfb5dec530e0500000000ffffffff\
-                                    03e80300000000000002aaeee80300000000000001aa200300000000000001ff\
-                                    00000000";
-    macro_rules! ordering_test_tx {
-        () => {
-            deserialize::<bitcoin::Transaction>(&Vec::<u8>::from_hex(ORDERING_TEST_TX).unwrap())
-                .unwrap()
-        };
-    }
-
-    use bitcoin::consensus::deserialize;
-    use bitcoin::hex::FromHex;
-    use bitcoin::TxOut;
-
-    use super::*;
-    #[test]
-    fn test_output_ordering_untouched() {
-        let original_tx = ordering_test_tx!();
-        let mut tx = original_tx.clone();
-
-        TxOrdering::Untouched.sort_tx(&mut tx);
-
-        assert_eq!(original_tx, tx);
-    }
-
-    #[test]
-    fn test_output_ordering_shuffle() {
-        let original_tx = ordering_test_tx!();
-        let mut tx = original_tx.clone();
-
-        (0..40)
-            .find(|_| {
-                TxOrdering::Shuffle.sort_tx(&mut tx);
-                original_tx.input != tx.input
-            })
-            .expect("it should have moved the inputs at least once");
-
-        let mut tx = original_tx.clone();
-        (0..40)
-            .find(|_| {
-                TxOrdering::Shuffle.sort_tx(&mut tx);
-                original_tx.output != tx.output
-            })
-            .expect("it should have moved the outputs at least once");
-    }
-
-    #[test]
-    fn test_output_ordering_custom_but_bip69() {
-        use core::str::FromStr;
-
-        let original_tx = ordering_test_tx!();
-        let mut tx = original_tx;
-
-        let bip69_txin_cmp = |tx_a: &TxIn, tx_b: &TxIn| {
-            let project_outpoint = |t: &TxIn| (t.previous_output.txid, t.previous_output.vout);
-            project_outpoint(tx_a).cmp(&project_outpoint(tx_b))
-        };
-
-        let bip69_txout_cmp = |tx_a: &TxOut, tx_b: &TxOut| {
-            let project_utxo = |t: &TxOut| (t.value, t.script_pubkey.clone());
-            project_utxo(tx_a).cmp(&project_utxo(tx_b))
-        };
-
-        let custom_bip69_ordering = TxOrdering::Custom {
-            input_sort: Arc::new(bip69_txin_cmp),
-            output_sort: Arc::new(bip69_txout_cmp),
-        };
-
-        custom_bip69_ordering.sort_tx(&mut tx);
-
-        assert_eq!(
-            tx.input[0].previous_output,
-            bitcoin::OutPoint::from_str(
-                "0e53ec5dfb2cb8a71fec32dc9a634a35b7e24799295ddd5278217822e0b31f57:5"
-            )
-            .unwrap()
-        );
-        assert_eq!(
-            tx.input[1].previous_output,
-            bitcoin::OutPoint::from_str(
-                "0f60fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2:0"
-            )
-            .unwrap()
-        );
-        assert_eq!(
-            tx.input[2].previous_output,
-            bitcoin::OutPoint::from_str(
-                "0f60fdd185542f2c6ea19030b0796051e7772b6026dd5ddccd7a2f93b73e6fc2:1"
-            )
-            .unwrap()
-        );
-
-        assert_eq!(tx.output[0].value.to_sat(), 800);
-        assert_eq!(tx.output[1].script_pubkey, ScriptBuf::from(vec![0xAA]));
-        assert_eq!(
-            tx.output[2].script_pubkey,
-            ScriptBuf::from(vec![0xAA, 0xEE])
-        );
-    }
-
-    #[test]
-    fn test_output_ordering_custom_with_sha256() {
-        use bitcoin::hashes::{sha256, Hash};
-
-        let original_tx = ordering_test_tx!();
-        let mut tx_1 = original_tx.clone();
-        let mut tx_2 = original_tx.clone();
-        let shared_secret = "secret_tweak";
-
-        let hash_txin_with_shared_secret_seed = Arc::new(|tx_a: &TxIn, tx_b: &TxIn| {
-            let secret_digest_from_txin = |txin: &TxIn| {
-                sha256::Hash::hash(
-                    &[
-                        &txin.previous_output.txid.to_raw_hash()[..],
-                        &txin.previous_output.vout.to_be_bytes(),
-                        shared_secret.as_bytes(),
-                    ]
-                    .concat(),
-                )
-            };
-            secret_digest_from_txin(tx_a).cmp(&secret_digest_from_txin(tx_b))
-        });
-
-        let hash_txout_with_shared_secret_seed = Arc::new(|tx_a: &TxOut, tx_b: &TxOut| {
-            let secret_digest_from_txout = |txin: &TxOut| {
-                sha256::Hash::hash(
-                    &[
-                        &txin.value.to_sat().to_be_bytes(),
-                        &txin.script_pubkey.clone().into_bytes()[..],
-                        shared_secret.as_bytes(),
-                    ]
-                    .concat(),
-                )
-            };
-            secret_digest_from_txout(tx_a).cmp(&secret_digest_from_txout(tx_b))
-        });
-
-        let custom_ordering_from_salted_sha256_1 = TxOrdering::Custom {
-            input_sort: hash_txin_with_shared_secret_seed.clone(),
-            output_sort: hash_txout_with_shared_secret_seed.clone(),
-        };
-
-        let custom_ordering_from_salted_sha256_2 = TxOrdering::Custom {
-            input_sort: hash_txin_with_shared_secret_seed,
-            output_sort: hash_txout_with_shared_secret_seed,
-        };
-
-        custom_ordering_from_salted_sha256_1.sort_tx(&mut tx_1);
-        custom_ordering_from_salted_sha256_2.sort_tx(&mut tx_2);
-
-        // Check the ordering is consistent between calls
-        assert_eq!(tx_1, tx_2);
-        // Check transaction order has changed
-        assert_ne!(tx_1, original_tx);
-        assert_ne!(tx_2, original_tx);
-    }
-
-    fn get_test_utxos() -> Vec<LocalOutput> {
-        use bitcoin::hashes::Hash;
-
-        vec![
-            LocalOutput {
-                outpoint: OutPoint {
-                    txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
-                    vout: 0,
-                },
-                txout: TxOut::NULL,
-                keychain: KeychainKind::External,
-                is_spent: false,
-                chain_position: chain::ChainPosition::Unconfirmed { last_seen: Some(0) },
-                derivation_index: 0,
-            },
-            LocalOutput {
-                outpoint: OutPoint {
-                    txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
-                    vout: 1,
-                },
-                txout: TxOut::NULL,
-                keychain: KeychainKind::Internal,
-                is_spent: false,
-                chain_position: chain::ChainPosition::Confirmed {
-                    anchor: chain::ConfirmationBlockTime {
-                        block_id: chain::BlockId {
-                            height: 32,
-                            hash: bitcoin::BlockHash::all_zeros(),
-                        },
-                        confirmation_time: 42,
-                    },
-                    transitively: None,
-                },
-                derivation_index: 1,
-            },
-        ]
-    }
-
-    #[test]
-    fn test_change_spend_policy_default() {
-        let change_spend_policy = ChangeSpendPolicy::default();
-        let filtered = get_test_utxos()
-            .into_iter()
-            .filter(|u| change_spend_policy.is_satisfied_by(u))
-            .count();
-
-        assert_eq!(filtered, 2);
-    }
-
-    #[test]
-    fn test_change_spend_policy_no_internal() {
-        let change_spend_policy = ChangeSpendPolicy::ChangeForbidden;
-        let filtered = get_test_utxos()
-            .into_iter()
-            .filter(|u| change_spend_policy.is_satisfied_by(u))
-            .collect::<Vec<_>>();
-
-        assert_eq!(filtered.len(), 1);
-        assert_eq!(filtered[0].keychain, KeychainKind::External);
-    }
-
-    #[test]
-    fn test_change_spend_policy_only_internal() {
-        let change_spend_policy = ChangeSpendPolicy::OnlyChange;
-        let filtered = get_test_utxos()
-            .into_iter()
-            .filter(|u| change_spend_policy.is_satisfied_by(u))
-            .collect::<Vec<_>>();
-
-        assert_eq!(filtered.len(), 1);
-        assert_eq!(filtered[0].keychain, KeychainKind::Internal);
-    }
-
-    #[test]
-    fn test_build_fee_bump_remove_change_output_single_desc() {
-        use crate::test_utils::*;
-        use bdk_chain::BlockId;
-        use bitcoin::{hashes::Hash, BlockHash, Network};
-
-        let mut wallet = Wallet::create_single(get_test_tr_single_sig())
-            .network(Network::Regtest)
-            .create_wallet_no_persist()
-            .unwrap();
-
-        insert_checkpoint(
-            &mut wallet,
-            BlockId {
-                height: 1,
-                hash: BlockHash::all_zeros(),
-            },
-        );
-
-        receive_output_in_latest_block(&mut wallet, Amount::ONE_BTC.to_sat());
-
-        // tx1 sending 15k sat to a recipient
-        let recip = ScriptBuf::from_hex(
-            "5120e8f5c4dc2f5d6a7595e7b108cb063da9c7550312da1e22875d78b9db62b59cd5",
-        )
-        .unwrap();
-        let mut builder = wallet.build_tx();
-        builder.add_recipient(recip.clone(), Amount::from_sat(15_000));
-        builder.fee_absolute(Amount::from_sat(1_000));
-        let psbt = builder.finish().unwrap();
-
-        let tx = psbt.extract_tx().unwrap();
-        let txid = tx.compute_txid();
-        let feerate = wallet.calculate_fee_rate(&tx).unwrap().to_sat_per_kwu();
-        insert_tx(&mut wallet, tx);
-
-        // build fee bump
-        let mut builder = wallet.build_fee_bump(txid).unwrap();
-        assert_eq!(
-            builder.params.recipients,
-            vec![(recip, Amount::from_sat(15_000))]
-        );
-        builder.fee_rate(FeeRate::from_sat_per_kwu(feerate + 250));
-        let _ = builder.finish().unwrap();
-    }
-
-    #[test]
-    fn not_duplicated_utxos_in_required_list() {
-        let mut params = TxParams::default();
-        let test_utxos = get_test_utxos();
-        let fake_weighted_utxo = WeightedUtxo {
-            satisfaction_weight: Weight::from_wu(0),
-            utxo: Utxo::Local(test_utxos[0].clone()),
-        };
-        for _ in 0..3 {
-            params
-                .utxos
-                .insert(test_utxos[0].outpoint, fake_weighted_utxo.clone());
-        }
-        assert_eq!(
-            vec![(test_utxos[0].outpoint, fake_weighted_utxo)],
-            params.utxos.into_iter().collect::<Vec<_>>()
-        );
-    }
-
-    #[test]
-    fn not_duplicated_foreign_utxos_with_same_outpoint_but_different_weight() {
-        use crate::test_utils::{get_funded_wallet_single, get_funded_wallet_wpkh, get_test_wpkh};
-
-        // Use two different wallets to avoid adding local utxos
-        let (wallet1, txid1) = get_funded_wallet_wpkh();
-        let (mut wallet2, txid2) = get_funded_wallet_single(get_test_wpkh());
-
-        // if the transactions were produced by the same wallet the following assert should fail
-        assert_ne!(txid1, txid2);
-
-        let utxo1 = wallet1.list_unspent().next().unwrap();
-        let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.tx.clone();
-
-        let satisfaction_weight = wallet1
-            .public_descriptor(KeychainKind::External)
-            .max_weight_to_satisfy()
-            .unwrap();
-
-        let mut builder = wallet2.build_tx();
-
-        // add foreign utxo with satisfaction weight x
-        assert!(builder
-            .add_foreign_utxo(
-                utxo1.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx1.as_ref().clone()),
-                    ..Default::default()
-                },
-                satisfaction_weight,
-            )
-            .is_ok());
-
-        let modified_satisfaction_weight = satisfaction_weight - Weight::from_wu(6);
-
-        assert_ne!(satisfaction_weight, modified_satisfaction_weight);
-
-        // add foreign utxo with same outpoint but satisfaction weight x - 6wu
-        assert!(builder
-            .add_foreign_utxo(
-                utxo1.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx1.as_ref().clone()),
-                    ..Default::default()
-                },
-                modified_satisfaction_weight,
-            )
-            .is_ok());
-
-        let foreign_utxo_with_modified_weight =
-            builder.params.utxos.values().collect::<Vec<_>>()[0];
-
-        assert_eq!(builder.params.utxos.len(), 1);
-        assert_eq!(
-            foreign_utxo_with_modified_weight.satisfaction_weight,
-            modified_satisfaction_weight
-        );
-    }
-
-    #[test]
-    fn test_prexisting_local_utxo_have_precedence_over_foreign_utxo_with_same_outpoint() {
-        // In this test we are assuming a setup where there are two wallets using the same
-        // descriptor, but only one is tracking transactions, while the other is not.
-        // Within this conditions we want the second wallet to be able to consider the unknown
-        // LocalOutputs provided by the first wallet with greater precedence than any foreign utxo,
-        // even if the foreign utxo shares the same outpoint Remember the second wallet does not
-        // know about any UTxOs, so in principle, an unknown local utxo could be added as foreign.
-        //
-        // In this case, somehow the wallet has knowledge of one local utxo and it tries to add the
-        // same utxo as a foreign one, but the API ignores this, because local utxos have higher
-        // precedence.
-        use crate::test_utils::{get_funded_wallet_wpkh, get_test_wpkh_and_change_desc};
-        use bitcoin::Network;
-
-        // Use the same wallet twice
-        let (wallet1, txid1) = get_funded_wallet_wpkh();
-        // But the second one has no knowledge of tx associated with txid1
-        let (main_descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
-        let mut wallet2 = Wallet::create(main_descriptor, change_descriptor)
-            .network(Network::Regtest)
-            .create_wallet_no_persist()
-            .expect("descriptors must be valid");
-
-        let utxo1 = wallet1.list_unspent().next().unwrap();
-        let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.tx.clone();
-
-        let satisfaction_weight = wallet1
-            .public_descriptor(KeychainKind::External)
-            .max_weight_to_satisfy()
-            .unwrap();
-
-        let mut builder = wallet2.build_tx();
-
-        // Add local UTxO manually, through tx_builder private parameters and not through
-        // add_utxo method because we are assuming wallet2 has not knowledge of utxo1 yet
-        builder.params.utxos.insert(
-            utxo1.outpoint,
-            WeightedUtxo {
-                satisfaction_weight: wallet1
-                    .public_descriptor(utxo1.keychain)
-                    .max_weight_to_satisfy()
-                    .unwrap(),
-                utxo: Utxo::Local(utxo1.clone()),
-            },
-        );
-
-        // add foreign utxo
-        assert!(builder
-            .add_foreign_utxo(
-                utxo1.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx1.as_ref().clone()),
-                    ..Default::default()
-                },
-                satisfaction_weight,
-            )
-            .is_ok());
-
-        let utxo_should_still_be_local = builder.params.utxos.values().collect::<Vec<_>>()[0];
-
-        assert_eq!(builder.params.utxos.len(), 1);
-        assert_eq!(utxo_should_still_be_local.utxo.outpoint(), utxo1.outpoint);
-        // UTxO should still be LocalOutput
-        assert!(matches!(
-            utxo_should_still_be_local,
-            WeightedUtxo {
-                utxo: Utxo::Local(..),
-                ..
-            }
-        ));
-    }
-
-    #[test]
-    fn test_prexisting_foreign_utxo_have_no_precedence_over_local_utxo_with_same_outpoint() {
-        // In this test we are assuming a setup where there are two wallets using the same
-        // descriptor, but only one is tracking transactions, while the other is not.
-        // Within this conditions we want the second wallet to be able to consider the unknown
-        // LocalOutputs provided by the first wallet with greater precedence than any foreign utxo,
-        // even if the foreign utxo shares the same outpoint Remember the second wallet does not
-        // know about any UTxOs, so in principle, an unknown local utxo could be added as foreign.
-        //
-        // In this case, the wallet adds a local utxo as if it were foreign and after this it adds
-        // it as local utxo. In this case the local utxo should still have precedence over the
-        // foreign utxo.
-        use crate::test_utils::{get_funded_wallet_wpkh, get_test_wpkh_and_change_desc};
-        use bitcoin::Network;
-
-        // Use the same wallet twice
-        let (wallet1, txid1) = get_funded_wallet_wpkh();
-        // But the second one has no knowledge of tx associated with txid1
-        let (main_descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
-        let mut wallet2 = Wallet::create(main_descriptor, change_descriptor)
-            .network(Network::Regtest)
-            .create_wallet_no_persist()
-            .expect("descriptors must be valid");
-
-        let utxo1 = wallet1.list_unspent().next().unwrap();
-        let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.tx.clone();
-
-        let satisfaction_weight = wallet1
-            .public_descriptor(KeychainKind::External)
-            .max_weight_to_satisfy()
-            .unwrap();
-
-        let mut builder = wallet2.build_tx();
-
-        // add foreign utxo
-        assert!(builder
-            .add_foreign_utxo(
-                utxo1.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx1.as_ref().clone()),
-                    ..Default::default()
-                },
-                satisfaction_weight,
-            )
-            .is_ok());
-
-        // Add local UTxO manually, through tx_builder private parameters and not through
-        // add_utxo method because we are assuming wallet2 has not knowledge of utxo1 yet
-        builder.params.utxos.insert(
-            utxo1.outpoint,
-            WeightedUtxo {
-                satisfaction_weight: wallet1
-                    .public_descriptor(utxo1.keychain)
-                    .max_weight_to_satisfy()
-                    .unwrap(),
-                utxo: Utxo::Local(utxo1.clone()),
-            },
-        );
-
-        let utxo_should_still_be_local = builder.params.utxos.values().collect::<Vec<_>>()[0];
-
-        assert_eq!(builder.params.utxos.len(), 1);
-        assert_eq!(utxo_should_still_be_local.utxo.outpoint(), utxo1.outpoint);
-        // UTxO should still be LocalOutput
-        assert!(matches!(
-            utxo_should_still_be_local,
-            WeightedUtxo {
-                utxo: Utxo::Local(..),
-                ..
-            }
-        ));
-    }
-}
diff --git a/crates/wallet/src/wallet/utils.rs b/crates/wallet/src/wallet/utils.rs
deleted file mode 100644 (file)
index 76d0aaa..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-use bitcoin::secp256k1::{All, Secp256k1};
-use bitcoin::{absolute, relative, Amount, Script, Sequence};
-
-use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
-
-use rand_core::RngCore;
-
-/// Trait to check if a value is below the dust limit.
-/// We are performing dust value calculation for a given script public key using rust-bitcoin to
-/// keep it compatible with network dust rate
-// we implement this trait to make sure we don't mess up the comparison with off-by-one like a <
-// instead of a <= etc.
-pub trait IsDust {
-    /// Check whether or not a value is below dust limit
-    fn is_dust(&self, script: &Script) -> bool;
-}
-
-impl IsDust for Amount {
-    fn is_dust(&self, script: &Script) -> bool {
-        *self < script.minimal_non_dust()
-    }
-}
-
-impl IsDust for u64 {
-    fn is_dust(&self, script: &Script) -> bool {
-        Amount::from_sat(*self).is_dust(script)
-    }
-}
-
-pub struct After {
-    pub current_height: Option<u32>,
-    pub assume_height_reached: bool,
-}
-
-impl After {
-    pub(crate) fn new(current_height: Option<u32>, assume_height_reached: bool) -> After {
-        After {
-            current_height,
-            assume_height_reached,
-        }
-    }
-}
-
-pub(crate) fn check_nsequence_rbf(sequence: Sequence, csv: Sequence) -> bool {
-    // The nSequence value must enable relative timelocks
-    if !sequence.is_relative_lock_time() {
-        return false;
-    }
-
-    // Both values should be represented in the same unit (either time-based or
-    // block-height based)
-    if sequence.is_time_locked() != csv.is_time_locked() {
-        return false;
-    }
-
-    // The value should be at least `csv`
-    if sequence < csv {
-        return false;
-    }
-
-    true
-}
-
-impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
-    fn check_after(&self, n: absolute::LockTime) -> bool {
-        if let Some(current_height) = self.current_height {
-            current_height >= n.to_consensus_u32()
-        } else {
-            self.assume_height_reached
-        }
-    }
-}
-
-pub struct Older {
-    pub current_height: Option<u32>,
-    pub create_height: Option<u32>,
-    pub assume_height_reached: bool,
-}
-
-impl Older {
-    pub(crate) fn new(
-        current_height: Option<u32>,
-        create_height: Option<u32>,
-        assume_height_reached: bool,
-    ) -> Older {
-        Older {
-            current_height,
-            create_height,
-            assume_height_reached,
-        }
-    }
-}
-
-impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
-    fn check_older(&self, n: relative::LockTime) -> bool {
-        if let Some(current_height) = self.current_height {
-            // TODO: test >= / >
-            current_height
-                >= self
-                    .create_height
-                    .unwrap_or(0)
-                    .checked_add(n.to_consensus_u32())
-                    .expect("Overflowing addition")
-        } else {
-            self.assume_height_reached
-        }
-    }
-}
-
-// The Knuth shuffling algorithm based on the original [Fisher-Yates method](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
-pub(crate) fn shuffle_slice<T>(list: &mut [T], rng: &mut impl RngCore) {
-    if list.is_empty() {
-        return;
-    }
-    let mut current_index = list.len() - 1;
-    while current_index > 0 {
-        let random_index = rng.next_u32() as usize % (current_index + 1);
-        list.swap(current_index, random_index);
-        current_index -= 1;
-    }
-}
-
-pub(crate) type SecpCtx = Secp256k1<All>;
-
-#[cfg(test)]
-mod test {
-    // When nSequence is lower than this flag the timelock is interpreted as block-height-based,
-    // otherwise it's time-based
-    pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
-
-    use super::{check_nsequence_rbf, shuffle_slice, IsDust};
-    use crate::bitcoin::{Address, Network, Sequence};
-    use alloc::vec::Vec;
-    use core::str::FromStr;
-    use rand::{rngs::StdRng, thread_rng, SeedableRng};
-
-    #[test]
-    fn test_is_dust() {
-        let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
-            .unwrap()
-            .require_network(Network::Bitcoin)
-            .unwrap()
-            .script_pubkey();
-        assert!(script_p2pkh.is_p2pkh());
-        assert!(545.is_dust(&script_p2pkh));
-        assert!(!546.is_dust(&script_p2pkh));
-
-        let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
-            .unwrap()
-            .require_network(Network::Bitcoin)
-            .unwrap()
-            .script_pubkey();
-        assert!(script_p2wpkh.is_p2wpkh());
-        assert!(293.is_dust(&script_p2wpkh));
-        assert!(!294.is_dust(&script_p2wpkh));
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_msb_set() {
-        let result = check_nsequence_rbf(Sequence(0x80000000), Sequence(5000));
-        assert!(!result);
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_lt_csv() {
-        let result = check_nsequence_rbf(Sequence(4000), Sequence(5000));
-        assert!(!result);
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_different_unit() {
-        let result =
-            check_nsequence_rbf(Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000), Sequence(5000));
-        assert!(!result);
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_mask() {
-        let result = check_nsequence_rbf(Sequence(0x3f + 10_000), Sequence(5000));
-        assert!(result);
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_same_unit_blocks() {
-        let result = check_nsequence_rbf(Sequence(10_000), Sequence(5000));
-        assert!(result);
-    }
-
-    #[test]
-    fn test_check_nsequence_rbf_same_unit_time() {
-        let result = check_nsequence_rbf(
-            Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 10_000),
-            Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000),
-        );
-        assert!(result);
-    }
-
-    #[test]
-    #[cfg(feature = "std")]
-    fn test_shuffle_slice_empty_vec() {
-        let mut test: Vec<u8> = vec![];
-        shuffle_slice(&mut test, &mut thread_rng());
-    }
-
-    #[test]
-    #[cfg(feature = "std")]
-    fn test_shuffle_slice_single_vec() {
-        let mut test: Vec<u8> = vec![0];
-        shuffle_slice(&mut test, &mut thread_rng());
-    }
-
-    #[test]
-    fn test_shuffle_slice_duple_vec() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let mut test: Vec<u8> = vec![0, 1];
-        shuffle_slice(&mut test, &mut rng);
-        assert_eq!(test, &[0, 1]);
-        let seed = [6; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let mut test: Vec<u8> = vec![0, 1];
-        shuffle_slice(&mut test, &mut rng);
-        assert_eq!(test, &[1, 0]);
-    }
-
-    #[test]
-    fn test_shuffle_slice_multi_vec() {
-        let seed = [0; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
-        shuffle_slice(&mut test, &mut rng);
-        assert_eq!(test, &[2, 1, 0, 4, 5]);
-        let seed = [25; 32];
-        let mut rng: StdRng = SeedableRng::from_seed(seed);
-        let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
-        shuffle_slice(&mut test, &mut rng);
-        assert_eq!(test, &[0, 4, 1, 2, 5]);
-    }
-}
diff --git a/crates/wallet/tests/psbt.rs b/crates/wallet/tests/psbt.rs
deleted file mode 100644 (file)
index a4d1749..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, TxIn};
-use bdk_wallet::test_utils::*;
-use bdk_wallet::{psbt, KeychainKind, SignOptions};
-use core::str::FromStr;
-
-// from bip 174
-const PSBT_STR: &str = "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA";
-
-#[test]
-#[should_panic(expected = "InputIndexOutOfRange")]
-fn test_psbt_malformed_psbt_input_legacy() {
-    let psbt_bip = Psbt::from_str(PSBT_STR).unwrap();
-    let (mut wallet, _) = get_funded_wallet_single(get_test_wpkh());
-    let send_to = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
-    let mut psbt = builder.finish().unwrap();
-    psbt.inputs.push(psbt_bip.inputs[0].clone());
-    let options = SignOptions {
-        trust_witness_utxo: true,
-        ..Default::default()
-    };
-    let _ = wallet.sign(&mut psbt, options).unwrap();
-}
-
-#[test]
-#[should_panic(expected = "InputIndexOutOfRange")]
-fn test_psbt_malformed_psbt_input_segwit() {
-    let psbt_bip = Psbt::from_str(PSBT_STR).unwrap();
-    let (mut wallet, _) = get_funded_wallet_single(get_test_wpkh());
-    let send_to = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
-    let mut psbt = builder.finish().unwrap();
-    psbt.inputs.push(psbt_bip.inputs[1].clone());
-    let options = SignOptions {
-        trust_witness_utxo: true,
-        ..Default::default()
-    };
-    let _ = wallet.sign(&mut psbt, options).unwrap();
-}
-
-#[test]
-#[should_panic(expected = "InputIndexOutOfRange")]
-fn test_psbt_malformed_tx_input() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_wpkh());
-    let send_to = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
-    let mut psbt = builder.finish().unwrap();
-    psbt.unsigned_tx.input.push(TxIn::default());
-    let options = SignOptions {
-        trust_witness_utxo: true,
-        ..Default::default()
-    };
-    let _ = wallet.sign(&mut psbt, options).unwrap();
-}
-
-#[test]
-fn test_psbt_sign_with_finalized() {
-    let psbt_bip = Psbt::from_str(PSBT_STR).unwrap();
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let send_to = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
-    let mut psbt = builder.finish().unwrap();
-
-    // add a finalized input
-    psbt.inputs.push(psbt_bip.inputs[0].clone());
-    psbt.unsigned_tx
-        .input
-        .push(psbt_bip.unsigned_tx.input[0].clone());
-
-    let _ = wallet.sign(&mut psbt, SignOptions::default()).unwrap();
-}
-
-#[test]
-fn test_psbt_fee_rate_with_witness_utxo() {
-    use psbt::PsbtUtils;
-
-    let expected_fee_rate = FeeRate::from_sat_per_kwu(310);
-
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(expected_fee_rate);
-    let mut psbt = builder.finish().unwrap();
-    let fee_amount = psbt.fee_amount();
-    assert!(fee_amount.is_some());
-
-    let unfinalized_fee_rate = psbt.fee_rate().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let finalized_fee_rate = psbt.fee_rate().unwrap();
-    assert!(finalized_fee_rate >= expected_fee_rate);
-    assert!(finalized_fee_rate < unfinalized_fee_rate);
-}
-
-#[test]
-fn test_psbt_fee_rate_with_nonwitness_utxo() {
-    use psbt::PsbtUtils;
-
-    let expected_fee_rate = FeeRate::from_sat_per_kwu(310);
-
-    let (mut wallet, _) = get_funded_wallet_single("pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(expected_fee_rate);
-    let mut psbt = builder.finish().unwrap();
-    let fee_amount = psbt.fee_amount();
-    assert!(fee_amount.is_some());
-    let unfinalized_fee_rate = psbt.fee_rate().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let finalized_fee_rate = psbt.fee_rate().unwrap();
-    assert!(finalized_fee_rate >= expected_fee_rate);
-    assert!(finalized_fee_rate < unfinalized_fee_rate);
-}
-
-#[test]
-fn test_psbt_fee_rate_with_missing_txout() {
-    use psbt::PsbtUtils;
-
-    let expected_fee_rate = FeeRate::from_sat_per_kwu(310);
-
-    let (mut wpkh_wallet,  _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wpkh_wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wpkh_wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(expected_fee_rate);
-    let mut wpkh_psbt = builder.finish().unwrap();
-
-    wpkh_psbt.inputs[0].witness_utxo = None;
-    wpkh_psbt.inputs[0].non_witness_utxo = None;
-    assert!(wpkh_psbt.fee_amount().is_none());
-    assert!(wpkh_psbt.fee_rate().is_none());
-
-    let desc = "pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/0)";
-    let change_desc = "pkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/1)";
-    let (mut pkh_wallet, _) = get_funded_wallet(desc, change_desc);
-    let addr = pkh_wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = pkh_wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    builder.fee_rate(expected_fee_rate);
-    let mut pkh_psbt = builder.finish().unwrap();
-
-    pkh_psbt.inputs[0].non_witness_utxo = None;
-    assert!(pkh_psbt.fee_amount().is_none());
-    assert!(pkh_psbt.fee_rate().is_none());
-}
-
-#[test]
-fn test_psbt_multiple_internalkey_signers() {
-    use bdk_wallet::signer::{SignerContext, SignerOrdering, SignerWrapper};
-    use bdk_wallet::KeychainKind;
-    use bitcoin::key::TapTweak;
-    use bitcoin::secp256k1::{schnorr, Keypair, Message, Secp256k1, XOnlyPublicKey};
-    use bitcoin::sighash::{Prevouts, SighashCache, TapSighashType};
-    use bitcoin::{PrivateKey, TxOut};
-    use std::sync::Arc;
-
-    let secp = Secp256k1::new();
-    let wif = "cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG";
-    let desc = format!("tr({})", wif);
-    let prv = PrivateKey::from_wif(wif).unwrap();
-    let keypair = Keypair::from_secret_key(&secp, &prv.inner);
-
-    let change_desc = "tr(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
-    let (mut wallet, _) = get_funded_wallet(&desc, change_desc);
-    let to_spend = wallet.balance().total();
-    let send_to = wallet.peek_address(KeychainKind::External, 0);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(send_to.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-    let unsigned_tx = psbt.unsigned_tx.clone();
-
-    // Adds a signer for the wrong internal key, bdk should not use this key to sign
-    wallet.add_signer(
-        KeychainKind::External,
-        // A signerordering lower than 100, bdk will use this signer first
-        SignerOrdering(0),
-        Arc::new(SignerWrapper::new(
-            PrivateKey::from_wif("5J5PZqvCe1uThJ3FZeUUFLCh2FuK9pZhtEK4MzhNmugqTmxCdwE").unwrap(),
-            SignerContext::Tap {
-                is_internal_key: true,
-            },
-        )),
-    );
-    let finalized = wallet.sign(&mut psbt, SignOptions::default()).unwrap();
-    assert!(finalized);
-
-    // To verify, we need the signature, message, and pubkey
-    let witness = psbt.inputs[0].final_script_witness.as_ref().unwrap();
-    assert!(!witness.is_empty());
-    let signature = schnorr::Signature::from_slice(witness.iter().next().unwrap()).unwrap();
-
-    // the prevout we're spending
-    let prevouts = &[TxOut {
-        script_pubkey: send_to.script_pubkey(),
-        value: to_spend,
-    }];
-    let prevouts = Prevouts::All(prevouts);
-    let input_index = 0;
-    let mut sighash_cache = SighashCache::new(unsigned_tx);
-    let sighash = sighash_cache
-        .taproot_key_spend_signature_hash(input_index, &prevouts, TapSighashType::Default)
-        .unwrap();
-    let message = Message::from(sighash);
-
-    // add tweak. this was taken from `signer::sign_psbt_schnorr`
-    let keypair = keypair.tap_tweak(&secp, None).to_inner();
-    let (xonlykey, _parity) = XOnlyPublicKey::from_keypair(&keypair);
-
-    // Must verify if we used the correct key to sign
-    let verify_res = secp.verify_schnorr(&signature, &message, &xonlykey);
-    assert!(verify_res.is_ok(), "The wrong internal key was used");
-}
diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs
deleted file mode 100644 (file)
index 35cbd85..0000000
+++ /dev/null
@@ -1,4308 +0,0 @@
-use std::path::Path;
-use std::str::FromStr;
-use std::sync::Arc;
-
-use anyhow::Context;
-use assert_matches::assert_matches;
-use bdk_chain::{BlockId, ChainPosition, ConfirmationBlockTime};
-use bdk_wallet::coin_selection::{self, LargestFirstCoinSelection};
-use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
-use bdk_wallet::error::CreateTxError;
-use bdk_wallet::psbt::PsbtUtils;
-use bdk_wallet::signer::{SignOptions, SignerError};
-use bdk_wallet::test_utils::*;
-use bdk_wallet::tx_builder::AddForeignUtxoError;
-use bdk_wallet::{
-    AddressInfo, Balance, ChangeSet, PersistedWallet, Update, Wallet, WalletPersister, WalletTx,
-};
-use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
-use bitcoin::constants::{ChainHash, COINBASE_MATURITY};
-use bitcoin::hashes::Hash;
-use bitcoin::key::Secp256k1;
-use bitcoin::psbt;
-use bitcoin::script::PushBytesBuf;
-use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
-use bitcoin::taproot::TapNodeHash;
-use bitcoin::{
-    absolute, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, ScriptBuf,
-    Sequence, Transaction, TxIn, TxOut, Txid, Weight,
-};
-use miniscript::{descriptor::KeyMap, Descriptor, DescriptorPublicKey};
-use rand::rngs::StdRng;
-use rand::SeedableRng;
-
-fn parse_descriptor(s: &str) -> (Descriptor<DescriptorPublicKey>, KeyMap) {
-    <Descriptor<DescriptorPublicKey>>::parse_descriptor(&Secp256k1::new(), s)
-        .expect("failed to parse descriptor")
-}
-
-// The satisfaction size of a P2WPKH is 112 WU =
-// 1 (elements in witness) + 1 (OP_PUSH) + 33 (pk) + 1 (OP_PUSH) + 72 (signature + sighash) + 1*4 (script len)
-// On the witness itself, we have to push once for the pk (33WU) and once for signature + sighash (72WU), for
-// a total of 105 WU.
-// Here, we push just once for simplicity, so we have to add an extra byte for the missing
-// OP_PUSH.
-const P2WPKH_FAKE_WITNESS_SIZE: usize = 106;
-
-const DB_MAGIC: &[u8] = &[0x21, 0x24, 0x48];
-
-#[test]
-fn wallet_is_persisted() -> anyhow::Result<()> {
-    fn run<Db, CreateDb, OpenDb>(
-        filename: &str,
-        create_db: CreateDb,
-        open_db: OpenDb,
-    ) -> anyhow::Result<()>
-    where
-        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
-        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
-        Db: WalletPersister,
-        Db::Error: std::error::Error + Send + Sync + 'static,
-    {
-        let temp_dir = tempfile::tempdir().expect("must create tempdir");
-        let file_path = temp_dir.path().join(filename);
-        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-
-        // create new wallet
-        let wallet_spk_index = {
-            let mut db = create_db(&file_path)?;
-            let mut wallet = Wallet::create(external_desc, internal_desc)
-                .network(Network::Testnet)
-                .create_wallet(&mut db)?;
-            wallet.reveal_next_address(KeychainKind::External);
-
-            // persist new wallet changes
-            assert!(wallet.persist(&mut db)?, "must write");
-            wallet.spk_index().clone()
-        };
-
-        // recover wallet
-        {
-            let mut db = open_db(&file_path).context("failed to recover db")?;
-            let _ = Wallet::load()
-                .check_network(Network::Testnet)
-                .load_wallet(&mut db)?
-                .expect("wallet must exist");
-        }
-        {
-            let mut db = open_db(&file_path).context("failed to recover db")?;
-            let wallet = Wallet::load()
-                .descriptor(KeychainKind::External, Some(external_desc))
-                .descriptor(KeychainKind::Internal, Some(internal_desc))
-                .check_network(Network::Testnet)
-                .load_wallet(&mut db)?
-                .expect("wallet must exist");
-
-            assert_eq!(wallet.network(), Network::Testnet);
-            assert_eq!(
-                wallet.spk_index().keychains().collect::<Vec<_>>(),
-                wallet_spk_index.keychains().collect::<Vec<_>>()
-            );
-            assert_eq!(
-                wallet.spk_index().last_revealed_indices(),
-                wallet_spk_index.last_revealed_indices()
-            );
-            let secp = Secp256k1::new();
-            assert_eq!(
-                *wallet.public_descriptor(KeychainKind::External),
-                external_desc
-                    .into_wallet_descriptor(&secp, wallet.network())
-                    .unwrap()
-                    .0
-            );
-        }
-
-        Ok(())
-    }
-
-    run(
-        "store.db",
-        |path| Ok(bdk_file_store::Store::create_new(DB_MAGIC, path)?),
-        |path| Ok(bdk_file_store::Store::open(DB_MAGIC, path)?),
-    )?;
-    run::<bdk_chain::rusqlite::Connection, _, _>(
-        "store.sqlite",
-        |path| Ok(bdk_chain::rusqlite::Connection::open(path)?),
-        |path| Ok(bdk_chain::rusqlite::Connection::open(path)?),
-    )?;
-
-    Ok(())
-}
-
-#[test]
-fn wallet_load_checks() -> anyhow::Result<()> {
-    fn run<Db, CreateDb, OpenDb>(
-        filename: &str,
-        create_db: CreateDb,
-        open_db: OpenDb,
-    ) -> anyhow::Result<()>
-    where
-        CreateDb: Fn(&Path) -> anyhow::Result<Db>,
-        OpenDb: Fn(&Path) -> anyhow::Result<Db>,
-        Db: WalletPersister + std::fmt::Debug,
-        Db::Error: std::error::Error + Send + Sync + 'static,
-    {
-        let temp_dir = tempfile::tempdir().expect("must create tempdir");
-        let file_path = temp_dir.path().join(filename);
-        let network = Network::Testnet;
-        let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-
-        // create new wallet
-        let _ = Wallet::create(external_desc, internal_desc)
-            .network(network)
-            .create_wallet(&mut create_db(&file_path)?)?;
-
-        assert_matches!(
-            Wallet::load()
-                .check_network(Network::Regtest)
-                .load_wallet(&mut open_db(&file_path)?),
-            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
-                LoadMismatch::Network {
-                    loaded: Network::Testnet,
-                    expected: Network::Regtest,
-                }
-            ))),
-            "unexpected network check result: Regtest (check) is not Testnet (loaded)",
-        );
-        let mainnet_hash = BlockHash::from_byte_array(ChainHash::BITCOIN.to_bytes());
-        assert_matches!(
-            Wallet::load().check_genesis_hash(mainnet_hash).load_wallet(&mut open_db(&file_path)?),
-            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Genesis { .. }))),
-            "unexpected genesis hash check result: mainnet hash (check) is not testnet hash (loaded)",
-        );
-        assert_matches!(
-            Wallet::load()
-                .descriptor(KeychainKind::External, Some(internal_desc))
-                .load_wallet(&mut open_db(&file_path)?),
-            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
-                LoadMismatch::Descriptor { .. }
-            ))),
-            "unexpected descriptors check result",
-        );
-        assert_matches!(
-            Wallet::load()
-                .descriptor(KeychainKind::External, Option::<&str>::None)
-                .load_wallet(&mut open_db(&file_path)?),
-            Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(
-                LoadMismatch::Descriptor { .. }
-            ))),
-            "unexpected descriptors check result",
-        );
-        // check setting keymaps
-        let (_, external_keymap) = parse_descriptor(external_desc);
-        let (_, internal_keymap) = parse_descriptor(internal_desc);
-        let wallet = Wallet::load()
-            .keymap(KeychainKind::External, external_keymap)
-            .keymap(KeychainKind::Internal, internal_keymap)
-            .load_wallet(&mut open_db(&file_path)?)
-            .expect("db should not fail")
-            .expect("wallet was persisted");
-        for keychain in [KeychainKind::External, KeychainKind::Internal] {
-            let keymap = wallet.get_signers(keychain).as_key_map(wallet.secp_ctx());
-            assert!(
-                !keymap.is_empty(),
-                "load should populate keymap for keychain {keychain:?}"
-            );
-        }
-        Ok(())
-    }
-
-    run(
-        "store.db",
-        |path| {
-            Ok(bdk_file_store::Store::<ChangeSet>::create_new(
-                DB_MAGIC, path,
-            )?)
-        },
-        |path| Ok(bdk_file_store::Store::<ChangeSet>::open(DB_MAGIC, path)?),
-    )?;
-    run(
-        "store.sqlite",
-        |path| Ok(bdk_chain::rusqlite::Connection::open(path)?),
-        |path| Ok(bdk_chain::rusqlite::Connection::open(path)?),
-    )?;
-
-    Ok(())
-}
-
-#[test]
-fn wallet_should_persist_anchors_and_recover() {
-    use bdk_chain::rusqlite;
-    let temp_dir = tempfile::tempdir().unwrap();
-    let db_path = temp_dir.path().join("wallet.db");
-    let mut db = rusqlite::Connection::open(db_path).unwrap();
-
-    let desc = get_test_tr_single_sig_xprv();
-    let mut wallet = Wallet::create_single(desc)
-        .network(Network::Testnet)
-        .create_wallet(&mut db)
-        .unwrap();
-    let small_output_tx = Transaction {
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-        version: transaction::Version::non_standard(0),
-        lock_time: absolute::LockTime::ZERO,
-    };
-    let txid = small_output_tx.compute_txid();
-    insert_tx(&mut wallet, small_output_tx);
-    let expected_anchor = ConfirmationBlockTime {
-        block_id: wallet.latest_checkpoint().block_id(),
-        confirmation_time: 200,
-    };
-    insert_anchor(&mut wallet, txid, expected_anchor);
-    assert!(wallet.persist(&mut db).unwrap());
-
-    // should recover persisted wallet
-    let secp = wallet.secp_ctx();
-    let (_, keymap) = <Descriptor<DescriptorPublicKey>>::parse_descriptor(secp, desc).unwrap();
-    assert!(!keymap.is_empty());
-    let wallet = Wallet::load()
-        .descriptor(KeychainKind::External, Some(desc))
-        .extract_keys()
-        .load_wallet(&mut db)
-        .unwrap()
-        .expect("must have loaded changeset");
-    // stored anchor should be retrieved in the same condition it was persisted
-    if let ChainPosition::Confirmed {
-        anchor: obtained_anchor,
-        ..
-    } = wallet
-        .get_tx(txid)
-        .expect("should retrieve stored tx")
-        .chain_position
-    {
-        assert_eq!(obtained_anchor, expected_anchor)
-    } else {
-        panic!("Should have got ChainPosition::Confirmed)");
-    }
-}
-
-#[test]
-fn single_descriptor_wallet_persist_and_recover() {
-    use bdk_chain::miniscript::Descriptor;
-    use bdk_chain::miniscript::DescriptorPublicKey;
-    use bdk_chain::rusqlite;
-    let temp_dir = tempfile::tempdir().unwrap();
-    let db_path = temp_dir.path().join("wallet.db");
-    let mut db = rusqlite::Connection::open(db_path).unwrap();
-
-    let desc = get_test_tr_single_sig_xprv();
-    let mut wallet = Wallet::create_single(desc)
-        .network(Network::Testnet)
-        .create_wallet(&mut db)
-        .unwrap();
-    let _ = wallet.reveal_addresses_to(KeychainKind::External, 2);
-    assert!(wallet.persist(&mut db).unwrap());
-
-    // should recover persisted wallet
-    let secp = wallet.secp_ctx();
-    let (_, keymap) = <Descriptor<DescriptorPublicKey>>::parse_descriptor(secp, desc).unwrap();
-    assert!(!keymap.is_empty());
-    let wallet = Wallet::load()
-        .descriptor(KeychainKind::External, Some(desc))
-        .extract_keys()
-        .load_wallet(&mut db)
-        .unwrap()
-        .expect("must have loaded changeset");
-    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(2));
-    // should have private key
-    assert_eq!(
-        wallet.get_signers(KeychainKind::External).as_key_map(secp),
-        keymap,
-    );
-
-    // should error on wrong internal params
-    let desc = get_test_wpkh();
-    let (exp_desc, _) = <Descriptor<DescriptorPublicKey>>::parse_descriptor(secp, desc).unwrap();
-    let err = Wallet::load()
-        .descriptor(KeychainKind::Internal, Some(desc))
-        .extract_keys()
-        .load_wallet(&mut db);
-    assert_matches!(
-        err,
-        Err(LoadWithPersistError::InvalidChangeSet(LoadError::Mismatch(LoadMismatch::Descriptor { keychain, loaded, expected })))
-        if keychain == KeychainKind::Internal && loaded.is_none() && expected == Some(exp_desc),
-        "single descriptor wallet should refuse change descriptor param"
-    );
-}
-
-#[test]
-fn test_error_external_and_internal_are_the_same() {
-    // identical descriptors should fail to create wallet
-    let desc = get_test_wpkh();
-    let err = Wallet::create(desc, desc)
-        .network(Network::Testnet)
-        .create_wallet_no_persist();
-    assert!(
-        matches!(&err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
-        "expected same descriptors error, got {:?}",
-        err,
-    );
-
-    // public + private of same descriptor should fail to create wallet
-    let desc = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
-    let change_desc = "wpkh([3c31d632/84'/1'/0']tpubDCYwFkks2cg78N7eoYbBatsFEGje8vW8arSKW4rLwD1AU1s9KJMDRHE32JkvYERuiFjArrsH7qpWSpJATed5ShZbG9KsskA5Rmi6NSYgYN2/0/*)";
-    let err = Wallet::create(desc, change_desc)
-        .network(Network::Testnet)
-        .create_wallet_no_persist();
-    assert!(
-        matches!(err, Err(DescriptorError::ExternalAndInternalAreTheSame)),
-        "expected same descriptors error, got {:?}",
-        err,
-    );
-}
-
-#[test]
-fn test_descriptor_checksum() {
-    let (wallet, _) = get_funded_wallet_wpkh();
-    let checksum = wallet.descriptor_checksum(KeychainKind::External);
-    assert_eq!(checksum.len(), 8);
-
-    let raw_descriptor = wallet
-        .keychains()
-        .next()
-        .unwrap()
-        .1
-        .to_string()
-        .split_once('#')
-        .unwrap()
-        .0
-        .to_string();
-    assert_eq!(calc_checksum(&raw_descriptor).unwrap(), checksum);
-}
-
-#[test]
-fn test_get_funded_wallet_balance() {
-    let (wallet, _) = get_funded_wallet_wpkh();
-
-    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
-    // sats are the transaction fee.
-    assert_eq!(wallet.balance().confirmed, Amount::from_sat(50_000));
-}
-
-#[test]
-fn test_get_funded_wallet_sent_and_received() {
-    let (wallet, txid) = get_funded_wallet_wpkh();
-
-    let mut tx_amounts: Vec<(Txid, (Amount, Amount))> = wallet
-        .transactions()
-        .map(|ct| (ct.tx_node.txid, wallet.sent_and_received(&ct.tx_node)))
-        .collect();
-    tx_amounts.sort_by(|a1, a2| a1.0.cmp(&a2.0));
-
-    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
-    let (sent, received) = wallet.sent_and_received(&tx);
-
-    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
-    // sats are the transaction fee.
-    assert_eq!(sent.to_sat(), 76_000);
-    assert_eq!(received.to_sat(), 50_000);
-}
-
-#[test]
-fn test_get_funded_wallet_tx_fees() {
-    let (wallet, txid) = get_funded_wallet_wpkh();
-
-    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
-    let tx_fee = wallet.calculate_fee(&tx).expect("transaction fee");
-
-    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
-    // sats are the transaction fee.
-    assert_eq!(tx_fee, Amount::from_sat(1000))
-}
-
-#[test]
-fn test_get_funded_wallet_tx_fee_rate() {
-    let (wallet, txid) = get_funded_wallet_wpkh();
-
-    let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
-    let tx_fee_rate = wallet
-        .calculate_fee_rate(&tx)
-        .expect("transaction fee rate");
-
-    // The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
-    // to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
-    // sats are the transaction fee.
-
-    // tx weight = 452 wu, as vbytes = (452 + 3) / 4 = 113
-    // fee_rate (sats per kwu) = fee / weight = 1000sat / 0.452kwu = 2212
-    // fee_rate (sats per vbyte ceil) = fee / vsize = 1000sat / 113vb = 9
-    assert_eq!(tx_fee_rate.to_sat_per_kwu(), 2212);
-    assert_eq!(tx_fee_rate.to_sat_per_vb_ceil(), 9);
-}
-
-#[test]
-fn test_list_output() {
-    let (wallet, txid) = get_funded_wallet_wpkh();
-    let txos = wallet
-        .list_output()
-        .map(|op| (op.outpoint, op))
-        .collect::<std::collections::BTreeMap<_, _>>();
-    assert_eq!(txos.len(), 2);
-    for (op, txo) in txos {
-        if op.txid == txid {
-            assert_eq!(txo.txout.value.to_sat(), 50_000);
-            assert!(!txo.is_spent);
-        } else {
-            assert_eq!(txo.txout.value.to_sat(), 76_000);
-            assert!(txo.is_spent);
-        }
-    }
-}
-
-macro_rules! assert_fee_rate {
-    ($psbt:expr, $fees:expr, $fee_rate:expr $( ,@dust_change $( $dust_change:expr )* )* $( ,@add_signature $( $add_signature:expr )* )* ) => ({
-        let psbt = $psbt.clone();
-        #[allow(unused_mut)]
-        let mut tx = $psbt.clone().extract_tx().expect("failed to extract tx");
-        $(
-            $( $add_signature )*
-                for txin in &mut tx.input {
-                    txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
-                }
-        )*
-
-            #[allow(unused_mut)]
-        #[allow(unused_assignments)]
-        let mut dust_change = false;
-        $(
-            $( $dust_change )*
-                dust_change = true;
-        )*
-
-            let fee_amount = psbt
-            .inputs
-            .iter()
-            .fold(Amount::ZERO, |acc, i| acc + i.witness_utxo.as_ref().unwrap().value)
-            - psbt
-            .unsigned_tx
-            .output
-            .iter()
-            .fold(Amount::ZERO, |acc, o| acc + o.value);
-
-        assert_eq!(fee_amount, $fees);
-
-        let tx_fee_rate = (fee_amount / tx.weight())
-            .to_sat_per_kwu();
-        let fee_rate = $fee_rate.to_sat_per_kwu();
-        let half_default = FeeRate::BROADCAST_MIN.checked_div(2)
-            .unwrap()
-            .to_sat_per_kwu();
-
-        if !dust_change {
-            assert!(tx_fee_rate >= fee_rate && tx_fee_rate - fee_rate < half_default, "Expected fee rate of {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
-        } else {
-            assert!(tx_fee_rate >= fee_rate, "Expected fee rate of at least {:?}, the tx has {:?}", fee_rate, tx_fee_rate);
-        }
-    });
-}
-
-macro_rules! from_str {
-    ($e:expr, $t:ty) => {{
-        use core::str::FromStr;
-        <$t>::from_str($e).unwrap()
-    }};
-
-    ($e:expr) => {
-        from_str!($e, _)
-    };
-}
-
-#[test]
-#[should_panic(expected = "NoRecipients")]
-fn test_create_tx_empty_recipients() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    wallet.build_tx().finish().unwrap();
-}
-
-#[test]
-#[should_panic(expected = "NoUtxosSelected")]
-fn test_create_tx_manually_selected_empty_utxos() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .manually_selected_only();
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_version_0() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .version(0);
-    assert!(matches!(builder.finish(), Err(CreateTxError::Version0)));
-}
-
-#[test]
-fn test_create_tx_version_1_csv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .version(1);
-    assert!(matches!(builder.finish(), Err(CreateTxError::Version1Csv)));
-}
-
-#[test]
-fn test_create_tx_custom_version() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .version(42);
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.version.0, 42);
-}
-
-#[test]
-fn test_create_tx_default_locktime_is_last_sync_height() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    // Since we never synced the wallet we don't have a last_sync_height
-    // we could use to try to prevent fee sniping. We default to 0.
-    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 2_000);
-}
-
-#[test]
-fn test_create_tx_fee_sniping_locktime_last_sync() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-
-    let psbt = builder.finish().unwrap();
-
-    // If there's no current_height we're left with using the last sync height
-    assert_eq!(
-        psbt.unsigned_tx.lock_time.to_consensus_u32(),
-        wallet.latest_checkpoint().height()
-    );
-}
-
-#[test]
-fn test_create_tx_default_locktime_cltv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 100_000);
-}
-
-#[test]
-fn test_create_tx_locktime_cltv_timestamp() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv_timestamp());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 1_734_230_218);
-
-    let finalized = wallet.sign(&mut psbt, SignOptions::default()).unwrap();
-
-    assert!(finalized);
-}
-
-#[test]
-fn test_create_tx_custom_locktime() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .current_height(630_001)
-        .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
-    let psbt = builder.finish().unwrap();
-
-    // When we explicitly specify a nlocktime
-    // we don't try any fee sniping prevention trick
-    // (we ignore the current_height)
-    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
-}
-
-#[test]
-fn test_create_tx_custom_locktime_compatible_with_cltv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.lock_time.to_consensus_u32(), 630_000);
-}
-
-#[test]
-fn test_create_tx_custom_locktime_incompatible_with_cltv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .nlocktime(absolute::LockTime::from_height(50000).unwrap());
-    assert!(matches!(builder.finish(),
-        Err(CreateTxError::LockTime { requested, required })
-        if requested.to_consensus_u32() == 50_000 && required.to_consensus_u32() == 100_000));
-}
-
-#[test]
-fn test_create_tx_custom_csv() {
-    // desc: wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(6)))
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .set_exact_sequence(Sequence(42))
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-    // we allow setting a sequence higher than required
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(42));
-}
-
-#[test]
-fn test_create_tx_no_rbf_csv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
-}
-
-#[test]
-fn test_create_tx_incompatible_csv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .set_exact_sequence(Sequence(3));
-    assert!(matches!(builder.finish(),
-        Err(CreateTxError::RbfSequenceCsv { sequence, csv })
-        if sequence.to_consensus_u32() == 3 && csv.to_consensus_u32() == 6));
-}
-
-#[test]
-fn test_create_tx_with_default_rbf_csv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_csv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-    // When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
-    // It will be set to the OP_CSV value, in this case 6
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
-}
-
-#[test]
-fn test_create_tx_no_rbf_cltv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    builder.set_exact_sequence(Sequence(0xFFFFFFFE));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
-}
-
-#[test]
-fn test_create_tx_custom_rbf_sequence() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .set_exact_sequence(Sequence(0xDEADBEEF));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xDEADBEEF));
-}
-
-#[test]
-fn test_create_tx_change_policy() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .do_not_spend_change();
-    assert!(builder.finish().is_ok());
-
-    // wallet has no change, so setting `only_spend_change`
-    // should cause tx building to fail
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .only_spend_change();
-    assert!(matches!(
-        builder.finish(),
-        Err(CreateTxError::CoinSelection(
-            coin_selection::InsufficientFunds { .. }
-        )),
-    ));
-}
-
-#[test]
-fn test_create_tx_default_sequence() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD));
-}
-
-macro_rules! check_fee {
-    ($wallet:expr, $psbt: expr) => {{
-        let tx = $psbt.clone().extract_tx().expect("failed to extract tx");
-        let tx_fee = $wallet.calculate_fee(&tx).ok();
-        assert_eq!(tx_fee, $psbt.fee_amount());
-        tx_fee
-    }};
-}
-
-#[test]
-fn test_create_tx_drain_wallet_and_drain_to() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.unsigned_tx.output.len(), 1);
-    assert_eq!(
-        psbt.unsigned_tx.output[0].value,
-        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
-        .unwrap()
-        .assume_checked();
-    let drain_addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(20_000))
-        .drain_to(drain_addr.script_pubkey())
-        .drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-    let outputs = psbt.unsigned_tx.output;
-
-    assert_eq!(outputs.len(), 2);
-    let main_output = outputs
-        .iter()
-        .find(|x| x.script_pubkey == addr.script_pubkey())
-        .unwrap();
-    let drain_output = outputs
-        .iter()
-        .find(|x| x.script_pubkey == drain_addr.script_pubkey())
-        .unwrap();
-    assert_eq!(main_output.value, Amount::from_sat(20_000));
-    assert_eq!(
-        drain_output.value,
-        Amount::from_sat(30_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-fn test_create_tx_drain_to_and_utxos() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let utxos: Vec<_> = wallet.list_unspent().map(|u| u.outpoint).collect();
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .add_utxos(&utxos)
-        .unwrap();
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.unsigned_tx.output.len(), 1);
-    assert_eq!(
-        psbt.unsigned_tx.output[0].value,
-        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-#[should_panic(expected = "NoRecipients")]
-fn test_create_tx_drain_to_no_drain_wallet_no_utxos() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let drain_addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(drain_addr.script_pubkey());
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_default_fee_rate() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::BROADCAST_MIN, @add_signature);
-}
-
-#[test]
-fn test_create_tx_custom_fee_rate() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
-}
-
-#[test]
-fn test_create_tx_absolute_fee() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_absolute(Amount::from_sat(100));
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(100));
-    assert_eq!(psbt.unsigned_tx.output.len(), 1);
-    assert_eq!(
-        psbt.unsigned_tx.output[0].value,
-        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-fn test_create_tx_absolute_zero_fee() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_absolute(Amount::ZERO);
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::ZERO);
-    assert_eq!(psbt.unsigned_tx.output.len(), 1);
-    assert_eq!(
-        psbt.unsigned_tx.output[0].value,
-        Amount::from_sat(50_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-#[should_panic(expected = "InsufficientFunds")]
-fn test_create_tx_absolute_high_fee() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_absolute(Amount::from_sat(60_000));
-    let _ = builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_add_change() {
-    use bdk_wallet::tx_builder::TxOrdering;
-    let seed = [0; 32];
-    let mut rng: StdRng = SeedableRng::from_seed(seed);
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .ordering(TxOrdering::Shuffle);
-    let psbt = builder.finish_with_aux_rand(&mut rng).unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.unsigned_tx.output.len(), 2);
-    assert_eq!(psbt.unsigned_tx.output[0].value, Amount::from_sat(25_000));
-    assert_eq!(
-        psbt.unsigned_tx.output[1].value,
-        Amount::from_sat(25_000) - fee.unwrap_or(Amount::ZERO)
-    );
-}
-
-#[test]
-fn test_create_tx_skip_change_dust() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(49_800));
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.unsigned_tx.output.len(), 1);
-    assert_eq!(psbt.unsigned_tx.output[0].value.to_sat(), 49_800);
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
-}
-
-#[test]
-#[should_panic(expected = "InsufficientFunds")]
-fn test_create_tx_drain_to_dust_amount() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    // very high fee rate, so that the only output would be below dust
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(454));
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_ordering_respected() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let bip69_txin_cmp = |tx_a: &TxIn, tx_b: &TxIn| {
-        let project_outpoint = |t: &TxIn| (t.previous_output.txid, t.previous_output.vout);
-        project_outpoint(tx_a).cmp(&project_outpoint(tx_b))
-    };
-
-    let bip69_txout_cmp = |tx_a: &TxOut, tx_b: &TxOut| {
-        let project_utxo = |t: &TxOut| (t.value, t.script_pubkey.clone());
-        project_utxo(tx_a).cmp(&project_utxo(tx_b))
-    };
-
-    let custom_bip69_ordering = bdk_wallet::tx_builder::TxOrdering::Custom {
-        input_sort: Arc::new(bip69_txin_cmp),
-        output_sort: Arc::new(bip69_txout_cmp),
-    };
-
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(10_000))
-        .ordering(custom_bip69_ordering);
-
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.unsigned_tx.output.len(), 3);
-    assert_eq!(
-        psbt.unsigned_tx.output[0].value,
-        Amount::from_sat(10_000) - fee.unwrap_or(Amount::ZERO)
-    );
-    assert_eq!(psbt.unsigned_tx.output[1].value, Amount::from_sat(10_000));
-    assert_eq!(psbt.unsigned_tx.output[2].value, Amount::from_sat(30_000));
-}
-
-#[test]
-fn test_create_tx_default_sighash() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(30_000));
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.inputs[0].sighash_type, None);
-}
-
-#[test]
-fn test_create_tx_custom_sighash() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .sighash(EcdsaSighashType::Single.into());
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(
-        psbt.inputs[0].sighash_type,
-        Some(EcdsaSighashType::Single.into())
-    );
-}
-
-#[test]
-fn test_create_tx_input_hd_keypaths() {
-    use bitcoin::bip32::{DerivationPath, Fingerprint};
-    use core::str::FromStr;
-
-    let (mut wallet, _) = get_funded_wallet_single("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.inputs[0].bip32_derivation.len(), 1);
-    assert_eq!(
-        psbt.inputs[0].bip32_derivation.values().next().unwrap(),
-        &(
-            Fingerprint::from_str("d34db33f").unwrap(),
-            DerivationPath::from_str("m/44'/0'/0'/0/0").unwrap()
-        )
-    );
-}
-
-#[test]
-fn test_create_tx_output_hd_keypaths() {
-    use bitcoin::bip32::{DerivationPath, Fingerprint};
-    use core::str::FromStr;
-
-    let (mut wallet, _) = get_funded_wallet_single("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
-
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.outputs[0].bip32_derivation.len(), 1);
-    let expected_derivation_path = format!("m/44'/0'/0'/0/{}", addr.index);
-    assert_eq!(
-        psbt.outputs[0].bip32_derivation.values().next().unwrap(),
-        &(
-            Fingerprint::from_str("d34db33f").unwrap(),
-            DerivationPath::from_str(&expected_derivation_path).unwrap()
-        )
-    );
-}
-
-#[test]
-fn test_create_tx_set_redeem_script_p2sh() {
-    use bitcoin::hex::FromHex;
-
-    let (mut wallet, _) =
-        get_funded_wallet_single("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(
-        psbt.inputs[0].redeem_script,
-        Some(ScriptBuf::from(
-            Vec::<u8>::from_hex(
-                "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
-            )
-            .unwrap()
-        ))
-    );
-    assert_eq!(psbt.inputs[0].witness_script, None);
-}
-
-#[test]
-fn test_create_tx_set_witness_script_p2wsh() {
-    use bitcoin::hex::FromHex;
-
-    let (mut wallet, _) =
-        get_funded_wallet_single("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.inputs[0].redeem_script, None);
-    assert_eq!(
-        psbt.inputs[0].witness_script,
-        Some(ScriptBuf::from(
-            Vec::<u8>::from_hex(
-                "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
-            )
-            .unwrap()
-        ))
-    );
-}
-
-#[test]
-fn test_create_tx_set_redeem_witness_script_p2wsh_p2sh() {
-    let (mut wallet, _) = get_funded_wallet_single(
-        "sh(wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)))",
-    );
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    let script = ScriptBuf::from_hex(
-        "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
-    )
-    .unwrap();
-
-    assert_eq!(psbt.inputs[0].redeem_script, Some(script.to_p2wsh()));
-    assert_eq!(psbt.inputs[0].witness_script, Some(script));
-}
-
-#[test]
-fn test_create_tx_non_witness_utxo() {
-    let (mut wallet, _) =
-        get_funded_wallet_single("sh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert!(psbt.inputs[0].non_witness_utxo.is_some());
-    assert!(psbt.inputs[0].witness_utxo.is_none());
-}
-
-#[test]
-fn test_create_tx_only_witness_utxo() {
-    let (mut wallet, _) =
-        get_funded_wallet_single("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .only_witness_utxo()
-        .drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert!(psbt.inputs[0].non_witness_utxo.is_none());
-    assert!(psbt.inputs[0].witness_utxo.is_some());
-}
-
-#[test]
-fn test_create_tx_shwpkh_has_witness_utxo() {
-    let (mut wallet, _) =
-        get_funded_wallet_single("sh(wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert!(psbt.inputs[0].witness_utxo.is_some());
-}
-
-#[test]
-fn test_create_tx_both_non_witness_utxo_and_witness_utxo_default() {
-    let (mut wallet, _) =
-        get_funded_wallet_single("wsh(pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert!(psbt.inputs[0].non_witness_utxo.is_some());
-    assert!(psbt.inputs[0].witness_utxo.is_some());
-}
-
-#[test]
-fn test_create_tx_add_utxo() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let small_output_tx = Transaction {
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-        version: transaction::Version::non_standard(0),
-        lock_time: absolute::LockTime::ZERO,
-    };
-    let txid = small_output_tx.compute_txid();
-    insert_tx(&mut wallet, small_output_tx);
-    let anchor = ConfirmationBlockTime {
-        block_id: wallet.latest_checkpoint().get(2000).unwrap().block_id(),
-        confirmation_time: 200,
-    };
-    insert_anchor(&mut wallet, txid, anchor);
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .add_utxo(OutPoint { txid, vout: 0 })
-        .unwrap();
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-
-    assert_eq!(
-        psbt.unsigned_tx.input.len(),
-        2,
-        "should add an additional input since 25_000 < 30_000"
-    );
-    assert_eq!(
-        sent_received.0,
-        Amount::from_sat(75_000),
-        "total should be sum of both inputs"
-    );
-}
-
-#[test]
-#[should_panic(expected = "InsufficientFunds")]
-fn test_create_tx_manually_selected_insufficient() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let small_output_tx = Transaction {
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-        version: transaction::Version::non_standard(0),
-        lock_time: absolute::LockTime::ZERO,
-    };
-    let txid = small_output_tx.compute_txid();
-    insert_tx(&mut wallet, small_output_tx.clone());
-    let anchor = ConfirmationBlockTime {
-        block_id: wallet.latest_checkpoint().get(2000).unwrap().block_id(),
-        confirmation_time: 200,
-    };
-    insert_anchor(&mut wallet, txid, anchor);
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .add_utxo(OutPoint { txid, vout: 0 })
-        .unwrap()
-        .manually_selected_only();
-    builder.finish().unwrap();
-}
-
-#[test]
-#[should_panic(expected = "SpendingPolicyRequired(External)")]
-fn test_create_tx_policy_path_required() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_a_or_b_plus_csv());
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000));
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_policy_path_no_csv() {
-    let (descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
-    let mut wallet = Wallet::create(descriptor, change_descriptor)
-        .network(Network::Regtest)
-        .create_wallet_no_persist()
-        .expect("wallet");
-
-    let tx = Transaction {
-        version: transaction::Version::non_standard(0),
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(50_000),
-        }],
-    };
-    insert_tx(&mut wallet, tx);
-
-    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
-    let root_id = external_policy.id;
-    // child #0 is just the key "A"
-    let path = vec![(root_id, vec![0])].into_iter().collect();
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .policy_path(path, KeychainKind::External);
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD));
-}
-
-#[test]
-fn test_create_tx_policy_path_use_csv() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_a_or_b_plus_csv());
-
-    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
-    let root_id = external_policy.id;
-    // child #1 is or(pk(B),older(144))
-    let path = vec![(root_id, vec![1])].into_iter().collect();
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .policy_path(path, KeychainKind::External);
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
-}
-
-#[test]
-fn test_create_tx_policy_path_ignored_subtree_with_csv() {
-    let (mut wallet, _) = get_funded_wallet_single("wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),or_i(and_v(v:pkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(30)),and_v(v:pkh(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(90)))))");
-
-    let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
-    let root_id = external_policy.id;
-    // child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
-    let path = vec![(root_id, vec![0])].into_iter().collect();
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(30_000))
-        .policy_path(path, KeychainKind::External);
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFD));
-}
-
-#[test]
-fn test_create_tx_global_xpubs_with_origin() {
-    use bitcoin::bip32;
-    let (mut wallet, _) = get_funded_wallet_single("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .add_global_xpubs();
-    let psbt = builder.finish().unwrap();
-
-    let key = bip32::Xpub::from_str("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
-    let fingerprint = bip32::Fingerprint::from_hex("73756c7f").unwrap();
-    let path = bip32::DerivationPath::from_str("m/48'/0'/0'/2'").unwrap();
-
-    assert_eq!(psbt.xpub.len(), 1);
-    assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
-}
-
-#[test]
-fn test_create_tx_increment_change_index() {
-    // Test derivation index and unused index of change keychain when creating a transaction
-    // Cases include wildcard and non-wildcard descriptors with and without an internal keychain
-    // note the test assumes that the first external address is revealed since we're using
-    // `receive_output`
-    struct TestCase {
-        name: &'static str,
-        descriptor: &'static str,
-        change_descriptor: Option<&'static str>,
-        // amount to send
-        to_send: u64,
-        // (derivation index, next unused index) of *change keychain*
-        expect: (Option<u32>, u32),
-    }
-    // total wallet funds
-    let amount = 10_000;
-    let recipient = Address::from_str("bcrt1q3qtze4ys45tgdvguj66zrk4fu6hq3a3v9pfly5")
-        .unwrap()
-        .assume_checked()
-        .script_pubkey();
-    let (desc, change_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-    [
-        TestCase {
-            name: "two wildcard, builder error",
-            descriptor: desc,
-            change_descriptor: Some(change_desc),
-            to_send: amount + 1,
-            // should not use or derive change index
-            expect: (None, 0),
-        },
-        TestCase {
-            name: "two wildcard, create change",
-            descriptor: desc,
-            change_descriptor: Some(change_desc),
-            to_send: 5_000,
-            // should use change index
-            expect: (Some(0), 1),
-        },
-        TestCase {
-            name: "two wildcard, no change",
-            descriptor: desc,
-            change_descriptor: Some(change_desc),
-            to_send: 9_850,
-            // should not use change index
-            expect: (None, 0),
-        },
-        TestCase {
-            name: "one wildcard, create change",
-            descriptor: desc,
-            change_descriptor: None,
-            to_send: 5_000,
-            // should use change index of external keychain
-            expect: (Some(1), 2),
-        },
-        TestCase {
-            name: "one wildcard, no change",
-            descriptor: desc,
-            change_descriptor: None,
-            to_send: 9_850,
-            // should not use change index
-            expect: (Some(0), 1),
-        },
-        TestCase {
-            name: "single key, create change",
-            descriptor: get_test_tr_single_sig(),
-            change_descriptor: None,
-            to_send: 5_000,
-            // single key only has one derivation index (0)
-            expect: (Some(0), 0),
-        },
-        TestCase {
-            name: "single key, no change",
-            descriptor: get_test_tr_single_sig(),
-            change_descriptor: None,
-            to_send: 9_850,
-            expect: (Some(0), 0),
-        },
-    ]
-    .into_iter()
-    .for_each(|test| {
-        // create wallet
-        let (params, change_keychain) = match test.change_descriptor {
-            Some(change_desc) => (
-                Wallet::create(test.descriptor, change_desc),
-                KeychainKind::Internal,
-            ),
-            None => (
-                Wallet::create_single(test.descriptor),
-                KeychainKind::External,
-            ),
-        };
-        let mut wallet = params
-            .network(Network::Regtest)
-            .create_wallet_no_persist()
-            .unwrap();
-        // fund wallet
-        receive_output(&mut wallet, amount, ReceiveTo::Mempool(0));
-        // create tx
-        let mut builder = wallet.build_tx();
-        builder.add_recipient(recipient.clone(), Amount::from_sat(test.to_send));
-        let res = builder.finish();
-        if !test.name.contains("error") {
-            assert!(res.is_ok());
-        }
-        let (exp_derivation_index, exp_next_unused) = test.expect;
-        assert_eq!(
-            wallet.derivation_index(change_keychain),
-            exp_derivation_index,
-            "derivation index test {}",
-            test.name,
-        );
-        assert_eq!(
-            wallet.next_unused_address(change_keychain).index,
-            exp_next_unused,
-            "next unused index test {}",
-            test.name,
-        );
-    });
-}
-
-#[test]
-fn test_add_foreign_utxo() {
-    let (mut wallet1, _) = get_funded_wallet_wpkh();
-    let (wallet2, _) =
-        get_funded_wallet_single("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let utxo = wallet2.list_unspent().next().expect("must take!");
-    let foreign_utxo_satisfaction = wallet2
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    let psbt_input = psbt::Input {
-        witness_utxo: Some(utxo.txout.clone()),
-        ..Default::default()
-    };
-
-    let mut builder = wallet1.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
-        .only_witness_utxo()
-        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
-        .unwrap();
-    let mut psbt = builder.finish().unwrap();
-    wallet1.insert_txout(utxo.outpoint, utxo.txout);
-    let fee = check_fee!(wallet1, psbt);
-    let sent_received =
-        wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-
-    assert_eq!(
-        (sent_received.0 - sent_received.1),
-        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
-        "we should have only net spent ~10_000"
-    );
-
-    assert!(
-        psbt.unsigned_tx
-            .input
-            .iter()
-            .any(|input| input.previous_output == utxo.outpoint),
-        "foreign_utxo should be in there"
-    );
-
-    let finished = wallet1
-        .sign(
-            &mut psbt,
-            SignOptions {
-                trust_witness_utxo: true,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-
-    assert!(
-        !finished,
-        "only one of the inputs should have been signed so far"
-    );
-
-    let finished = wallet2
-        .sign(
-            &mut psbt,
-            SignOptions {
-                trust_witness_utxo: true,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-    assert!(finished, "all the inputs should have been signed now");
-}
-
-#[test]
-fn test_calculate_fee_with_missing_foreign_utxo() {
-    use bdk_chain::tx_graph::CalculateFeeError;
-    let (mut wallet1, _) = get_funded_wallet_wpkh();
-    let (wallet2, _) =
-        get_funded_wallet_single("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let utxo = wallet2.list_unspent().next().expect("must take!");
-    let foreign_utxo_satisfaction = wallet2
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    let psbt_input = psbt::Input {
-        witness_utxo: Some(utxo.txout.clone()),
-        ..Default::default()
-    };
-
-    let mut builder = wallet1.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
-        .only_witness_utxo()
-        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
-        .unwrap();
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let res = wallet1.calculate_fee(&tx);
-    assert!(
-        matches!(res, Err(CalculateFeeError::MissingTxOut(outpoints)) if outpoints[0] == utxo.outpoint)
-    );
-}
-
-#[test]
-fn test_add_foreign_utxo_invalid_psbt_input() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
-    let foreign_utxo_satisfaction = wallet
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    let mut builder = wallet.build_tx();
-    let result =
-        builder.add_foreign_utxo(outpoint, psbt::Input::default(), foreign_utxo_satisfaction);
-    assert!(matches!(result, Err(AddForeignUtxoError::MissingUtxo)));
-}
-
-#[test]
-fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
-    let (mut wallet1, txid1) = get_funded_wallet_wpkh();
-    let (wallet2, txid2) =
-        get_funded_wallet_single("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
-
-    let utxo2 = wallet2.list_unspent().next().unwrap();
-    let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.tx.clone();
-    let tx2 = wallet2.get_tx(txid2).unwrap().tx_node.tx.clone();
-
-    let satisfaction_weight = wallet2
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    let mut builder = wallet1.build_tx();
-    assert!(
-        builder
-            .add_foreign_utxo(
-                utxo2.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx1.as_ref().clone()),
-                    ..Default::default()
-                },
-                satisfaction_weight
-            )
-            .is_err(),
-        "should fail when outpoint doesn't match psbt_input"
-    );
-    assert!(
-        builder
-            .add_foreign_utxo(
-                utxo2.outpoint,
-                psbt::Input {
-                    non_witness_utxo: Some(tx2.as_ref().clone()),
-                    ..Default::default()
-                },
-                satisfaction_weight
-            )
-            .is_ok(),
-        "should be ok when outpoint does match psbt_input"
-    );
-}
-
-#[test]
-fn test_add_foreign_utxo_only_witness_utxo() {
-    let (mut wallet1, _) = get_funded_wallet_wpkh();
-    let (wallet2, txid2) =
-        get_funded_wallet_single("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let utxo2 = wallet2.list_unspent().next().unwrap();
-
-    let satisfaction_weight = wallet2
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    {
-        let mut builder = wallet1.build_tx();
-        builder.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000));
-
-        let psbt_input = psbt::Input {
-            witness_utxo: Some(utxo2.txout.clone()),
-            ..Default::default()
-        };
-        builder
-            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
-            .unwrap();
-        assert!(
-            builder.finish().is_err(),
-            "psbt_input with witness_utxo should fail with only witness_utxo"
-        );
-    }
-
-    {
-        let mut builder = wallet1.build_tx();
-        builder.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000));
-
-        let psbt_input = psbt::Input {
-            witness_utxo: Some(utxo2.txout.clone()),
-            ..Default::default()
-        };
-        builder
-            .only_witness_utxo()
-            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
-            .unwrap();
-        assert!(
-            builder.finish().is_ok(),
-            "psbt_input with just witness_utxo should succeed when `only_witness_utxo` is enabled"
-        );
-    }
-
-    {
-        let mut builder = wallet1.build_tx();
-        builder.add_recipient(addr.script_pubkey(), Amount::from_sat(60_000));
-
-        let tx2 = wallet2.get_tx(txid2).unwrap().tx_node.tx;
-        let psbt_input = psbt::Input {
-            non_witness_utxo: Some(tx2.as_ref().clone()),
-            ..Default::default()
-        };
-        builder
-            .add_foreign_utxo(utxo2.outpoint, psbt_input, satisfaction_weight)
-            .unwrap();
-        assert!(
-            builder.finish().is_ok(),
-            "psbt_input with non_witness_utxo should succeed by default"
-        );
-    }
-}
-
-#[test]
-fn test_get_psbt_input() {
-    // this should grab a known good utxo and set the input
-    let (wallet, _) = get_funded_wallet_wpkh();
-    for utxo in wallet.list_unspent() {
-        let psbt_input = wallet.get_psbt_input(utxo, None, false).unwrap();
-        assert!(psbt_input.witness_utxo.is_some() || psbt_input.non_witness_utxo.is_some());
-    }
-}
-
-#[test]
-#[should_panic(
-    expected = "MissingKeyOrigin(\"tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3\")"
-)]
-fn test_create_tx_global_xpubs_origin_missing() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .add_global_xpubs();
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_create_tx_global_xpubs_master_without_origin() {
-    use bitcoin::bip32;
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(25_000))
-        .add_global_xpubs();
-    let psbt = builder.finish().unwrap();
-
-    let key = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
-    let fingerprint = bip32::Fingerprint::from_hex("997a323b").unwrap();
-
-    assert_eq!(psbt.xpub.len(), 1);
-    assert_eq!(
-        psbt.xpub.get(&key),
-        Some(&(fingerprint, bip32::DerivationPath::default()))
-    );
-}
-
-#[test]
-#[should_panic(expected = "IrreplaceableTransaction")]
-fn test_bump_fee_irreplaceable_tx() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    builder.set_exact_sequence(Sequence(0xFFFFFFFE));
-    let psbt = builder.finish().unwrap();
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-    wallet.build_fee_bump(txid).unwrap().finish().unwrap();
-}
-
-#[test]
-#[should_panic(expected = "TransactionConfirmed")]
-fn test_bump_fee_confirmed_tx() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-
-    insert_tx(&mut wallet, tx);
-
-    let anchor = ConfirmationBlockTime {
-        block_id: wallet.latest_checkpoint().get(42).unwrap().block_id(),
-        confirmation_time: 42_000,
-    };
-    insert_anchor(&mut wallet, txid, anchor);
-
-    wallet.build_fee_bump(txid).unwrap().finish().unwrap();
-}
-
-#[test]
-fn test_bump_fee_low_fee_rate() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-
-    let psbt = builder.finish().unwrap();
-    let feerate = psbt.fee_rate().unwrap();
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::BROADCAST_MIN);
-    let res = builder.finish();
-    assert_matches!(
-        res,
-        Err(CreateTxError::FeeRateTooLow { .. }),
-        "expected FeeRateTooLow error"
-    );
-
-    let required = feerate.to_sat_per_kwu() + 250; // +1 sat/vb
-    let sat_vb = required as f64 / 250.0;
-    let expect = format!("Fee rate too low: required {} sat/vb", sat_vb);
-    assert_eq!(res.unwrap_err().to_string(), expect);
-}
-
-#[test]
-#[should_panic(expected = "FeeTooLow")]
-fn test_bump_fee_low_abs() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_absolute(Amount::from_sat(10));
-    builder.finish().unwrap();
-}
-
-#[test]
-#[should_panic(expected = "FeeTooLow")]
-fn test_bump_fee_zero_abs() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_absolute(Amount::ZERO);
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_bump_fee_reduce_change() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let psbt = builder.finish().unwrap();
-    let original_sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let original_fee = check_fee!(wallet, psbt);
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(feerate);
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(sent_received.0, original_sent_received.0);
-    assert_eq!(
-        sent_received.1 + fee.unwrap_or(Amount::ZERO),
-        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
-    );
-    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_absolute(Amount::from_sat(200));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(sent_received.0, original_sent_received.0);
-    assert_eq!(
-        sent_received.1 + fee.unwrap_or(Amount::ZERO),
-        original_sent_received.1 + original_fee.unwrap_or(Amount::ZERO)
-    );
-    assert!(
-        fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO),
-        "{} > {}",
-        fee.unwrap_or(Amount::ZERO),
-        original_fee.unwrap_or(Amount::ZERO)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(200));
-}
-
-#[test]
-fn test_bump_fee_reduce_single_recipient() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.clone().extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let original_fee = check_fee!(wallet, psbt);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .fee_rate(feerate)
-        // remove original tx drain_to address and amount
-        .set_recipients(Vec::new())
-        // set back original drain_to address
-        .drain_to(addr.script_pubkey())
-        // drain wallet output amount will be re-calculated with new fee rate
-        .drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(sent_received.0, original_sent_received.0);
-    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.output.len(), 1);
-    assert_eq!(
-        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
-        sent_received.0
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), feerate, @add_signature);
-}
-
-#[test]
-fn test_bump_fee_absolute_reduce_single_recipient() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let original_fee = check_fee!(wallet, psbt);
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .fee_absolute(Amount::from_sat(300))
-        // remove original tx drain_to address and amount
-        .set_recipients(Vec::new())
-        // set back original drain_to address
-        .drain_to(addr.script_pubkey())
-        // drain wallet output amount will be re-calculated with new fee rate
-        .drain_wallet();
-    let psbt = builder.finish().unwrap();
-    let tx = &psbt.unsigned_tx;
-    let sent_received = wallet.sent_and_received(tx);
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(sent_received.0, original_sent_received.0);
-    assert!(fee.unwrap_or(Amount::ZERO) > original_fee.unwrap_or(Amount::ZERO));
-
-    assert_eq!(tx.output.len(), 1);
-    assert_eq!(
-        tx.output[0].value + fee.unwrap_or(Amount::ZERO),
-        sent_received.0
-    );
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(300));
-}
-
-#[test]
-fn test_bump_fee_drain_wallet() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    // receive an extra tx so that our wallet has two utxos.
-    let tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-    };
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx.clone());
-    let anchor = ConfirmationBlockTime {
-        block_id: wallet.latest_checkpoint().block_id(),
-        confirmation_time: 42_000,
-    };
-    insert_anchor(&mut wallet, txid, anchor);
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .add_utxo(OutPoint {
-            txid: tx.compute_txid(),
-            vout: 0,
-        })
-        .unwrap()
-        .manually_selected_only();
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-    assert_eq!(original_sent_received.0, Amount::from_sat(25_000));
-
-    // for the new feerate, it should be enough to reduce the output, but since we specify
-    // `drain_wallet` we expect to spend everything
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .drain_wallet()
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
-    let psbt = builder.finish().unwrap();
-    let sent_received = wallet.sent_and_received(&psbt.extract_tx().expect("failed to extract tx"));
-
-    assert_eq!(sent_received.0, Amount::from_sat(75_000));
-}
-
-#[test]
-#[should_panic(expected = "InsufficientFunds")]
-fn test_bump_fee_remove_output_manually_selected_only() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    // receive an extra tx so that our wallet has two utxos. then we manually pick only one of
-    // them, and make sure that `bump_fee` doesn't try to add more. This fails because we've
-    // told the wallet it's not allowed to add more inputs AND it can't reduce the value of the
-    // existing output. In other words, bump_fee + manually_selected_only is always an error
-    // unless there is a change output.
-    let init_tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-    };
-
-    let position: ChainPosition<ConfirmationBlockTime> =
-        wallet.transactions().last().unwrap().chain_position;
-    insert_tx(&mut wallet, init_tx.clone());
-    match position {
-        ChainPosition::Confirmed { anchor, .. } => {
-            insert_anchor(&mut wallet, init_tx.compute_txid(), anchor)
-        }
-        other => panic!("all wallet txs must be confirmed: {:?}", other),
-    }
-
-    let outpoint = OutPoint {
-        txid: init_tx.compute_txid(),
-        vout: 0,
-    };
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .add_utxo(outpoint)
-        .unwrap()
-        .manually_selected_only();
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-    assert_eq!(original_sent_received.0, Amount::from_sat(25_000));
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .manually_selected_only()
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(255));
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_bump_fee_add_input() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let init_tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-    };
-    let txid = init_tx.compute_txid();
-    let pos: ChainPosition<ConfirmationBlockTime> =
-        wallet.transactions().last().unwrap().chain_position;
-    insert_tx(&mut wallet, init_tx);
-    match pos {
-        ChainPosition::Confirmed { anchor, .. } => insert_anchor(&mut wallet, txid, anchor),
-        other => panic!("all wallet txs must be confirmed: {:?}", other),
-    }
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_details = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-    assert_eq!(
-        sent_received.0,
-        original_details.0 + Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        fee.unwrap_or(Amount::ZERO) + sent_received.1,
-        Amount::from_sat(30_000)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(45_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
-}
-
-#[test]
-fn test_bump_fee_absolute_add_input() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    receive_output_in_latest_block(&mut wallet, 25_000);
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    let psbt = builder.finish().unwrap();
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_absolute(Amount::from_sat(6_000));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(
-        sent_received.0,
-        original_sent_received.0 + Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        fee.unwrap_or(Amount::ZERO) + sent_received.1,
-        Amount::from_sat(30_000)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(45_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(6_000));
-}
-
-#[test]
-fn test_bump_fee_no_change_add_input_and_change() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let op = receive_output_in_latest_block(&mut wallet, 25_000);
-
-    // initially make a tx without change by using `drain_to`
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .add_utxo(op)
-        .unwrap()
-        .manually_selected_only();
-    let psbt = builder.finish().unwrap();
-    let original_sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let original_fee = check_fee!(wallet, psbt);
-
-    let tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    // Now bump the fees, the wallet should add an extra input and a change output, and leave
-    // the original output untouched.
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    let original_send_all_amount = original_sent_received.0 - original_fee.unwrap_or(Amount::ZERO);
-    assert_eq!(
-        sent_received.0,
-        original_sent_received.0 + Amount::from_sat(50_000)
-    );
-    assert_eq!(
-        sent_received.1,
-        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        original_send_all_amount
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(75_000) - original_send_all_amount - fee.unwrap_or(Amount::ZERO)
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(50), @add_signature);
-}
-
-#[test]
-fn test_bump_fee_add_input_change_dust() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    receive_output_in_latest_block(&mut wallet, 25_000);
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    let psbt = builder.finish().unwrap();
-    let original_sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let original_fee = check_fee!(wallet, psbt);
-
-    let mut tx = psbt.extract_tx().expect("failed to extract tx");
-    for txin in &mut tx.input {
-        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // to get realistic weight
-    }
-    let original_tx_weight = tx.weight();
-    assert_eq!(tx.input.len(), 1);
-    assert_eq!(tx.output.len(), 2);
-    let txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    // We set a fee high enough that during rbf we are forced to add
-    // a new input and also that we have to remove the change
-    // that we had previously
-
-    // We calculate the new weight as:
-    //   original weight
-    // + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4
-    // + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4
-    // - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4
-    let new_tx_weight =
-        original_tx_weight + Weight::from_wu(160) + Weight::from_wu(112) - Weight::from_wu(124);
-    // two inputs (50k, 25k) and one output (45k) - epsilon
-    // We use epsilon here to avoid asking for a slightly too high feerate
-    let fee_abs = 50_000 + 25_000 - 45_000 - 10;
-    builder.fee_rate(Amount::from_sat(fee_abs) / new_tx_weight);
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(
-        original_sent_received.1,
-        Amount::from_sat(5_000) - original_fee.unwrap_or(Amount::ZERO)
-    );
-
-    assert_eq!(
-        sent_received.0,
-        original_sent_received.0 + Amount::from_sat(25_000)
-    );
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(30_000));
-    assert_eq!(sent_received.1, Amount::ZERO);
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 1);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(45_000)
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(140), @dust_change, @add_signature);
-}
-
-#[test]
-fn test_bump_fee_force_add_input() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    let psbt = builder.finish().unwrap();
-    let mut tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    for txin in &mut tx.input {
-        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
-    }
-    insert_tx(&mut wallet, tx.clone());
-    // the new fee_rate is low enough that just reducing the change would be fine, but we force
-    // the addition of an extra input with `add_utxo()`
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .add_utxo(incoming_op)
-        .unwrap()
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(5));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(
-        sent_received.0,
-        original_sent_received.0 + Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        fee.unwrap_or(Amount::ZERO) + sent_received.1,
-        Amount::from_sat(30_000)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(45_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), FeeRate::from_sat_per_vb_unchecked(5), @add_signature);
-}
-
-#[test]
-fn test_bump_fee_absolute_force_add_input() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    let psbt = builder.finish().unwrap();
-    let mut tx = psbt.extract_tx().expect("failed to extract tx");
-    let original_sent_received = wallet.sent_and_received(&tx);
-    let txid = tx.compute_txid();
-    // skip saving the new utxos, we know they can't be used anyways
-    for txin in &mut tx.input {
-        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
-    }
-    insert_tx(&mut wallet, tx.clone());
-
-    // the new fee_rate is low enough that just reducing the change would be fine, but we force
-    // the addition of an extra input with `add_utxo()`
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .add_utxo(incoming_op)
-        .unwrap()
-        .fee_absolute(Amount::from_sat(250));
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(
-        sent_received.0,
-        original_sent_received.0 + Amount::from_sat(25_000)
-    );
-    assert_eq!(
-        fee.unwrap_or(Amount::ZERO) + sent_received.1,
-        Amount::from_sat(30_000)
-    );
-
-    let tx = &psbt.unsigned_tx;
-    assert_eq!(tx.input.len(), 2);
-    assert_eq!(tx.output.len(), 2);
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey == addr.script_pubkey())
-            .unwrap()
-            .value,
-        Amount::from_sat(45_000)
-    );
-    assert_eq!(
-        tx.output
-            .iter()
-            .find(|txout| txout.script_pubkey != addr.script_pubkey())
-            .unwrap()
-            .value,
-        sent_received.1
-    );
-
-    assert_eq!(fee.unwrap_or(Amount::ZERO), Amount::from_sat(250));
-}
-
-#[test]
-#[should_panic(expected = "InsufficientFunds")]
-fn test_bump_fee_unconfirmed_inputs_only() {
-    // We try to bump the fee, but:
-    // - We can't reduce the change, as we have no change
-    // - All our UTXOs are unconfirmed
-    // So, we fail with "InsufficientFunds", as per RBF rule 2:
-    // The replacement transaction may only include an unconfirmed input
-    // if that input was included in one of the original transactions.
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.drain_wallet().drain_to(addr.script_pubkey());
-    let psbt = builder.finish().unwrap();
-    // Now we receive one transaction with 0 confirmations. We won't be able to use that for
-    // fee bumping, as it's still unconfirmed!
-    receive_output(&mut wallet, 25_000, ReceiveTo::Mempool(0));
-    let mut tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    for txin in &mut tx.input {
-        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
-    }
-    insert_tx(&mut wallet, tx);
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(25));
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_bump_fee_unconfirmed_input() {
-    // We create a tx draining the wallet and spending one confirmed
-    // and one unconfirmed UTXO. We check that we can fee bump normally
-    // (BIP125 rule 2 only apply to newly added unconfirmed input, you can
-    // always fee bump with an unconfirmed input if it was included in the
-    // original transaction)
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    // We receive a tx with 0 confirmations, which will be used as an input
-    // in the drain tx.
-    receive_output(&mut wallet, 25_000, ReceiveTo::Mempool(0));
-    let mut builder = wallet.build_tx();
-    builder.drain_wallet().drain_to(addr.script_pubkey());
-    let psbt = builder.finish().unwrap();
-    let mut tx = psbt.extract_tx().expect("failed to extract tx");
-    let txid = tx.compute_txid();
-    for txin in &mut tx.input {
-        txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
-    }
-    insert_tx(&mut wallet, tx);
-
-    let mut builder = wallet.build_fee_bump(txid).unwrap();
-    builder
-        .fee_rate(FeeRate::from_sat_per_vb_unchecked(15))
-        // remove original tx drain_to address and amount
-        .set_recipients(Vec::new())
-        // set back original drain_to address
-        .drain_to(addr.script_pubkey())
-        // drain wallet output amount will be re-calculated with new fee rate
-        .drain_wallet();
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_fee_amount_negative_drain_val() {
-    // While building the transaction, bdk would calculate the drain_value
-    // as
-    // current_delta - fee_amount - drain_fee
-    // using saturating_sub, meaning that if the result would end up negative,
-    // it'll remain to zero instead.
-    // This caused a bug in master where we would calculate the wrong fee
-    // for a transaction.
-    // See https://github.com/bitcoindevkit/bdk/issues/660
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
-        .unwrap()
-        .assume_checked();
-    let fee_rate = FeeRate::from_sat_per_kwu(500);
-    let incoming_op = receive_output_in_latest_block(&mut wallet, 8859);
-
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(send_to.script_pubkey(), Amount::from_sat(8630))
-        .add_utxo(incoming_op)
-        .unwrap()
-        .fee_rate(fee_rate);
-    let psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    assert_eq!(psbt.inputs.len(), 1);
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate, @add_signature);
-}
-
-#[test]
-fn test_sign_single_xprv() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_sign_single_xprv_with_master_fingerprint_and_path() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_sign_single_xprv_bip44_path() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/44'/0'/0'/0/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_sign_single_xprv_sh_wpkh() {
-    let (mut wallet, _) = get_funded_wallet_single("sh(wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*))");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_sign_single_wif() {
-    let (mut wallet, _) =
-        get_funded_wallet_single("wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_sign_single_xprv_no_hd_keypaths() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    psbt.inputs[0].bip32_derivation.clear();
-    assert_eq!(psbt.inputs[0].bip32_derivation.len(), 0);
-
-    let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
-    assert!(finalized);
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(extracted.input[0].witness.len(), 2);
-}
-
-#[test]
-fn test_include_output_redeem_witness_script() {
-    let desc = get_test_wpkh();
-    let change_desc = "sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))";
-    let (mut wallet, _) = get_funded_wallet(desc, change_desc);
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
-        .include_output_redeem_witness_script();
-    let psbt = builder.finish().unwrap();
-
-    // p2sh-p2wsh transaction should contain both witness and redeem scripts
-    assert!(psbt
-        .outputs
-        .iter()
-        .any(|output| output.redeem_script.is_some() && output.witness_script.is_some()));
-}
-
-#[test]
-fn test_signing_only_one_of_multiple_inputs() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(45_000))
-        .include_output_redeem_witness_script();
-    let mut psbt = builder.finish().unwrap();
-
-    // add another input to the psbt that is at least passable.
-    let dud_input = bitcoin::psbt::Input {
-        witness_utxo: Some(TxOut {
-            value: Amount::from_sat(100_000),
-            script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
-                "wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
-            )
-            .unwrap()
-            .script_pubkey(),
-        }),
-        ..Default::default()
-    };
-
-    psbt.inputs.push(dud_input);
-    psbt.unsigned_tx.input.push(bitcoin::TxIn::default());
-    let is_final = wallet
-        .sign(
-            &mut psbt,
-            SignOptions {
-                trust_witness_utxo: true,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-    assert!(
-        !is_final,
-        "shouldn't be final since we can't sign one of the inputs"
-    );
-    assert!(
-        psbt.inputs[0].final_script_witness.is_some(),
-        "should finalized input it signed"
-    )
-}
-
-#[test]
-fn test_try_finalize_sign_option() {
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-
-    for try_finalize in &[true, false] {
-        let addr = wallet.next_unused_address(KeychainKind::External);
-        let mut builder = wallet.build_tx();
-        builder.drain_to(addr.script_pubkey()).drain_wallet();
-        let mut psbt = builder.finish().unwrap();
-
-        let finalized = wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    try_finalize: *try_finalize,
-                    ..Default::default()
-                },
-            )
-            .unwrap();
-
-        psbt.inputs.iter().for_each(|input| {
-            if *try_finalize {
-                assert!(finalized);
-                assert!(input.final_script_sig.is_none());
-                assert!(input.final_script_witness.is_some());
-            } else {
-                assert!(!finalized);
-                assert!(input.final_script_sig.is_none());
-                assert!(input.final_script_witness.is_none());
-            }
-        });
-    }
-}
-
-#[test]
-fn test_taproot_try_finalize_sign_option() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree());
-
-    for try_finalize in &[true, false] {
-        let addr = wallet.next_unused_address(KeychainKind::External);
-        let mut builder = wallet.build_tx();
-        builder.drain_to(addr.script_pubkey()).drain_wallet();
-        let mut psbt = builder.finish().unwrap();
-
-        let finalized = wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    try_finalize: *try_finalize,
-                    ..Default::default()
-                },
-            )
-            .unwrap();
-
-        psbt.inputs.iter().for_each(|input| {
-            if *try_finalize {
-                assert!(finalized);
-                assert!(input.final_script_sig.is_none());
-                assert!(input.final_script_witness.is_some());
-                assert!(input.tap_key_sig.is_none());
-                assert!(input.tap_script_sigs.is_empty());
-                assert!(input.tap_scripts.is_empty());
-                assert!(input.tap_key_origins.is_empty());
-                assert!(input.tap_internal_key.is_none());
-                assert!(input.tap_merkle_root.is_none());
-            } else {
-                assert!(!finalized);
-                assert!(input.final_script_sig.is_none());
-                assert!(input.final_script_witness.is_none());
-            }
-        });
-        psbt.outputs.iter().for_each(|output| {
-            if *try_finalize {
-                assert!(finalized);
-                assert!(output.tap_key_origins.is_empty());
-            } else {
-                assert!(!finalized);
-                assert!(!output.tap_key_origins.is_empty());
-            }
-        });
-    }
-}
-
-#[test]
-fn test_sign_nonstandard_sighash() {
-    let sighash = EcdsaSighashType::NonePlusAnyoneCanPay;
-
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .sighash(sighash.into())
-        .drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let result = wallet.sign(&mut psbt, Default::default());
-    assert!(
-        result.is_err(),
-        "Signing should have failed because the TX uses non-standard sighashes"
-    );
-    assert_matches!(
-        result,
-        Err(SignerError::NonStandardSighash),
-        "Signing failed with the wrong error type"
-    );
-
-    // try again after opting-in
-    let result = wallet.sign(
-        &mut psbt,
-        SignOptions {
-            allow_all_sighashes: true,
-            ..Default::default()
-        },
-    );
-    assert!(result.is_ok(), "Signing should have worked");
-    assert!(
-        result.unwrap(),
-        "Should finalize the input since we can produce signatures"
-    );
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(
-        *extracted.input[0].witness.to_vec()[0].last().unwrap(),
-        sighash.to_u32() as u8,
-        "The signature should have been made with the right sighash"
-    );
-}
-
-#[test]
-fn test_unused_address() {
-    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
-    let change_descriptor = get_test_wpkh();
-    let mut wallet = Wallet::create(descriptor, change_descriptor)
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .expect("wallet");
-
-    // `list_unused_addresses` should be empty if we haven't revealed any
-    assert!(wallet
-        .list_unused_addresses(KeychainKind::External)
-        .next()
-        .is_none());
-
-    assert_eq!(
-        wallet
-            .next_unused_address(KeychainKind::External)
-            .to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-    assert_eq!(
-        wallet
-            .list_unused_addresses(KeychainKind::External)
-            .next()
-            .unwrap()
-            .to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-}
-
-#[test]
-fn test_next_unused_address() {
-    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
-    let change_descriptor = get_test_wpkh();
-    let mut wallet = Wallet::create(descriptor, change_descriptor)
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .expect("wallet");
-    assert_eq!(wallet.derivation_index(KeychainKind::External), None);
-
-    assert_eq!(
-        wallet
-            .next_unused_address(KeychainKind::External)
-            .to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0));
-    // calling next_unused again gives same address
-    assert_eq!(
-        wallet
-            .next_unused_address(KeychainKind::External)
-            .to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(0));
-
-    // test mark used / unused
-    assert!(wallet.mark_used(KeychainKind::External, 0));
-    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
-    assert_eq!(next_unused_addr.index, 1);
-
-    assert!(wallet.unmark_used(KeychainKind::External, 0));
-    let next_unused_addr = wallet.next_unused_address(KeychainKind::External);
-    assert_eq!(next_unused_addr.index, 0);
-
-    // use the above address
-    receive_output(&mut wallet, 25_000, ReceiveTo::Mempool(0));
-
-    assert_eq!(
-        wallet
-            .next_unused_address(KeychainKind::External)
-            .to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-    assert_eq!(wallet.derivation_index(KeychainKind::External), Some(1));
-
-    // trying to mark index 0 unused should return false
-    assert!(!wallet.unmark_used(KeychainKind::External, 0));
-}
-
-#[test]
-fn test_peek_address_at_index() {
-    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
-    let change_descriptor = get_test_wpkh();
-    let mut wallet = Wallet::create(descriptor, change_descriptor)
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .expect("wallet");
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 1).to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 0).to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 2).to_string(),
-        "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
-    );
-
-    // current new address is not affected
-    assert_eq!(
-        wallet
-            .reveal_next_address(KeychainKind::External)
-            .to_string(),
-        "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
-    );
-
-    assert_eq!(
-        wallet
-            .reveal_next_address(KeychainKind::External)
-            .to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-}
-
-#[test]
-fn test_peek_address_at_index_not_derivable() {
-    let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/1)";
-    let wallet = Wallet::create(descriptor, get_test_wpkh())
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .unwrap();
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 1).to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 0).to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 2).to_string(),
-        "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
-    );
-}
-
-#[test]
-fn test_returns_index_and_address() {
-    let descriptor =
-        "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
-    let mut wallet = Wallet::create(descriptor, get_test_wpkh())
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .unwrap();
-
-    // new index 0
-    assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External),
-        AddressInfo {
-            index: 0,
-            address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::External,
-        }
-    );
-
-    // new index 1
-    assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External),
-        AddressInfo {
-            index: 1,
-            address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::External,
-        }
-    );
-
-    // peek index 25
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 25),
-        AddressInfo {
-            index: 25,
-            address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::External,
-        }
-    );
-
-    // new index 2
-    assert_eq!(
-        wallet.reveal_next_address(KeychainKind::External),
-        AddressInfo {
-            index: 2,
-            address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::External,
-        }
-    );
-}
-
-#[test]
-fn test_sending_to_bip350_bech32m_address() {
-    let (mut wallet, _) = get_funded_wallet_wpkh();
-    let addr = Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(45_000));
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_get_address() {
-    use bdk_wallet::descriptor::template::Bip84;
-    let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-    let wallet = Wallet::create(
-        Bip84(key, KeychainKind::External),
-        Bip84(key, KeychainKind::Internal),
-    )
-    .network(Network::Regtest)
-    .create_wallet_no_persist()
-    .unwrap();
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::External, 0),
-        AddressInfo {
-            index: 0,
-            address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::External,
-        }
-    );
-
-    assert_eq!(
-        wallet.peek_address(KeychainKind::Internal, 0),
-        AddressInfo {
-            index: 0,
-            address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79")
-                .unwrap()
-                .assume_checked(),
-            keychain: KeychainKind::Internal,
-        }
-    );
-}
-
-#[test]
-fn test_reveal_addresses() {
-    let (desc, change_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-    let mut wallet = Wallet::create(desc, change_desc)
-        .network(Network::Signet)
-        .create_wallet_no_persist()
-        .unwrap();
-    let keychain = KeychainKind::External;
-
-    let last_revealed_addr = wallet.reveal_addresses_to(keychain, 9).last().unwrap();
-    assert_eq!(wallet.derivation_index(keychain), Some(9));
-
-    let unused_addrs = wallet.list_unused_addresses(keychain).collect::<Vec<_>>();
-    assert_eq!(unused_addrs.len(), 10);
-    assert_eq!(unused_addrs.last().unwrap(), &last_revealed_addr);
-
-    // revealing to an already revealed index returns nothing
-    let mut already_revealed = wallet.reveal_addresses_to(keychain, 9);
-    assert!(already_revealed.next().is_none());
-}
-
-#[test]
-fn test_get_address_no_reuse() {
-    use bdk_wallet::descriptor::template::Bip84;
-    use std::collections::HashSet;
-
-    let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
-    let mut wallet = Wallet::create(
-        Bip84(key, KeychainKind::External),
-        Bip84(key, KeychainKind::Internal),
-    )
-    .network(Network::Regtest)
-    .create_wallet_no_persist()
-    .unwrap();
-
-    let mut used_set = HashSet::new();
-
-    (0..3).for_each(|_| {
-        let external_addr = wallet.reveal_next_address(KeychainKind::External).address;
-        assert!(used_set.insert(external_addr));
-
-        let internal_addr = wallet.reveal_next_address(KeychainKind::Internal).address;
-        assert!(used_set.insert(internal_addr));
-    });
-}
-
-#[test]
-fn test_taproot_psbt_populate_tap_key_origins() {
-    let (desc, change_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-    let (mut wallet, _) = get_funded_wallet(desc, change_desc);
-    let addr = wallet.reveal_next_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(
-        psbt.inputs[0]
-            .tap_key_origins
-            .clone()
-            .into_iter()
-            .collect::<Vec<_>>(),
-        vec![(
-            from_str!("0841db1dbaf949dbbda893e01a18f2cca9179cf8ea2d8e667857690502b06483"),
-            (vec![], (from_str!("f6a5cb8b"), from_str!("m/0/0")))
-        )],
-        "Wrong input tap_key_origins"
-    );
-    assert_eq!(
-        psbt.outputs[0]
-            .tap_key_origins
-            .clone()
-            .into_iter()
-            .collect::<Vec<_>>(),
-        vec![(
-            from_str!("9187c1e80002d19ddde9c5c7f5394e9a063cee8695867b58815af0562695ca21"),
-            (vec![], (from_str!("f6a5cb8b"), from_str!("m/0/1")))
-        )],
-        "Wrong output tap_key_origins"
-    );
-}
-
-#[test]
-fn test_taproot_psbt_populate_tap_key_origins_repeated_key() {
-    let (mut wallet, _) = get_funded_wallet(get_test_tr_repeated_key(), get_test_tr_single_sig());
-    let addr = wallet.reveal_next_address(KeychainKind::External);
-
-    let path = vec![("rn4nre9c".to_string(), vec![0])]
-        .into_iter()
-        .collect();
-
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .policy_path(path, KeychainKind::External);
-    let psbt = builder.finish().unwrap();
-
-    let mut input_key_origins = psbt.inputs[0]
-        .tap_key_origins
-        .clone()
-        .into_iter()
-        .collect::<Vec<_>>();
-    input_key_origins.sort();
-
-    assert_eq!(
-        input_key_origins,
-        vec![
-            (
-                from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
-                (
-                    vec![
-                        from_str!(
-                            "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
-                        ),
-                        from_str!(
-                            "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
-                        ),
-                    ],
-                    (FromStr::from_str("ece52657").unwrap(), vec![].into())
-                )
-            ),
-            (
-                from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
-                (
-                    vec![],
-                    (FromStr::from_str("871fd295").unwrap(), vec![].into())
-                )
-            )
-        ],
-        "Wrong input tap_key_origins"
-    );
-
-    let mut output_key_origins = psbt.outputs[0]
-        .tap_key_origins
-        .clone()
-        .into_iter()
-        .collect::<Vec<_>>();
-    output_key_origins.sort();
-
-    assert_eq!(
-        input_key_origins, output_key_origins,
-        "Wrong output tap_key_origins"
-    );
-}
-
-#[test]
-fn test_taproot_psbt_input_tap_tree() {
-    use bitcoin::hex::FromHex;
-    use bitcoin::taproot;
-
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let psbt = builder.finish().unwrap();
-
-    assert_eq!(
-        psbt.inputs[0].tap_merkle_root,
-        Some(
-            TapNodeHash::from_str(
-                "61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
-            )
-            .unwrap()
-        ),
-    );
-    assert_eq!(
-        psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
-        vec![
-            (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (ScriptBuf::from_hex("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac").unwrap(), taproot::LeafVersion::TapScript)),
-            (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (ScriptBuf::from_hex("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac").unwrap(), taproot::LeafVersion::TapScript)),
-        ],
-    );
-    assert_eq!(
-        psbt.inputs[0].tap_internal_key,
-        Some(from_str!(
-            "b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"
-        ))
-    );
-
-    // Since we are creating an output to the same address as the input, assert that the
-    // internal_key is the same
-    assert_eq!(
-        psbt.inputs[0].tap_internal_key,
-        psbt.outputs[0].tap_internal_key
-    );
-
-    let tap_tree: bitcoin::taproot::TapTree = serde_json::from_str(r#"[1,{"Script":["2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",192]},1,{"Script":["208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac",192]}]"#).unwrap();
-    assert_eq!(psbt.outputs[0].tap_tree, Some(tap_tree));
-}
-
-#[test]
-fn test_taproot_sign_missing_witness_utxo() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-    let witness_utxo = psbt.inputs[0].witness_utxo.take();
-
-    let result = wallet.sign(
-        &mut psbt,
-        SignOptions {
-            allow_all_sighashes: true,
-            ..Default::default()
-        },
-    );
-    assert_matches!(
-        result,
-        Err(SignerError::MissingWitnessUtxo),
-        "Signing should have failed with the correct error because the witness_utxo is missing"
-    );
-
-    // restore the witness_utxo
-    psbt.inputs[0].witness_utxo = witness_utxo;
-
-    let result = wallet.sign(
-        &mut psbt,
-        SignOptions {
-            allow_all_sighashes: true,
-            ..Default::default()
-        },
-    );
-
-    assert_matches!(
-        result,
-        Ok(true),
-        "Should finalize the input since we can produce signatures"
-    );
-}
-
-#[test]
-fn test_taproot_sign_using_non_witness_utxo() {
-    let (mut wallet, prev_txid) = get_funded_wallet_single(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder.drain_to(addr.script_pubkey()).drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    psbt.inputs[0].witness_utxo = None;
-    psbt.inputs[0].non_witness_utxo =
-        Some(wallet.get_tx(prev_txid).unwrap().tx_node.as_ref().clone());
-    assert!(
-        psbt.inputs[0].non_witness_utxo.is_some(),
-        "Previous tx should be present in the database"
-    );
-
-    let result = wallet.sign(&mut psbt, Default::default());
-    assert!(result.is_ok(), "Signing should have worked");
-    assert!(
-        result.unwrap(),
-        "Should finalize the input since we can produce signatures"
-    );
-}
-
-#[test]
-fn test_taproot_foreign_utxo() {
-    let (mut wallet1, _) = get_funded_wallet_wpkh();
-    let (wallet2, _) = get_funded_wallet_single(get_test_tr_single_sig());
-
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let utxo = wallet2.list_unspent().next().unwrap();
-    let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
-    let foreign_utxo_satisfaction = wallet2
-        .public_descriptor(KeychainKind::External)
-        .max_weight_to_satisfy()
-        .unwrap();
-
-    assert!(
-        psbt_input.non_witness_utxo.is_none(),
-        "`non_witness_utxo` should never be populated for taproot"
-    );
-
-    let mut builder = wallet1.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), Amount::from_sat(60_000))
-        .add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
-        .unwrap();
-    let psbt = builder.finish().unwrap();
-    let sent_received =
-        wallet1.sent_and_received(&psbt.clone().extract_tx().expect("failed to extract tx"));
-    wallet1.insert_txout(utxo.outpoint, utxo.txout);
-    let fee = check_fee!(wallet1, psbt);
-
-    assert_eq!(
-        sent_received.0 - sent_received.1,
-        Amount::from_sat(10_000) + fee.unwrap_or(Amount::ZERO),
-        "we should have only net spent ~10_000"
-    );
-
-    assert!(
-        psbt.unsigned_tx
-            .input
-            .iter()
-            .any(|input| input.previous_output == utxo.outpoint),
-        "foreign_utxo should be in there"
-    );
-}
-
-fn test_spend_from_wallet(mut wallet: Wallet) {
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    assert_eq!(psbt.unsigned_tx.version.0, 2);
-    assert!(
-        wallet.sign(&mut psbt, Default::default()).unwrap(),
-        "Unable to finalize tx"
-    );
-}
-
-//     #[test]
-//     fn test_taproot_key_spend() {
-//         let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig());
-//         test_spend_from_wallet(wallet);
-
-//         let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig_xprv());
-//         test_spend_from_wallet(wallet);
-//     }
-
-#[test]
-fn test_taproot_no_key_spend() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    assert!(
-        wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    sign_with_tap_internal_key: false,
-                    ..Default::default()
-                },
-            )
-            .unwrap(),
-        "Unable to finalize tx"
-    );
-
-    assert!(psbt.inputs.iter().all(|i| i.tap_key_sig.is_none()));
-}
-
-#[test]
-fn test_taproot_script_spend() {
-    let (wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree());
-    test_spend_from_wallet(wallet);
-
-    let (wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_xprv());
-    test_spend_from_wallet(wallet);
-}
-
-#[test]
-fn test_taproot_script_spend_sign_all_leaves() {
-    use bdk_wallet::signer::TapLeavesOptions;
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    assert!(
-        wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    tap_leaves_options: TapLeavesOptions::All,
-                    ..Default::default()
-                },
-            )
-            .unwrap(),
-        "Unable to finalize tx"
-    );
-
-    assert!(psbt
-        .inputs
-        .iter()
-        .all(|i| i.tap_script_sigs.len() == i.tap_scripts.len()));
-}
-
-#[test]
-fn test_taproot_script_spend_sign_include_some_leaves() {
-    use bdk_wallet::signer::TapLeavesOptions;
-    use bitcoin::taproot::TapLeafHash;
-
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-    let mut script_leaves: Vec<_> = psbt.inputs[0]
-        .tap_scripts
-        .clone()
-        .values()
-        .map(|(script, version)| TapLeafHash::from_script(script, *version))
-        .collect();
-    let included_script_leaves = vec![script_leaves.pop().unwrap()];
-    let excluded_script_leaves = script_leaves;
-
-    assert!(
-        wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    tap_leaves_options: TapLeavesOptions::Include(included_script_leaves.clone()),
-                    ..Default::default()
-                },
-            )
-            .unwrap(),
-        "Unable to finalize tx"
-    );
-
-    assert!(psbt.inputs[0]
-        .tap_script_sigs
-        .iter()
-        .all(|s| included_script_leaves.contains(&s.0 .1)
-            && !excluded_script_leaves.contains(&s.0 .1)));
-}
-
-#[test]
-fn test_taproot_script_spend_sign_exclude_some_leaves() {
-    use bdk_wallet::signer::TapLeavesOptions;
-    use bitcoin::taproot::TapLeafHash;
-
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-    let mut script_leaves: Vec<_> = psbt.inputs[0]
-        .tap_scripts
-        .clone()
-        .values()
-        .map(|(script, version)| TapLeafHash::from_script(script, *version))
-        .collect();
-    let included_script_leaves = [script_leaves.pop().unwrap()];
-    let excluded_script_leaves = script_leaves;
-
-    assert!(
-        wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    tap_leaves_options: TapLeavesOptions::Exclude(excluded_script_leaves.clone()),
-                    ..Default::default()
-                },
-            )
-            .unwrap(),
-        "Unable to finalize tx"
-    );
-
-    assert!(psbt.inputs[0]
-        .tap_script_sigs
-        .iter()
-        .all(|s| included_script_leaves.contains(&s.0 .1)
-            && !excluded_script_leaves.contains(&s.0 .1)));
-}
-
-#[test]
-fn test_taproot_script_spend_sign_no_leaves() {
-    use bdk_wallet::signer::TapLeavesOptions;
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_with_taptree_both_priv());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    wallet
-        .sign(
-            &mut psbt,
-            SignOptions {
-                tap_leaves_options: TapLeavesOptions::None,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-
-    assert!(psbt.inputs.iter().all(|i| i.tap_script_sigs.is_empty()));
-}
-
-#[test]
-fn test_taproot_sign_derive_index_from_psbt() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig_xprv());
-
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), Amount::from_sat(25_000));
-    let mut psbt = builder.finish().unwrap();
-
-    // re-create the wallet with an empty db
-    let wallet_empty = Wallet::create(get_test_tr_single_sig_xprv(), get_test_tr_single_sig())
-        .network(Network::Regtest)
-        .create_wallet_no_persist()
-        .unwrap();
-
-    // signing with an empty db means that we will only look at the psbt to infer the
-    // derivation index
-    assert!(
-        wallet_empty.sign(&mut psbt, Default::default()).unwrap(),
-        "Unable to finalize tx"
-    );
-}
-
-#[test]
-fn test_taproot_sign_explicit_sighash_all() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .sighash(TapSighashType::All.into())
-        .drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let result = wallet.sign(&mut psbt, Default::default());
-    assert!(
-        result.is_ok(),
-        "Signing should work because SIGHASH_ALL is safe"
-    )
-}
-
-#[test]
-fn test_taproot_sign_non_default_sighash() {
-    let sighash = TapSighashType::NonePlusAnyoneCanPay;
-
-    let (mut wallet, _) = get_funded_wallet_single(get_test_tr_single_sig());
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .sighash(sighash.into())
-        .drain_wallet();
-    let mut psbt = builder.finish().unwrap();
-
-    let witness_utxo = psbt.inputs[0].witness_utxo.take();
-
-    let result = wallet.sign(&mut psbt, Default::default());
-    assert!(
-        result.is_err(),
-        "Signing should have failed because the TX uses non-standard sighashes"
-    );
-    assert_matches!(
-        result,
-        Err(SignerError::NonStandardSighash),
-        "Signing failed with the wrong error type"
-    );
-
-    // try again after opting-in
-    let result = wallet.sign(
-        &mut psbt,
-        SignOptions {
-            allow_all_sighashes: true,
-            ..Default::default()
-        },
-    );
-    assert!(
-        result.is_err(),
-        "Signing should have failed because the witness_utxo is missing"
-    );
-    assert_matches!(
-        result,
-        Err(SignerError::MissingWitnessUtxo),
-        "Signing failed with the wrong error type"
-    );
-
-    // restore the witness_utxo
-    psbt.inputs[0].witness_utxo = witness_utxo;
-
-    let result = wallet.sign(
-        &mut psbt,
-        SignOptions {
-            allow_all_sighashes: true,
-            ..Default::default()
-        },
-    );
-
-    assert!(result.is_ok(), "Signing should have worked");
-    assert!(
-        result.unwrap(),
-        "Should finalize the input since we can produce signatures"
-    );
-
-    let extracted = psbt.extract_tx().expect("failed to extract tx");
-    assert_eq!(
-        *extracted.input[0].witness.to_vec()[0].last().unwrap(),
-        sighash as u8,
-        "The signature should have been made with the right sighash"
-    );
-}
-
-#[test]
-fn test_spend_coinbase() {
-    let (desc, change_desc) = get_test_wpkh_and_change_desc();
-    let mut wallet = Wallet::create(desc, change_desc)
-        .network(Network::Regtest)
-        .create_wallet_no_persist()
-        .unwrap();
-
-    let confirmation_height = 5;
-    let confirmation_block_id = BlockId {
-        height: confirmation_height,
-        hash: BlockHash::all_zeros(),
-    };
-    insert_checkpoint(&mut wallet, confirmation_block_id);
-    let coinbase_tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![TxIn {
-            previous_output: OutPoint::null(),
-            ..Default::default()
-        }],
-        output: vec![TxOut {
-            script_pubkey: wallet
-                .next_unused_address(KeychainKind::External)
-                .script_pubkey(),
-            value: Amount::from_sat(25_000),
-        }],
-    };
-    let txid = coinbase_tx.compute_txid();
-    insert_tx(&mut wallet, coinbase_tx);
-    let anchor = ConfirmationBlockTime {
-        block_id: confirmation_block_id,
-        confirmation_time: 30_000,
-    };
-    insert_anchor(&mut wallet, txid, anchor);
-
-    // NOTE: A transaction spending an output coming from the coinbase tx at height h, is eligible
-    // to be included in block h + [100 = COINBASE_MATURITY] or higher.
-    // Tx elibible to be included in the next block will be accepted in the mempool, used in block
-    // templates and relayed on the network.
-    // Miners may include such tx in a block when their chaintip is at h + [99 = COINBASE_MATURITY - 1].
-    // This means these coins are available for selection at height h + 99.
-    //
-    // By https://bitcoin.stackexchange.com/a/119017
-    let not_yet_mature_time = confirmation_height + COINBASE_MATURITY - 2;
-    let maturity_time = confirmation_height + COINBASE_MATURITY - 1;
-
-    let balance = wallet.balance();
-    assert_eq!(
-        balance,
-        Balance {
-            immature: Amount::from_sat(25_000),
-            trusted_pending: Amount::ZERO,
-            untrusted_pending: Amount::ZERO,
-            confirmed: Amount::ZERO
-        }
-    );
-
-    // We try to create a transaction, only to notice that all
-    // our funds are unspendable
-    let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), balance.immature / 2)
-        .current_height(confirmation_height);
-    assert!(matches!(
-        builder.finish(),
-        Err(CreateTxError::CoinSelection(
-            coin_selection::InsufficientFunds {
-                needed: _,
-                available: Amount::ZERO
-            }
-        ))
-    ));
-
-    // Still unspendable...
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), balance.immature / 2)
-        .current_height(not_yet_mature_time);
-    assert_matches!(
-        builder.finish(),
-        Err(CreateTxError::CoinSelection(
-            coin_selection::InsufficientFunds {
-                needed: _,
-                available: Amount::ZERO
-            }
-        ))
-    );
-
-    insert_checkpoint(
-        &mut wallet,
-        BlockId {
-            height: maturity_time,
-            hash: BlockHash::all_zeros(),
-        },
-    );
-    let balance = wallet.balance();
-    assert_eq!(
-        balance,
-        Balance {
-            immature: Amount::ZERO,
-            trusted_pending: Amount::ZERO,
-            untrusted_pending: Amount::ZERO,
-            confirmed: Amount::from_sat(25_000)
-        }
-    );
-    let mut builder = wallet.build_tx();
-    builder
-        .add_recipient(addr.script_pubkey(), balance.confirmed / 2)
-        .current_height(maturity_time);
-    builder.finish().unwrap();
-}
-
-#[test]
-fn test_allow_dust_limit() {
-    let (mut wallet, _) = get_funded_wallet_single(get_test_single_sig_cltv());
-
-    let addr = wallet.next_unused_address(KeychainKind::External);
-
-    let mut builder = wallet.build_tx();
-
-    builder.add_recipient(addr.script_pubkey(), Amount::ZERO);
-
-    assert_matches!(
-        builder.finish(),
-        Err(CreateTxError::OutputBelowDustLimit(0))
-    );
-
-    let mut builder = wallet.build_tx();
-
-    builder
-        .allow_dust(true)
-        .add_recipient(addr.script_pubkey(), Amount::ZERO);
-
-    assert!(builder.finish().is_ok());
-}
-
-#[test]
-fn test_fee_rate_sign_no_grinding_high_r() {
-    // Our goal is to obtain a transaction with a signature with high-R (71 bytes
-    // instead of 70). We then check that our fee rate and fee calculation is
-    // alright.
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-    let mut builder = wallet.build_tx();
-    let mut data = PushBytesBuf::try_from(vec![0]).unwrap();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_rate(fee_rate)
-        .add_data(&data);
-    let mut psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-    let (op_return_vout, _) = psbt
-        .unsigned_tx
-        .output
-        .iter()
-        .enumerate()
-        .find(|(_n, i)| i.script_pubkey.is_op_return())
-        .unwrap();
-
-    let mut sig_len: usize = 0;
-    // We try to sign many different times until we find a longer signature (71 bytes)
-    while sig_len < 71 {
-        // Changing the OP_RETURN data will make the signature change (but not the fee, until
-        // data[0] is small enough)
-        data.as_mut_bytes()[0] += 1;
-        psbt.unsigned_tx.output[op_return_vout].script_pubkey = ScriptBuf::new_op_return(&data);
-        // Clearing the previous signature
-        psbt.inputs[0].partial_sigs.clear();
-        // Signing
-        wallet
-            .sign(
-                &mut psbt,
-                SignOptions {
-                    try_finalize: false,
-                    allow_grinding: false,
-                    ..Default::default()
-                },
-            )
-            .unwrap();
-        // We only have one key in the partial_sigs map, this is a trick to retrieve it
-        let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
-        sig_len = psbt.inputs[0].partial_sigs[key]
-            .signature
-            .serialize_der()
-            .len();
-    }
-    // Actually finalizing the transaction...
-    wallet
-        .sign(
-            &mut psbt,
-            SignOptions {
-                allow_grinding: false,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-    // ...and checking that everything is fine
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
-}
-
-#[test]
-fn test_fee_rate_sign_grinding_low_r() {
-    // Our goal is to obtain a transaction with a signature with low-R (70 bytes)
-    // by setting the `allow_grinding` signing option as true.
-    // We then check that our fee rate and fee calculation is alright and that our
-    // signature is 70 bytes.
-    let (mut wallet, _) = get_funded_wallet_single("wpkh(tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)");
-    let addr = wallet.next_unused_address(KeychainKind::External);
-    let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
-    let mut builder = wallet.build_tx();
-    builder
-        .drain_to(addr.script_pubkey())
-        .drain_wallet()
-        .fee_rate(fee_rate);
-    let mut psbt = builder.finish().unwrap();
-    let fee = check_fee!(wallet, psbt);
-
-    wallet
-        .sign(
-            &mut psbt,
-            SignOptions {
-                try_finalize: false,
-                allow_grinding: true,
-                ..Default::default()
-            },
-        )
-        .unwrap();
-
-    let key = psbt.inputs[0].partial_sigs.keys().next().unwrap();
-    let sig_len = psbt.inputs[0].partial_sigs[key]
-        .signature
-        .serialize_der()
-        .len();
-    assert_eq!(sig_len, 70);
-    assert_fee_rate!(psbt, fee.unwrap_or(Amount::ZERO), fee_rate);
-}
-
-#[test]
-fn test_taproot_load_descriptor_duplicated_keys() {
-    // Added after issue https://github.com/bitcoindevkit/bdk/issues/760
-    //
-    // Having the same key in multiple taproot leaves is safe and should be accepted by BDK
-
-    let (wallet, _) = get_funded_wallet_single(get_test_tr_dup_keys());
-    let addr = wallet.peek_address(KeychainKind::External, 0);
-
-    assert_eq!(
-        addr.to_string(),
-        "bcrt1pvysh4nmh85ysrkpwtrr8q8gdadhgdejpy6f9v424a8v9htjxjhyqw9c5s5"
-    );
-}
-
-/// In dev mode this test panics, but in release mode, or if the `debug_panic` in `TxOutIndex::replenish_inner_index`
-/// is commented out, there is no panic and the balance is calculated correctly. See issue [#1483]
-/// and PR [#1486] for discussion on mixing non-wildcard and wildcard descriptors.
-///
-/// [#1483]: https://github.com/bitcoindevkit/bdk/issues/1483
-/// [#1486]: https://github.com/bitcoindevkit/bdk/pull/1486
-#[test]
-#[cfg(debug_assertions)]
-#[should_panic(
-    expected = "replenish lookahead: must not have existing spk: keychain=Internal, lookahead=25, next_store_index=0, next_reveal_index=0"
-)]
-fn test_keychains_with_overlapping_spks() {
-    // this can happen if a non-wildcard descriptor keychain derives an spk that a
-    // wildcard descriptor keychain in the same wallet also derives.
-
-    // index 1 spk overlaps with non-wildcard change descriptor
-    let wildcard_keychain = "wpkh(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/*)";
-    let non_wildcard_keychain = "wpkh(tprv8ZgxMBicQKsPdDArR4xSAECuVxeX1jwwSXR4ApKbkYgZiziDc4LdBy2WvJeGDfUSE4UT4hHhbgEwbdq8ajjUHiKDegkwrNU6V55CxcxonVN/1)";
-
-    let (mut wallet, _) = get_funded_wallet(wildcard_keychain, non_wildcard_keychain);
-    assert_eq!(wallet.balance().confirmed, Amount::from_sat(50000));
-
-    let addr = wallet
-        .reveal_addresses_to(KeychainKind::External, 1)
-        .last()
-        .unwrap()
-        .address;
-    let anchor = ConfirmationBlockTime {
-        block_id: BlockId {
-            height: 2000,
-            hash: BlockHash::all_zeros(),
-        },
-        confirmation_time: 0,
-    };
-    let _outpoint = receive_output_to_address(&mut wallet, addr, 8000, anchor);
-    assert_eq!(wallet.balance().confirmed, Amount::from_sat(58000));
-}
-
-#[test]
-/// The wallet should re-use previously allocated change addresses when the tx using them is cancelled
-fn test_tx_cancellation() {
-    macro_rules! new_tx {
-        ($wallet:expr) => {{
-            let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
-                .unwrap()
-                .assume_checked();
-            let mut builder = $wallet.build_tx();
-            builder.add_recipient(addr.script_pubkey(), Amount::from_sat(10_000));
-
-            let psbt = builder.finish().unwrap();
-
-            psbt
-        }};
-    }
-
-    let (mut wallet, _) = get_funded_wallet(get_test_wpkh(), get_test_tr_single_sig_xprv());
-
-    let psbt1 = new_tx!(wallet);
-    let change_derivation_1 = psbt1
-        .unsigned_tx
-        .output
-        .iter()
-        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
-        .unwrap();
-    assert_eq!(change_derivation_1, (KeychainKind::Internal, 0));
-
-    let psbt2 = new_tx!(wallet);
-
-    let change_derivation_2 = psbt2
-        .unsigned_tx
-        .output
-        .iter()
-        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
-        .unwrap();
-    assert_eq!(change_derivation_2, (KeychainKind::Internal, 1));
-
-    wallet.cancel_tx(&psbt1.extract_tx().expect("failed to extract tx"));
-
-    let psbt3 = new_tx!(wallet);
-    let change_derivation_3 = psbt3
-        .unsigned_tx
-        .output
-        .iter()
-        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
-        .unwrap();
-    assert_eq!(change_derivation_3, (KeychainKind::Internal, 0));
-
-    let psbt3 = new_tx!(wallet);
-    let change_derivation_3 = psbt3
-        .unsigned_tx
-        .output
-        .iter()
-        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
-        .unwrap();
-    assert_eq!(change_derivation_3, (KeychainKind::Internal, 2));
-
-    wallet.cancel_tx(&psbt3.extract_tx().expect("failed to extract tx"));
-
-    let psbt3 = new_tx!(wallet);
-    let change_derivation_4 = psbt3
-        .unsigned_tx
-        .output
-        .iter()
-        .find_map(|txout| wallet.derivation_of_spk(txout.script_pubkey.clone()))
-        .unwrap();
-    assert_eq!(change_derivation_4, (KeychainKind::Internal, 2));
-}
-
-#[test]
-fn test_thread_safety() {
-    fn thread_safe<T: Send + Sync>() {}
-    thread_safe::<Wallet>(); // compiles only if true
-    thread_safe::<PersistedWallet<bdk_chain::rusqlite::Connection>>();
-}
-
-#[test]
-fn single_descriptor_wallet_can_create_tx_and_receive_change() {
-    // create single descriptor wallet and fund it
-    let mut wallet = Wallet::create_single(get_test_tr_single_sig_xprv())
-        .network(Network::Testnet)
-        .create_wallet_no_persist()
-        .unwrap();
-    assert_eq!(wallet.keychains().count(), 1);
-    let amt = Amount::from_sat(5_000);
-    receive_output(&mut wallet, 2 * amt.to_sat(), ReceiveTo::Mempool(2));
-    // create spend tx that produces a change output
-    let addr = Address::from_str("bcrt1qc6fweuf4xjvz4x3gx3t9e0fh4hvqyu2qw4wvxm")
-        .unwrap()
-        .assume_checked();
-    let mut builder = wallet.build_tx();
-    builder.add_recipient(addr.script_pubkey(), amt);
-    let mut psbt = builder.finish().unwrap();
-    assert!(wallet.sign(&mut psbt, SignOptions::default()).unwrap());
-    let tx = psbt.extract_tx().unwrap();
-    let _txid = tx.compute_txid();
-    insert_tx(&mut wallet, tx);
-    let unspent: Vec<_> = wallet.list_unspent().collect();
-    assert_eq!(unspent.len(), 1);
-    let utxo = unspent.first().unwrap();
-    assert!(utxo.txout.value < amt);
-    assert_eq!(
-        utxo.keychain,
-        KeychainKind::External,
-        "tx change should go to external keychain"
-    );
-}
-
-#[test]
-fn test_transactions_sort_by() {
-    let (mut wallet, _txid) = get_funded_wallet_wpkh();
-    receive_output(&mut wallet, 25_000, ReceiveTo::Mempool(0));
-
-    // sort by chain position, unconfirmed then confirmed by descending block height
-    let sorted_txs: Vec<WalletTx> =
-        wallet.transactions_sort_by(|t1, t2| t2.chain_position.cmp(&t1.chain_position));
-    let conf_heights: Vec<Option<u32>> = sorted_txs
-        .iter()
-        .map(|tx| tx.chain_position.confirmation_height_upper_bound())
-        .collect();
-    assert_eq!([None, Some(2000), Some(1000)], conf_heights.as_slice());
-}
-
-#[test]
-fn test_tx_builder_is_send_safe() {
-    let (mut wallet, _txid) = get_funded_wallet_wpkh();
-    let _box: Box<dyn Send + Sync> = Box::new(wallet.build_tx());
-}
-
-#[test]
-fn test_wallet_transactions_relevant() {
-    let (mut test_wallet, _txid) = get_funded_wallet_wpkh();
-    let relevant_tx_count_before = test_wallet.transactions().count();
-    let full_tx_count_before = test_wallet.tx_graph().full_txs().count();
-    let chain_tip = test_wallet.local_chain().tip().block_id();
-    let canonical_tx_count_before = test_wallet
-        .tx_graph()
-        .list_canonical_txs(test_wallet.local_chain(), chain_tip)
-        .count();
-
-    // add not relevant transaction to test wallet
-    let (other_external_desc, other_internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
-    let (other_wallet, other_txid) = get_funded_wallet(other_internal_desc, other_external_desc);
-    let test_wallet_update = Update {
-        tx_update: other_wallet.tx_graph().clone().into(),
-        ..Default::default()
-    };
-    test_wallet.apply_update(test_wallet_update).unwrap();
-
-    // verify transaction from other wallet was added but is not in relevant transactions list.
-    let relevant_tx_count_after = test_wallet.transactions().count();
-    let full_tx_count_after = test_wallet.tx_graph().full_txs().count();
-    let canonical_tx_count_after = test_wallet
-        .tx_graph()
-        .list_canonical_txs(test_wallet.local_chain(), chain_tip)
-        .count();
-
-    assert_eq!(relevant_tx_count_before, relevant_tx_count_after);
-    assert!(!test_wallet
-        .transactions()
-        .any(|wallet_tx| wallet_tx.tx_node.txid == other_txid));
-    assert!(test_wallet
-        .tx_graph()
-        .list_canonical_txs(test_wallet.local_chain(), chain_tip)
-        .any(|wallet_tx| wallet_tx.tx_node.txid == other_txid));
-    assert!(full_tx_count_before < full_tx_count_after);
-    assert!(canonical_tx_count_before < canonical_tx_count_after);
-}
diff --git a/example-crates/example_wallet_electrum/Cargo.toml b/example-crates/example_wallet_electrum/Cargo.toml
deleted file mode 100644 (file)
index 946cd05..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "example_wallet_electrum"
-version = "0.2.0"
-edition = "2021"
-
-[dependencies]
-bdk_wallet = { path = "../../crates/wallet", features = ["file_store"] }
-bdk_electrum = { version = "0.21" }
-anyhow = "1"
diff --git a/example-crates/example_wallet_electrum/src/main.rs b/example-crates/example_wallet_electrum/src/main.rs
deleted file mode 100644 (file)
index 5942714..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-use bdk_wallet::file_store::Store;
-use bdk_wallet::Wallet;
-use std::io::Write;
-
-use bdk_electrum::electrum_client;
-use bdk_electrum::BdkElectrumClient;
-use bdk_wallet::bitcoin::Amount;
-use bdk_wallet::bitcoin::Network;
-use bdk_wallet::chain::collections::HashSet;
-use bdk_wallet::{KeychainKind, SignOptions};
-
-const DB_MAGIC: &str = "bdk_wallet_electrum_example";
-const SEND_AMOUNT: Amount = Amount::from_sat(5000);
-const STOP_GAP: usize = 50;
-const BATCH_SIZE: usize = 5;
-
-const NETWORK: Network = Network::Testnet;
-const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002";
-
-fn main() -> Result<(), anyhow::Error> {
-    let db_path = "bdk-electrum-example.db";
-
-    let mut db = Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
-
-    let wallet_opt = Wallet::load()
-        .descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
-        .descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
-        .extract_keys()
-        .check_network(NETWORK)
-        .load_wallet(&mut db)?;
-    let mut wallet = match wallet_opt {
-        Some(wallet) => wallet,
-        None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
-            .network(NETWORK)
-            .create_wallet(&mut db)?,
-    };
-
-    let address = wallet.next_unused_address(KeychainKind::External);
-    wallet.persist(&mut db)?;
-    println!("Generated Address: {}", address);
-
-    let balance = wallet.balance();
-    println!("Wallet balance before syncing: {}", balance.total());
-
-    print!("Syncing...");
-    let client = BdkElectrumClient::new(electrum_client::Client::new(ELECTRUM_URL)?);
-
-    // Populate the electrum client's transaction cache so it doesn't redownload transaction we
-    // already have.
-    client.populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx));
-
-    let request = wallet.start_full_scan().inspect({
-        let mut stdout = std::io::stdout();
-        let mut once = HashSet::<KeychainKind>::new();
-        move |k, spk_i, _| {
-            if once.insert(k) {
-                print!("\nScanning keychain [{:?}]", k);
-            }
-            print!(" {:<3}", spk_i);
-            stdout.flush().expect("must flush");
-        }
-    });
-
-    let update = client.full_scan(request, STOP_GAP, BATCH_SIZE, false)?;
-
-    println!();
-
-    wallet.apply_update(update)?;
-    wallet.persist(&mut db)?;
-
-    let balance = wallet.balance();
-    println!("Wallet balance after syncing: {}", balance.total());
-
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
-
-    let mut tx_builder = wallet.build_tx();
-    tx_builder.add_recipient(address.script_pubkey(), SEND_AMOUNT);
-
-    let mut psbt = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
-
-    let tx = psbt.extract_tx()?;
-    client.transaction_broadcast(&tx)?;
-    println!("Tx broadcasted! Txid: {}", tx.compute_txid());
-
-    Ok(())
-}
diff --git a/example-crates/example_wallet_esplora_async/Cargo.toml b/example-crates/example_wallet_esplora_async/Cargo.toml
deleted file mode 100644 (file)
index 21b27be..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "example_wallet_esplora_async"
-version = "0.2.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-bdk_wallet = { path = "../../crates/wallet", features = ["rusqlite"] }
-bdk_esplora = { version = "0.20", features = ["async-https", "tokio"] }
-tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
-anyhow = "1"
diff --git a/example-crates/example_wallet_esplora_async/src/main.rs b/example-crates/example_wallet_esplora_async/src/main.rs
deleted file mode 100644 (file)
index b6ab5d6..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-use std::{collections::BTreeSet, io::Write};
-
-use anyhow::Ok;
-use bdk_esplora::{esplora_client, EsploraAsyncExt};
-use bdk_wallet::{
-    bitcoin::{Amount, Network},
-    rusqlite::Connection,
-    KeychainKind, SignOptions, Wallet,
-};
-
-const SEND_AMOUNT: Amount = Amount::from_sat(5000);
-const STOP_GAP: usize = 5;
-const PARALLEL_REQUESTS: usize = 5;
-
-const DB_PATH: &str = "bdk-example-esplora-async.sqlite";
-const NETWORK: Network = Network::Signet;
-const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";
-
-#[tokio::main]
-async fn main() -> Result<(), anyhow::Error> {
-    let mut conn = Connection::open(DB_PATH)?;
-
-    let wallet_opt = Wallet::load()
-        .descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
-        .descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
-        .extract_keys()
-        .check_network(NETWORK)
-        .load_wallet(&mut conn)?;
-    let mut wallet = match wallet_opt {
-        Some(wallet) => wallet,
-        None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
-            .network(NETWORK)
-            .create_wallet(&mut conn)?,
-    };
-
-    let address = wallet.next_unused_address(KeychainKind::External);
-    wallet.persist(&mut conn)?;
-    println!("Next unused address: ({}) {}", address.index, address);
-
-    let balance = wallet.balance();
-    println!("Wallet balance before syncing: {}", balance.total());
-
-    print!("Syncing...");
-    let client = esplora_client::Builder::new(ESPLORA_URL).build_async()?;
-
-    let request = wallet.start_full_scan().inspect({
-        let mut stdout = std::io::stdout();
-        let mut once = BTreeSet::<KeychainKind>::new();
-        move |keychain, spk_i, _| {
-            if once.insert(keychain) {
-                print!("\nScanning keychain [{:?}]", keychain);
-            }
-            print!(" {:<3}", spk_i);
-            stdout.flush().expect("must flush")
-        }
-    });
-
-    let update = client
-        .full_scan(request, STOP_GAP, PARALLEL_REQUESTS)
-        .await?;
-
-    wallet.apply_update(update)?;
-    wallet.persist(&mut conn)?;
-    println!();
-
-    let balance = wallet.balance();
-    println!("Wallet balance after syncing: {}", balance.total());
-
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
-
-    let mut tx_builder = wallet.build_tx();
-    tx_builder.add_recipient(address.script_pubkey(), SEND_AMOUNT);
-
-    let mut psbt = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
-
-    let tx = psbt.extract_tx()?;
-    client.broadcast(&tx).await?;
-    println!("Tx broadcasted! Txid: {}", tx.compute_txid());
-
-    Ok(())
-}
diff --git a/example-crates/example_wallet_esplora_blocking/Cargo.toml b/example-crates/example_wallet_esplora_blocking/Cargo.toml
deleted file mode 100644 (file)
index ec8cb54..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "example_wallet_esplora_blocking"
-version = "0.2.0"
-edition = "2021"
-publish = false
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-bdk_wallet = { path = "../../crates/wallet", features = ["file_store"] }
-bdk_esplora = { version = "0.20", features = ["blocking"] }
-anyhow = "1"
diff --git a/example-crates/example_wallet_esplora_blocking/src/main.rs b/example-crates/example_wallet_esplora_blocking/src/main.rs
deleted file mode 100644 (file)
index 7966f19..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-use std::{collections::BTreeSet, io::Write};
-
-use bdk_esplora::{esplora_client, EsploraExt};
-use bdk_wallet::{
-    bitcoin::{Amount, Network},
-    file_store::Store,
-    KeychainKind, SignOptions, Wallet,
-};
-
-const DB_MAGIC: &str = "bdk_wallet_esplora_example";
-const DB_PATH: &str = "bdk-example-esplora-blocking.db";
-const SEND_AMOUNT: Amount = Amount::from_sat(5000);
-const STOP_GAP: usize = 5;
-const PARALLEL_REQUESTS: usize = 5;
-
-const NETWORK: Network = Network::Signet;
-const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
-const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
-const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";
-
-fn main() -> Result<(), anyhow::Error> {
-    let mut db = Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), DB_PATH)?;
-
-    let wallet_opt = Wallet::load()
-        .descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
-        .descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
-        .extract_keys()
-        .check_network(NETWORK)
-        .load_wallet(&mut db)?;
-    let mut wallet = match wallet_opt {
-        Some(wallet) => wallet,
-        None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
-            .network(NETWORK)
-            .create_wallet(&mut db)?,
-    };
-
-    let address = wallet.next_unused_address(KeychainKind::External);
-    wallet.persist(&mut db)?;
-    println!(
-        "Next unused address: ({}) {}",
-        address.index, address.address
-    );
-
-    let balance = wallet.balance();
-    println!("Wallet balance before syncing: {}", balance.total());
-
-    print!("Syncing...");
-    let client = esplora_client::Builder::new(ESPLORA_URL).build_blocking();
-
-    let request = wallet.start_full_scan().inspect({
-        let mut stdout = std::io::stdout();
-        let mut once = BTreeSet::<KeychainKind>::new();
-        move |keychain, spk_i, _| {
-            if once.insert(keychain) {
-                print!("\nScanning keychain [{:?}] ", keychain);
-            }
-            print!(" {:<3}", spk_i);
-            stdout.flush().expect("must flush")
-        }
-    });
-
-    let update = client.full_scan(request, STOP_GAP, PARALLEL_REQUESTS)?;
-
-    wallet.apply_update(update)?;
-    wallet.persist(&mut db)?;
-    println!();
-
-    let balance = wallet.balance();
-    println!("Wallet balance after syncing: {}", balance.total());
-
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
-
-    let mut tx_builder = wallet.build_tx();
-    tx_builder.add_recipient(address.script_pubkey(), SEND_AMOUNT);
-
-    let mut psbt = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
-
-    let tx = psbt.extract_tx()?;
-    client.broadcast(&tx)?;
-    println!("Tx broadcasted! Txid: {}", tx.compute_txid());
-
-    Ok(())
-}
diff --git a/example-crates/example_wallet_rpc/Cargo.toml b/example-crates/example_wallet_rpc/Cargo.toml
deleted file mode 100644 (file)
index b20447e..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-[package]
-name = "example_wallet_rpc"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-bdk_wallet = { path = "../../crates/wallet", features = ["file_store"] }
-bdk_bitcoind_rpc = { version = "0.18" }
-
-anyhow = "1"
-clap = { version = "4.5.17", features = ["derive", "env"] }
-ctrlc = "2.0.1"
diff --git a/example-crates/example_wallet_rpc/README.md b/example-crates/example_wallet_rpc/README.md
deleted file mode 100644 (file)
index 9edb7ef..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-# Wallet RPC Example 
-
-```
-$ cargo run --bin example_wallet_rpc -- --help
-
-Bitcoind RPC example using `bdk_wallet::Wallet`
-
-Usage: example_wallet_rpc [OPTIONS] <DESCRIPTOR> [CHANGE_DESCRIPTOR]
-
-Arguments:
-  <DESCRIPTOR>         Wallet descriptor [env: DESCRIPTOR=]
-  [CHANGE_DESCRIPTOR]  Wallet change descriptor [env: CHANGE_DESCRIPTOR=]
-
-Options:
-      --start-height <START_HEIGHT>  Earliest block height to start sync from [env: START_HEIGHT=] [default: 0]
-
-      --network <NETWORK>            Bitcoin network to connect to [env: BITCOIN_NETWORK=] [default: regtest]
-
-      --db-path <DB_PATH>            Where to store wallet data [env: BDK_DB_PATH=] [default: .bdk_wallet_rpc_example.db]
-
-      --url <URL>                    RPC URL [env: RPC_URL=] [default: 127.0.0.1:18443]
-
-      --rpc-cookie <RPC_COOKIE>      RPC auth cookie file [env: RPC_COOKIE=]
-
-      --rpc-user <RPC_USER>          RPC auth username [env: RPC_USER=]
-
-      --rpc-pass <RPC_PASS>          RPC auth password [env: RPC_PASS=]
-
-  -h, --help                         Print help
-  
-  -V, --version                      Print version
-
-```
-
diff --git a/example-crates/example_wallet_rpc/src/main.rs b/example-crates/example_wallet_rpc/src/main.rs
deleted file mode 100644 (file)
index 2021f83..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-use bdk_bitcoind_rpc::{
-    bitcoincore_rpc::{Auth, Client, RpcApi},
-    Emitter,
-};
-use bdk_wallet::{
-    bitcoin::{Block, Network, Transaction},
-    file_store::Store,
-    KeychainKind, Wallet,
-};
-use clap::{self, Parser};
-use std::{path::PathBuf, sync::mpsc::sync_channel, thread::spawn, time::Instant};
-
-const DB_MAGIC: &str = "bdk-rpc-wallet-example";
-
-/// Bitcoind RPC example using `bdk_wallet::Wallet`.
-///
-/// This syncs the chain block-by-block and prints the current balance, transaction count and UTXO
-/// count.
-#[derive(Parser, Debug)]
-#[clap(author, version, about, long_about = None)]
-#[clap(propagate_version = true)]
-pub struct Args {
-    /// Wallet descriptor
-    #[clap(env = "DESCRIPTOR")]
-    pub descriptor: String,
-    /// Wallet change descriptor
-    #[clap(env = "CHANGE_DESCRIPTOR")]
-    pub change_descriptor: Option<String>,
-    /// Earliest block height to start sync from
-    #[clap(env = "START_HEIGHT", long, default_value = "0")]
-    pub start_height: u32,
-    /// Bitcoin network to connect to
-    #[clap(env = "BITCOIN_NETWORK", long, default_value = "regtest")]
-    pub network: Network,
-    /// Where to store wallet data
-    #[clap(
-        env = "BDK_DB_PATH",
-        long,
-        default_value = ".bdk_wallet_rpc_example.db"
-    )]
-    pub db_path: PathBuf,
-
-    /// RPC URL
-    #[clap(env = "RPC_URL", long, default_value = "127.0.0.1:18443")]
-    pub url: String,
-    /// RPC auth cookie file
-    #[clap(env = "RPC_COOKIE", long)]
-    pub rpc_cookie: Option<PathBuf>,
-    /// RPC auth username
-    #[clap(env = "RPC_USER", long)]
-    pub rpc_user: Option<String>,
-    /// RPC auth password
-    #[clap(env = "RPC_PASS", long)]
-    pub rpc_pass: Option<String>,
-}
-
-impl Args {
-    fn client(&self) -> anyhow::Result<Client> {
-        Ok(Client::new(
-            &self.url,
-            match (&self.rpc_cookie, &self.rpc_user, &self.rpc_pass) {
-                (None, None, None) => Auth::None,
-                (Some(path), _, _) => Auth::CookieFile(path.clone()),
-                (_, Some(user), Some(pass)) => Auth::UserPass(user.clone(), pass.clone()),
-                (_, Some(_), None) => panic!("rpc auth: missing rpc_pass"),
-                (_, None, Some(_)) => panic!("rpc auth: missing rpc_user"),
-            },
-        )?)
-    }
-}
-
-#[derive(Debug)]
-enum Emission {
-    SigTerm,
-    Block(bdk_bitcoind_rpc::BlockEvent<Block>),
-    Mempool(Vec<(Transaction, u64)>),
-}
-
-fn main() -> anyhow::Result<()> {
-    let args = Args::parse();
-
-    let rpc_client = args.client()?;
-    println!(
-        "Connected to Bitcoin Core RPC at {:?}",
-        rpc_client.get_blockchain_info().unwrap()
-    );
-
-    let start_load_wallet = Instant::now();
-    let mut db =
-        Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), args.db_path)?;
-    let wallet_opt = Wallet::load()
-        .descriptor(KeychainKind::External, Some(args.descriptor.clone()))
-        .descriptor(KeychainKind::Internal, args.change_descriptor.clone())
-        .extract_keys()
-        .check_network(args.network)
-        .load_wallet(&mut db)?;
-    let mut wallet = match wallet_opt {
-        Some(wallet) => wallet,
-        None => match &args.change_descriptor {
-            Some(change_desc) => Wallet::create(args.descriptor.clone(), change_desc.clone())
-                .network(args.network)
-                .create_wallet(&mut db)?,
-            None => Wallet::create_single(args.descriptor.clone())
-                .network(args.network)
-                .create_wallet(&mut db)?,
-        },
-    };
-    println!(
-        "Loaded wallet in {}s",
-        start_load_wallet.elapsed().as_secs_f32()
-    );
-
-    let balance = wallet.balance();
-    println!("Wallet balance before syncing: {}", balance.total());
-
-    let wallet_tip = wallet.latest_checkpoint();
-    println!(
-        "Wallet tip: {} at height {}",
-        wallet_tip.hash(),
-        wallet_tip.height()
-    );
-
-    let (sender, receiver) = sync_channel::<Emission>(21);
-
-    let signal_sender = sender.clone();
-    ctrlc::set_handler(move || {
-        signal_sender
-            .send(Emission::SigTerm)
-            .expect("failed to send sigterm")
-    });
-
-    let emitter_tip = wallet_tip.clone();
-    spawn(move || -> Result<(), anyhow::Error> {
-        let mut emitter = Emitter::new(&rpc_client, emitter_tip, args.start_height);
-        while let Some(emission) = emitter.next_block()? {
-            sender.send(Emission::Block(emission))?;
-        }
-        sender.send(Emission::Mempool(emitter.mempool()?))?;
-        Ok(())
-    });
-
-    let mut blocks_received = 0_usize;
-    for emission in receiver {
-        match emission {
-            Emission::SigTerm => {
-                println!("Sigterm received, exiting...");
-                break;
-            }
-            Emission::Block(block_emission) => {
-                blocks_received += 1;
-                let height = block_emission.block_height();
-                let hash = block_emission.block_hash();
-                let connected_to = block_emission.connected_to();
-                let start_apply_block = Instant::now();
-                wallet.apply_block_connected_to(&block_emission.block, height, connected_to)?;
-                wallet.persist(&mut db)?;
-                let elapsed = start_apply_block.elapsed().as_secs_f32();
-                println!(
-                    "Applied block {} at height {} in {}s",
-                    hash, height, elapsed
-                );
-            }
-            Emission::Mempool(mempool_emission) => {
-                let start_apply_mempool = Instant::now();
-                wallet.apply_unconfirmed_txs(mempool_emission);
-                wallet.persist(&mut db)?;
-                println!(
-                    "Applied unconfirmed transactions in {}s",
-                    start_apply_mempool.elapsed().as_secs_f32()
-                );
-                break;
-            }
-        }
-    }
-    let wallet_tip_end = wallet.latest_checkpoint();
-    let balance = wallet.balance();
-    println!(
-        "Synced {} blocks in {}s",
-        blocks_received,
-        start_load_wallet.elapsed().as_secs_f32(),
-    );
-    println!(
-        "Wallet tip is '{}:{}'",
-        wallet_tip_end.height(),
-        wallet_tip_end.hash()
-    );
-    println!("Wallet balance is {}", balance.total());
-    println!(
-        "Wallet has {} transactions and {} utxos",
-        wallet.transactions().count(),
-        wallet.list_unspent().count()
-    );
-
-    Ok(())
-}