• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bitcoindevkit / bdk / 9507819776

13 Jun 2024 10:52PM UTC coverage: 83.064% (-0.05%) from 83.114%
9507819776

push

github

notmandatory
Merge bitcoindevkit/bdk#1454: Refactor wallet and persist mod, remove bdk_persist crate

ec36c7ecc feat(example): use changeset staging with rpc polling example (志宇)
19328d499 feat(wallet)!: change persist API to use `StageExt` and `StageExtAsync` (志宇)
2e40b0118 feat(chain): reintroduce a way to stage changesets before persisting (志宇)
36e82ec68 chore(chain): relax `miniscript` feature flag scope (志宇)
9e97ac033 refactor(persist): update file_store, sqlite, wallet to use bdk_chain::persist (Steve Myers)
54b0c11cb feat(persist): add PersistAsync trait and StagedPersistAsync struct (Steve Myers)
aa640ab27 refactor(persist): rename PersistBackend to Persist, move to chain crate (Steve Myers)

Pull request description:

  ### Description

  Sorry to submit another refactor PR for the persist related stuff, but I think it's worth revisiting. My primary motivations are:

  1. remove `db` from `Wallet` so users have the ability to use `async` storage crates, for example using `sqlx`. I updated docs and examples to let users know they are responsible for persisting changes.
  2. remove the `anyhow` dependency everywhere (except as a dev test dependency). It really doesn't belong in a lib and by removing persistence from `Wallet` it isn't needed.
  3. remove the `bdk_persist` crate and revert back to the original design with generic error types. I kept the `Debug` and `Display` constrains on persist errors so they could still be used with the `anyhow!` macro.

  ### Notes to the reviewers

  I also replaced/renamed old `Persist` with `StagedPersist` struct inspired by #1453, it is only used in examples. The `Wallet` handles it's own staging.

  ### Changelog notice

  Changed

  - Removed `db` from `Wallet`, users are now responsible for persisting ... (continued)

176 of 248 new or added lines in 6 files covered. (70.97%)

3 existing lines in 2 files now uncovered.

11153 of 13427 relevant lines covered (83.06%)

16771.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.45
/crates/wallet/src/wallet/mod.rs
1
// Bitcoin Dev Kit
2
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3
//
4
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5
//
6
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9
// You may not use this file except in accordance with one or both of these
10
// licenses.
11

12
//! Wallet
13
//!
14
//! This module defines the [`Wallet`].
15
use crate::collections::{BTreeMap, HashMap};
16
use alloc::{
17
    boxed::Box,
18
    string::{String, ToString},
19
    sync::Arc,
20
    vec::Vec,
21
};
22
pub use bdk_chain::keychain::Balance;
23
use bdk_chain::{
24
    indexed_tx_graph,
25
    keychain::KeychainTxOutIndex,
26
    local_chain::{
27
        self, ApplyHeaderError, CannotConnectError, CheckPoint, CheckPointIter, LocalChain,
28
    },
29
    persist::{PersistBackend, StageExt},
30
    spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult},
31
    tx_graph::{CanonicalTx, TxGraph},
32
    Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut,
33
    Indexed, IndexedTxGraph,
34
};
35
use bitcoin::secp256k1::{All, Secp256k1};
36
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
37
use bitcoin::{
38
    absolute, psbt, Address, Block, FeeRate, Network, OutPoint, Script, ScriptBuf, Sequence,
39
    Transaction, TxOut, Txid, Witness,
40
};
41
use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
42
use bitcoin::{constants::genesis_block, Amount};
43
use core::fmt;
44
use core::ops::Deref;
45
use descriptor::error::Error as DescriptorError;
46
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
47

48
use bdk_chain::tx_graph::CalculateFeeError;
49

50
pub mod coin_selection;
51
pub mod export;
52
pub mod signer;
53
pub mod tx_builder;
54
pub(crate) mod utils;
55

56
pub mod error;
57

58
pub use utils::IsDust;
59

60
use coin_selection::DefaultCoinSelectionAlgorithm;
61
use signer::{SignOptions, SignerOrdering, SignersContainer, TransactionSigner};
62
use tx_builder::{FeePolicy, TxBuilder, TxParams};
63
use utils::{check_nsequence_rbf, After, Older, SecpCtx};
64

65
use crate::descriptor::policy::BuildSatisfaction;
66
use crate::descriptor::{
67
    self, calc_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DescriptorMeta,
68
    ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
69
};
70
use crate::psbt::PsbtUtils;
71
use crate::signer::SignerError;
72
use crate::types::*;
73
use crate::wallet::coin_selection::Excess::{Change, NoChange};
74
use crate::wallet::error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError};
75

76
use self::coin_selection::Error;
77

78
const COINBASE_MATURITY: u32 = 100;
79

80
/// A Bitcoin wallet
81
///
82
/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
83
/// Its main components are:
84
///
85
/// 1. output *descriptors* from which it can derive addresses.
86
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
87
///
88
/// The user is responsible for loading and writing wallet changes using an implementation of
89
/// [`PersistBackend`]. See individual functions and example for instructions on when [`Wallet`]
90
/// state needs to be persisted.
91
///
92
/// [`PersistBackend`]: bdk_chain::persist::PersistBackend
93
/// [`signer`]: crate::signer
94
#[derive(Debug)]
95
pub struct Wallet {
96
    signers: Arc<SignersContainer>,
97
    change_signers: Arc<SignersContainer>,
98
    chain: LocalChain,
99
    indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
100
    stage: ChangeSet,
101
    network: Network,
102
    secp: SecpCtx,
103
}
104

105
/// An update to [`Wallet`].
106
///
107
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
108
#[derive(Debug, Clone, Default)]
109
pub struct Update {
110
    /// Contains the last active derivation indices per keychain (`K`), which is used to update the
111
    /// [`KeychainTxOutIndex`].
112
    pub last_active_indices: BTreeMap<KeychainKind, u32>,
113

114
    /// Update for the wallet's internal [`TxGraph`].
115
    pub graph: TxGraph<ConfirmationTimeHeightAnchor>,
116

117
    /// Update for the wallet's internal [`LocalChain`].
118
    ///
119
    /// [`LocalChain`]: local_chain::LocalChain
120
    pub chain: Option<CheckPoint>,
121
}
122

123
impl From<FullScanResult<KeychainKind>> for Update {
124
    fn from(value: FullScanResult<KeychainKind>) -> Self {
×
125
        Self {
×
126
            last_active_indices: value.last_active_indices,
×
127
            graph: value.graph_update,
×
128
            chain: Some(value.chain_update),
×
129
        }
×
130
    }
×
131
}
132

133
impl From<SyncResult> for Update {
134
    fn from(value: SyncResult) -> Self {
×
135
        Self {
×
136
            last_active_indices: BTreeMap::new(),
×
137
            graph: value.graph_update,
×
138
            chain: Some(value.chain_update),
×
139
        }
×
140
    }
×
141
}
142

143
/// The changes made to a wallet by applying an [`Update`].
144
pub type ChangeSet =
145
    bdk_chain::persist::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
146

147
/// A derived address and the index it was found at.
148
/// For convenience this automatically derefs to `Address`
149
#[derive(Debug, PartialEq, Eq)]
150
pub struct AddressInfo {
151
    /// Child index of this address
152
    pub index: u32,
153
    /// Address
154
    pub address: Address,
155
    /// Type of keychain
156
    pub keychain: KeychainKind,
157
}
158

159
impl Deref for AddressInfo {
160
    type Target = Address;
161

162
    fn deref(&self) -> &Self::Target {
888✔
163
        &self.address
888✔
164
    }
888✔
165
}
166

167
impl fmt::Display for AddressInfo {
168
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208✔
169
        write!(f, "{}", self.address)
208✔
170
    }
208✔
171
}
172

173
/// The error type when constructing a fresh [`Wallet`].
174
///
175
/// Methods [`new`] and [`new_with_genesis_hash`] may return this error.
176
///
177
/// [`new`]: Wallet::new
178
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
179
#[derive(Debug)]
180
pub enum NewError {
181
    /// There was problem with the passed-in descriptor(s).
182
    Descriptor(crate::descriptor::DescriptorError),
183
}
184

185
impl fmt::Display for NewError {
186
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
187
        match self {
×
188
            NewError::Descriptor(e) => e.fmt(f),
×
189
        }
×
UNCOV
190
    }
×
191
}
192

193
#[cfg(feature = "std")]
194
impl std::error::Error for NewError {}
195

196
/// The error type when loading a [`Wallet`] from a [`ChangeSet`].
197
///
198
/// Method [`load_from_changeset`] may return this error.
199
///
200
/// [`load_from_changeset`]: Wallet::load_from_changeset
201
#[derive(Debug)]
202
pub enum LoadError {
203
    /// There was a problem with the passed-in descriptor(s).
204
    Descriptor(crate::descriptor::DescriptorError),
205
    /// Data loaded from persistence is missing network type.
206
    MissingNetwork,
207
    /// Data loaded from persistence is missing genesis hash.
208
    MissingGenesis,
209
    /// Data loaded from persistence is missing descriptor.
210
    MissingDescriptor(KeychainKind),
211
}
212

213
impl fmt::Display for LoadError {
214
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
215
        match self {
×
216
            LoadError::Descriptor(e) => e.fmt(f),
×
217
            LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
×
UNCOV
218
            LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
×
219
            LoadError::MissingDescriptor(k) => {
×
220
                write!(f, "loaded data is missing descriptor for keychain {k:?}")
×
221
            }
222
        }
223
    }
×
224
}
225

226
#[cfg(feature = "std")]
227
impl std::error::Error for LoadError {}
228

229
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
230
///
231
/// Methods [`new_or_load`] and [`new_or_load_with_genesis_hash`] may return this error.
232
///
233
/// [`new_or_load`]: Wallet::new_or_load
234
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
235
#[derive(Debug)]
236
pub enum NewOrLoadError {
237
    /// There is a problem with the passed-in descriptor.
238
    Descriptor(crate::descriptor::DescriptorError),
239
    /// The loaded genesis hash does not match what was provided.
240
    LoadedGenesisDoesNotMatch {
241
        /// The expected genesis block hash.
242
        expected: BlockHash,
243
        /// The block hash loaded from persistence.
244
        got: Option<BlockHash>,
245
    },
246
    /// The loaded network type does not match what was provided.
247
    LoadedNetworkDoesNotMatch {
248
        /// The expected network type.
249
        expected: Network,
250
        /// The network type loaded from persistence.
251
        got: Option<Network>,
252
    },
253
    /// The loaded desccriptor does not match what was provided.
254
    LoadedDescriptorDoesNotMatch {
255
        /// The descriptor loaded from persistence.
256
        got: Option<ExtendedDescriptor>,
257
        /// The keychain of the descriptor not matching
258
        keychain: KeychainKind,
259
    },
260
}
261

262
impl fmt::Display for NewOrLoadError {
263
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
264
        match self {
×
265
            NewOrLoadError::Descriptor(e) => e.fmt(f),
×
266
            NewOrLoadError::LoadedGenesisDoesNotMatch { expected, got } => {
×
267
                write!(f, "loaded genesis hash is not {}, got {:?}", expected, got)
×
268
            }
269
            NewOrLoadError::LoadedNetworkDoesNotMatch { expected, got } => {
×
270
                write!(f, "loaded network type is not {}, got {:?}", expected, got)
×
271
            }
272
            NewOrLoadError::LoadedDescriptorDoesNotMatch { got, keychain } => {
×
273
                write!(
×
274
                    f,
×
275
                    "loaded descriptor is different from what was provided, got {:?} for keychain {:?}",
×
276
                    got, keychain
×
277
                )
×
278
            }
279
        }
280
    }
×
281
}
282

283
#[cfg(feature = "std")]
284
impl std::error::Error for NewOrLoadError {}
285

286
/// An error that may occur when inserting a transaction into [`Wallet`].
287
#[derive(Debug)]
288
pub enum InsertTxError {
289
    /// The error variant that occurs when the caller attempts to insert a transaction with a
290
    /// confirmation height that is greater than the internal chain tip.
291
    ConfirmationHeightCannotBeGreaterThanTip {
292
        /// The internal chain's tip height.
293
        tip_height: u32,
294
        /// The introduced transaction's confirmation height.
295
        tx_height: u32,
296
    },
297
}
298

299
impl fmt::Display for InsertTxError {
300
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
301
        match self {
×
302
            InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
×
303
                tip_height,
×
304
                tx_height,
×
305
            } => {
×
306
                write!(f, "cannot insert tx with confirmation height ({}) higher than internal tip height ({})", tx_height, tip_height)
×
307
            }
×
308
        }
×
309
    }
×
310
}
311

312
#[cfg(feature = "std")]
313
impl std::error::Error for InsertTxError {}
314

315
/// An error that may occur when applying a block to [`Wallet`].
316
#[derive(Debug)]
317
pub enum ApplyBlockError {
318
    /// Occurs when the update chain cannot connect with original chain.
319
    CannotConnect(CannotConnectError),
320
    /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
321
    UnexpectedConnectedToHash {
322
        /// Block hash of `connected_to`.
323
        connected_to_hash: BlockHash,
324
        /// Expected block hash of `connected_to`, as derived from `block`.
325
        expected_hash: BlockHash,
326
    },
327
}
328

329
impl fmt::Display for ApplyBlockError {
330
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
331
        match self {
×
332
            ApplyBlockError::CannotConnect(err) => err.fmt(f),
×
333
            ApplyBlockError::UnexpectedConnectedToHash {
334
                expected_hash: block_hash,
×
335
                connected_to_hash: checkpoint_hash,
×
336
            } => write!(
×
337
                f,
×
338
                "`connected_to` hash {} differs from the expected hash {} (which is derived from `block`)",
×
339
                checkpoint_hash, block_hash
×
340
            ),
×
341
        }
342
    }
×
343
}
344

345
#[cfg(feature = "std")]
346
impl std::error::Error for ApplyBlockError {}
347

348
impl Wallet {
349
    /// Initialize an empty [`Wallet`].
350
    pub fn new<E: IntoWalletDescriptor>(
151✔
351
        descriptor: E,
151✔
352
        change_descriptor: E,
151✔
353
        network: Network,
151✔
354
    ) -> Result<Self, NewError> {
151✔
355
        let genesis_hash = genesis_block(network).block_hash();
151✔
356
        Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
151✔
357
    }
151✔
358

359
    /// Initialize an empty [`Wallet`] with a custom genesis hash.
360
    ///
361
    /// This is like [`Wallet::new`] with an additional `genesis_hash` parameter. This is useful
362
    /// for syncing from alternative networks.
363
    pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
153✔
364
        descriptor: E,
153✔
365
        change_descriptor: E,
153✔
366
        network: Network,
153✔
367
        genesis_hash: BlockHash,
153✔
368
    ) -> Result<Self, NewError> {
153✔
369
        let secp = Secp256k1::new();
153✔
370
        let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
153✔
371
        let mut index = KeychainTxOutIndex::<KeychainKind>::default();
153✔
372

373
        let (signers, change_signers) =
151✔
374
            create_signers(&mut index, &secp, descriptor, change_descriptor, network)
153✔
375
                .map_err(NewError::Descriptor)?;
153✔
376

377
        let indexed_graph = IndexedTxGraph::new(index);
151✔
378

151✔
379
        let staged = ChangeSet {
151✔
380
            chain: chain_changeset,
151✔
381
            indexed_tx_graph: indexed_graph.initial_changeset(),
151✔
382
            network: Some(network),
151✔
383
        };
151✔
384

151✔
385
        Ok(Wallet {
151✔
386
            signers,
151✔
387
            change_signers,
151✔
388
            network,
151✔
389
            chain,
151✔
390
            indexed_graph,
151✔
391
            stage: staged,
151✔
392
            secp,
151✔
393
        })
151✔
394
    }
153✔
395

396
    /// Load [`Wallet`] from the given previously persisted [`ChangeSet`].
397
    ///
398
    /// Note that the descriptor secret keys are not persisted to the db; this means that after
399
    /// calling this method the [`Wallet`] **won't** know the secret keys, and as such, won't be
400
    /// able to sign transactions.
401
    ///
402
    /// If you wish to use the wallet to sign transactions, you need to add the secret keys
403
    /// manually to the [`Wallet`]:
404
    ///
405
    /// ```rust,no_run
406
    /// # use bdk_wallet::Wallet;
407
    /// # use bdk_wallet::signer::{SignersContainer, SignerOrdering};
408
    /// # use bdk_wallet::descriptor::Descriptor;
409
    /// # use bitcoin::key::Secp256k1;
410
    /// # use bdk_wallet::KeychainKind;
411
    /// # use bdk_sqlite::{Store, rusqlite::Connection};
412
    /// #
413
    /// # fn main() -> Result<(), anyhow::Error> {
414
    /// # use bdk_chain::persist::PersistBackend;
415
    /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
416
    /// # let file_path = temp_dir.path().join("store.db");
417
    /// # let conn = Connection::open(file_path).expect("must open connection");
418
    /// # let mut db = Store::new(conn).expect("must create db");
419
    /// let secp = Secp256k1::new();
420
    ///
421
    /// let (external_descriptor, external_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)").unwrap();
422
    /// let (internal_descriptor, internal_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)").unwrap();
423
    ///
424
    /// let external_signer_container = SignersContainer::build(external_keymap, &external_descriptor, &secp);
425
    /// let internal_signer_container = SignersContainer::build(internal_keymap, &internal_descriptor, &secp);
426
    /// let changeset = db.load_changes()?.expect("there must be an existing changeset");
427
    /// let mut wallet = Wallet::load_from_changeset(changeset)?;
428
    ///
429
    /// external_signer_container.signers().into_iter()
430
    ///     .for_each(|s| wallet.add_signer(KeychainKind::External, SignerOrdering::default(), s.clone()));
431
    /// internal_signer_container.signers().into_iter()
432
    ///     .for_each(|s| wallet.add_signer(KeychainKind::Internal, SignerOrdering::default(), s.clone()));
433
    /// # Ok(())
434
    /// # }
435
    /// ```
436
    ///
437
    /// Alternatively, you can call [`Wallet::new_or_load`], which will add the private keys of the
438
    /// passed-in descriptors to the [`Wallet`].
439
    pub fn load_from_changeset(changeset: ChangeSet) -> Result<Self, LoadError> {
96✔
440
        let secp = Secp256k1::new();
96✔
441
        let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
96✔
442
        let chain =
96✔
443
            LocalChain::from_changeset(changeset.chain).map_err(|_| LoadError::MissingGenesis)?;
96✔
444
        let mut index = KeychainTxOutIndex::<KeychainKind>::default();
96✔
445
        let descriptor = changeset
96✔
446
            .indexed_tx_graph
96✔
447
            .indexer
96✔
448
            .keychains_added
96✔
449
            .get(&KeychainKind::External)
96✔
450
            .ok_or(LoadError::MissingDescriptor(KeychainKind::External))?
96✔
451
            .clone();
96✔
452
        let change_descriptor = changeset
96✔
453
            .indexed_tx_graph
96✔
454
            .indexer
96✔
455
            .keychains_added
96✔
456
            .get(&KeychainKind::Internal)
96✔
457
            .ok_or(LoadError::MissingDescriptor(KeychainKind::Internal))?
96✔
458
            .clone();
96✔
459

96✔
460
        let (signers, change_signers) =
96✔
461
            create_signers(&mut index, &secp, descriptor, change_descriptor, network)
96✔
462
                .expect("Can't fail: we passed in valid descriptors, recovered from the changeset");
96✔
463

96✔
464
        let mut indexed_graph = IndexedTxGraph::new(index);
96✔
465
        indexed_graph.apply_changeset(changeset.indexed_tx_graph);
96✔
466

96✔
467
        let stage = ChangeSet::default();
96✔
468

96✔
469
        Ok(Wallet {
96✔
470
            signers,
96✔
471
            change_signers,
96✔
472
            chain,
96✔
473
            indexed_graph,
96✔
474
            stage,
96✔
475
            network,
96✔
476
            secp,
96✔
477
        })
96✔
478
    }
96✔
479

480
    /// Either loads [`Wallet`] from the given [`ChangeSet`] or initializes it if one does not exist.
481
    ///
482
    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
483
    ///
484
    /// ```rust,no_run
485
    /// # use bdk_chain::persist::PersistBackend;
486
    /// # use bdk_wallet::Wallet;
487
    /// # use bdk_sqlite::{Store, rusqlite::Connection};
488
    /// # use bitcoin::Network::Testnet;
489
    /// # let conn = Connection::open_in_memory().expect("must open connection");
490
    /// let mut db = Store::new(conn).expect("must create db");
491
    /// let changeset = db.load_changes()?;
492
    ///
493
    /// let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
494
    /// let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
495
    ///
496
    /// let mut wallet = Wallet::new_or_load(external_descriptor, internal_descriptor, changeset, Testnet)?;
497
    /// # Ok::<(), anyhow::Error>(())
498
    /// ```
499
    pub fn new_or_load<E: IntoWalletDescriptor>(
10✔
500
        descriptor: E,
10✔
501
        change_descriptor: E,
10✔
502
        changeset: Option<ChangeSet>,
10✔
503
        network: Network,
10✔
504
    ) -> Result<Self, NewOrLoadError> {
10✔
505
        let genesis_hash = genesis_block(network).block_hash();
10✔
506
        Self::new_or_load_with_genesis_hash(
10✔
507
            descriptor,
10✔
508
            change_descriptor,
10✔
509
            changeset,
10✔
510
            network,
10✔
511
            genesis_hash,
10✔
512
        )
10✔
513
    }
10✔
514

515
    /// Either loads [`Wallet`] from a [`ChangeSet`] or initializes it if one does not exist, using the
516
    /// provided descriptor, change descriptor, network, and custom genesis hash.
517
    ///
518
    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
519
    /// This is like [`Wallet::new_or_load`] with an additional `genesis_hash` parameter. This is
520
    /// useful for syncing from alternative networks.
521
    pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
12✔
522
        descriptor: E,
12✔
523
        change_descriptor: E,
12✔
524
        changeset: Option<ChangeSet>,
12✔
525
        network: Network,
12✔
526
        genesis_hash: BlockHash,
12✔
527
    ) -> Result<Self, NewOrLoadError> {
12✔
528
        if let Some(changeset) = changeset {
12✔
529
            let mut wallet = Self::load_from_changeset(changeset).map_err(|e| match e {
10✔
NEW
530
                LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
NEW
531
                LoadError::MissingNetwork => NewOrLoadError::LoadedNetworkDoesNotMatch {
×
NEW
532
                    expected: network,
×
NEW
533
                    got: None,
×
NEW
534
                },
×
NEW
535
                LoadError::MissingGenesis => NewOrLoadError::LoadedGenesisDoesNotMatch {
×
NEW
536
                    expected: genesis_hash,
×
NEW
537
                    got: None,
×
NEW
538
                },
×
NEW
539
                LoadError::MissingDescriptor(keychain) => {
×
NEW
540
                    NewOrLoadError::LoadedDescriptorDoesNotMatch {
×
541
                        got: None,
×
NEW
542
                        keychain,
×
543
                    }
×
544
                }
545
            })?;
10✔
546
            if wallet.network != network {
10✔
547
                return Err(NewOrLoadError::LoadedNetworkDoesNotMatch {
2✔
548
                    expected: network,
2✔
549
                    got: Some(wallet.network),
2✔
550
                });
2✔
551
            }
8✔
552
            if wallet.chain.genesis_hash() != genesis_hash {
8✔
553
                return Err(NewOrLoadError::LoadedGenesisDoesNotMatch {
2✔
554
                    expected: genesis_hash,
2✔
555
                    got: Some(wallet.chain.genesis_hash()),
2✔
556
                });
2✔
557
            }
6✔
558

559
            let (expected_descriptor, expected_descriptor_keymap) = descriptor
6✔
560
                .into_wallet_descriptor(&wallet.secp, network)
6✔
561
                .map_err(NewOrLoadError::Descriptor)?;
6✔
562
            let wallet_descriptor = wallet.public_descriptor(KeychainKind::External);
6✔
563
            if wallet_descriptor != &expected_descriptor {
6✔
564
                return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
2✔
565
                    got: Some(wallet_descriptor.clone()),
2✔
566
                    keychain: KeychainKind::External,
2✔
567
                });
2✔
568
            }
4✔
569
            // if expected descriptor has private keys add them as new signers
4✔
570
            if !expected_descriptor_keymap.is_empty() {
4✔
571
                let signer_container = SignersContainer::build(
4✔
572
                    expected_descriptor_keymap,
4✔
573
                    &expected_descriptor,
4✔
574
                    &wallet.secp,
4✔
575
                );
4✔
576
                signer_container.signers().into_iter().for_each(|signer| {
4✔
577
                    wallet.add_signer(
4✔
578
                        KeychainKind::External,
4✔
579
                        SignerOrdering::default(),
4✔
580
                        signer.clone(),
4✔
581
                    )
4✔
582
                });
4✔
583
            }
4✔
584

585
            let (expected_change_descriptor, expected_change_descriptor_keymap) = change_descriptor
4✔
586
                .into_wallet_descriptor(&wallet.secp, network)
4✔
587
                .map_err(NewOrLoadError::Descriptor)?;
4✔
588
            let wallet_change_descriptor = wallet.public_descriptor(KeychainKind::Internal);
4✔
589
            if wallet_change_descriptor != &expected_change_descriptor {
4✔
590
                return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
2✔
591
                    got: Some(wallet_change_descriptor.clone()),
2✔
592
                    keychain: KeychainKind::Internal,
2✔
593
                });
2✔
594
            }
2✔
595
            // if expected change descriptor has private keys add them as new signers
2✔
596
            if !expected_change_descriptor_keymap.is_empty() {
2✔
597
                let signer_container = SignersContainer::build(
2✔
598
                    expected_change_descriptor_keymap,
2✔
599
                    &expected_change_descriptor,
2✔
600
                    &wallet.secp,
2✔
601
                );
2✔
602
                signer_container.signers().into_iter().for_each(|signer| {
2✔
603
                    wallet.add_signer(
2✔
604
                        KeychainKind::Internal,
2✔
605
                        SignerOrdering::default(),
2✔
606
                        signer.clone(),
2✔
607
                    )
2✔
608
                });
2✔
609
            }
2✔
610

611
            Ok(wallet)
2✔
612
        } else {
613
            Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
2✔
614
                .map_err(|e| match e {
2✔
NEW
615
                    NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
616
                })
2✔
617
        }
618
    }
12✔
619

620
    /// Get the Bitcoin network the wallet is using.
621
    pub fn network(&self) -> Network {
48✔
622
        self.network
48✔
623
    }
48✔
624

625
    /// Iterator over all keychains in this wallet
626
    pub fn keychains(&self) -> impl Iterator<Item = (&KeychainKind, &ExtendedDescriptor)> {
64✔
627
        self.indexed_graph.index.keychains()
64✔
628
    }
64✔
629

630
    /// Peek an address of the given `keychain` at `index` without revealing it.
631
    ///
632
    /// For non-wildcard descriptors this returns the same address at every provided index.
633
    ///
634
    /// # Panics
635
    ///
636
    /// This panics when the caller requests for an address of derivation index greater than the
637
    /// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
638
    pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
1,248✔
639
        let mut spk_iter = self
1,248✔
640
            .indexed_graph
1,248✔
641
            .index
1,248✔
642
            .unbounded_spk_iter(&keychain)
1,248✔
643
            .expect("keychain must exist");
1,248✔
644
        if !spk_iter.descriptor().has_wildcard() {
1,248✔
645
            index = 0;
952✔
646
        }
952✔
647
        let (index, spk) = spk_iter
1,248✔
648
            .nth(index as usize)
1,248✔
649
            .expect("derivation index is out of bounds");
1,248✔
650

1,248✔
651
        AddressInfo {
1,248✔
652
            index,
1,248✔
653
            address: Address::from_script(&spk, self.network).expect("must have address form"),
1,248✔
654
            keychain,
1,248✔
655
        }
1,248✔
656
    }
1,248✔
657

658
    /// Attempt to reveal the next address of the given `keychain`.
659
    ///
660
    /// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
661
    /// contain a wildcard or every address is already revealed up to the maximum derivation
662
    /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
663
    /// then the last revealed address will be returned.
664
    ///
665
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
666
    /// calls to this method before closing the wallet. For example:
667
    ///
668
    /// ```rust,no_run
669
    /// # use bdk_chain::persist::PersistBackend;
670
    /// # use bdk_wallet::wallet::{Wallet, ChangeSet};
671
    /// # use bdk_wallet::KeychainKind;
672
    /// # use bdk_sqlite::{Store, rusqlite::Connection};
673
    /// # let conn = Connection::open_in_memory().expect("must open connection");
674
    /// # let mut db = Store::new(conn).expect("must create store");
675
    /// # let changeset = ChangeSet::default();
676
    /// # let mut wallet = Wallet::load_from_changeset(changeset).expect("load wallet");
677
    /// let next_address = wallet.reveal_next_address(KeychainKind::External);
678
    /// wallet.commit_to(&mut db)?;
679
    ///
680
    /// // Now it's safe to show the user their next address!
681
    /// println!("Next address: {}", next_address.address);
682
    /// # Ok::<(), anyhow::Error>(())
683
    /// ```
684
    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo {
120✔
685
        let index = &mut self.indexed_graph.index;
120✔
686
        let stage = &mut self.stage;
120✔
687

120✔
688
        let ((index, spk), index_changeset) = index
120✔
689
            .reveal_next_spk(&keychain)
120✔
690
            .expect("keychain must exist");
120✔
691

120✔
692
        stage.append(indexed_tx_graph::ChangeSet::from(index_changeset).into());
120✔
693

120✔
694
        AddressInfo {
120✔
695
            index,
120✔
696
            address: Address::from_script(spk.as_script(), self.network)
120✔
697
                .expect("must have address form"),
120✔
698
            keychain,
120✔
699
        }
120✔
700
    }
120✔
701

702
    /// Reveal addresses up to and including the target `index` and return an iterator
703
    /// of newly revealed addresses.
704
    ///
705
    /// If the target `index` is unreachable, we make a best effort to reveal up to the last
706
    /// possible index. If all addresses up to the given `index` are already revealed, then
707
    /// no new addresses are returned.
708
    ///
709
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
710
    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
711
    pub fn reveal_addresses_to(
16✔
712
        &mut self,
16✔
713
        keychain: KeychainKind,
16✔
714
        index: u32,
16✔
715
    ) -> impl Iterator<Item = AddressInfo> + '_ {
16✔
716
        let (spks, index_changeset) = self
16✔
717
            .indexed_graph
16✔
718
            .index
16✔
719
            .reveal_to_target(&keychain, index)
16✔
720
            .expect("keychain must exist");
16✔
721

16✔
722
        self.stage.append(index_changeset.into());
16✔
723

16✔
724
        spks.into_iter().map(move |(index, spk)| AddressInfo {
24✔
725
            index,
10✔
726
            address: Address::from_script(&spk, self.network).expect("must have address form"),
10✔
727
            keychain,
10✔
728
        })
24✔
729
    }
16✔
730

731
    /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
732
    /// derivation index that hasn't been used.
733
    ///
734
    /// This will attempt to derive and reveal a new address if no newly revealed addresses
735
    /// are available. See also [`reveal_next_address`](Self::reveal_next_address).
736
    ///
737
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
738
    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
739
    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo {
896✔
740
        let index = &mut self.indexed_graph.index;
896✔
741

896✔
742
        let ((index, spk), index_changeset) = index
896✔
743
            .next_unused_spk(&keychain)
896✔
744
            .expect("keychain must exist");
896✔
745

896✔
746
        self.stage
896✔
747
            .append(indexed_tx_graph::ChangeSet::from(index_changeset).into());
896✔
748

896✔
749
        AddressInfo {
896✔
750
            index,
896✔
751
            address: Address::from_script(spk.as_script(), self.network)
896✔
752
                .expect("must have address form"),
896✔
753
            keychain,
896✔
754
        }
896✔
755
    }
896✔
756

757
    /// Marks an address used of the given `keychain` at `index`.
758
    ///
759
    /// Returns whether the given index was present and then removed from the unused set.
760
    pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
8✔
761
        self.indexed_graph.index.mark_used(keychain, index)
8✔
762
    }
8✔
763

764
    /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted
765
    /// back into the unused set.
766
    ///
767
    /// Since this is only a superficial marker, it will have no effect if the address at the given
768
    /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the
769
    /// derived spk.
770
    ///
771
    /// [`mark_used`]: Self::mark_used
772
    pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
16✔
773
        self.indexed_graph.index.unmark_used(keychain, index)
16✔
774
    }
16✔
775

776
    /// List addresses that are revealed but unused.
777
    ///
778
    /// Note if the returned iterator is empty you can reveal more addresses
779
    /// by using [`reveal_next_address`](Self::reveal_next_address) or
780
    /// [`reveal_addresses_to`](Self::reveal_addresses_to).
781
    pub fn list_unused_addresses(
24✔
782
        &self,
24✔
783
        keychain: KeychainKind,
24✔
784
    ) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
24✔
785
        self.indexed_graph
24✔
786
            .index
24✔
787
            .unused_keychain_spks(&keychain)
24✔
788
            .map(move |(index, spk)| AddressInfo {
32✔
789
                index,
11✔
790
                address: Address::from_script(spk, self.network).expect("must have address form"),
11✔
791
                keychain,
11✔
792
            })
32✔
793
    }
24✔
794

795
    /// Return whether or not a `script` is part of this wallet (either internal or external)
796
    pub fn is_mine(&self, script: &Script) -> bool {
1,768✔
797
        self.indexed_graph.index.index_of_spk(script).is_some()
1,768✔
798
    }
1,768✔
799

800
    /// Finds how the wallet derived the script pubkey `spk`.
801
    ///
802
    /// Will only return `Some(_)` if the wallet has given out the spk.
803
    pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
72✔
804
        self.indexed_graph.index.index_of_spk(spk).cloned()
72✔
805
    }
72✔
806

807
    /// Return the list of unspent outputs of this wallet
808
    pub fn list_unspent(&self) -> impl Iterator<Item = LocalOutput> + '_ {
1,200✔
809
        self.indexed_graph
1,200✔
810
            .graph()
1,200✔
811
            .filter_chain_unspents(
1,200✔
812
                &self.chain,
1,200✔
813
                self.chain.tip().block_id(),
1,200✔
814
                self.indexed_graph.index.outpoints().iter().cloned(),
1,200✔
815
            )
1,200✔
816
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
1,312✔
817
    }
1,200✔
818

819
    /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
820
    ///
821
    /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
822
    pub fn list_output(&self) -> impl Iterator<Item = LocalOutput> + '_ {
8✔
823
        self.indexed_graph
8✔
824
            .graph()
8✔
825
            .filter_chain_txouts(
8✔
826
                &self.chain,
8✔
827
                self.chain.tip().block_id(),
8✔
828
                self.indexed_graph.index.outpoints().iter().cloned(),
8✔
829
            )
8✔
830
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
9✔
831
    }
8✔
832

833
    /// Get all the checkpoints the wallet is currently storing indexed by height.
834
    pub fn checkpoints(&self) -> CheckPointIter {
×
835
        self.chain.iter_checkpoints()
×
836
    }
×
837

838
    /// Returns the latest checkpoint.
839
    pub fn latest_checkpoint(&self) -> CheckPoint {
72✔
840
        self.chain.tip()
72✔
841
    }
72✔
842

843
    /// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
844
    ///
845
    /// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
846
    /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
847
    /// electrum server) which will go through each address until it reaches a *stop gap*.
848
    ///
849
    /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
850
    /// script pubkeys the wallet is storing internally).
851
    pub fn all_unbounded_spk_iters(
×
852
        &self,
×
853
    ) -> BTreeMap<KeychainKind, impl Iterator<Item = Indexed<ScriptBuf>> + Clone> {
×
854
        self.indexed_graph.index.all_unbounded_spk_iters()
×
855
    }
×
856

857
    /// Get an unbounded script pubkey iterator for the given `keychain`.
858
    ///
859
    /// See [`all_unbounded_spk_iters`] for more documentation
860
    ///
861
    /// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
862
    pub fn unbounded_spk_iter(
×
863
        &self,
×
864
        keychain: KeychainKind,
×
865
    ) -> impl Iterator<Item = Indexed<ScriptBuf>> + Clone {
×
866
        self.indexed_graph
×
867
            .index
×
868
            .unbounded_spk_iter(&keychain)
×
869
            .expect("keychain must exist")
×
870
    }
×
871

872
    /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
873
    /// wallet's database.
874
    pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
72✔
875
        let ((keychain, index), _) = self.indexed_graph.index.txout(op)?;
72✔
876
        self.indexed_graph
72✔
877
            .graph()
72✔
878
            .filter_chain_unspents(
72✔
879
                &self.chain,
72✔
880
                self.chain.tip().block_id(),
72✔
881
                core::iter::once(((), op)),
72✔
882
            )
72✔
883
            .map(|(_, full_txo)| new_local_utxo(keychain, index, full_txo))
72✔
884
            .next()
72✔
885
    }
72✔
886

887
    /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
888
    ///
889
    /// This is used for providing a previous output's value so that we can use [`calculate_fee`]
890
    /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
891
    /// not be returned in [`list_unspent`] or [`list_output`].
892
    ///
893
    /// **WARNINGS:** This should only be used to add `TxOut`s that the wallet does not own. Only
894
    /// insert `TxOut`s that you trust the values for!
895
    ///
896
    /// You must persist the changes resulting from one or more calls to this method if you need
897
    /// the inserted `TxOut` data to be reloaded after closing the wallet.
898
    /// See [`Wallet::reveal_next_address`].
899
    ///
900
    /// [`calculate_fee`]: Self::calculate_fee
901
    /// [`calculate_fee_rate`]: Self::calculate_fee_rate
902
    /// [`list_unspent`]: Self::list_unspent
903
    /// [`list_output`]: Self::list_output
904
    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
16✔
905
        let additions = self.indexed_graph.insert_txout(outpoint, txout);
16✔
906
        self.stage.append(additions.into());
16✔
907
    }
16✔
908

909
    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
910
    ///
911
    /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
912
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
913
    ///
914
    /// Note `tx` does not have to be in the graph for this to work.
915
    ///
916
    /// # Examples
917
    ///
918
    /// ```rust, no_run
919
    /// # use bitcoin::Txid;
920
    /// # use bdk_wallet::Wallet;
921
    /// # let mut wallet: Wallet = todo!();
922
    /// # let txid:Txid = todo!();
923
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
924
    /// let fee = wallet.calculate_fee(&tx).expect("fee");
925
    /// ```
926
    ///
927
    /// ```rust, no_run
928
    /// # use bitcoin::Psbt;
929
    /// # use bdk_wallet::Wallet;
930
    /// # let mut wallet: Wallet = todo!();
931
    /// # let mut psbt: Psbt = todo!();
932
    /// let tx = &psbt.clone().extract_tx().expect("tx");
933
    /// let fee = wallet.calculate_fee(tx).expect("fee");
934
    /// ```
935
    /// [`insert_txout`]: Self::insert_txout
936
    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
536✔
937
        self.indexed_graph.graph().calculate_fee(tx)
536✔
938
    }
536✔
939

940
    /// Calculate the [`FeeRate`] for a given transaction.
941
    ///
942
    /// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you must
943
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
944
    ///
945
    /// Note `tx` does not have to be in the graph for this to work.
946
    ///
947
    /// # Examples
948
    ///
949
    /// ```rust, no_run
950
    /// # use bitcoin::Txid;
951
    /// # use bdk_wallet::Wallet;
952
    /// # let mut wallet: Wallet = todo!();
953
    /// # let txid:Txid = todo!();
954
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
955
    /// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
956
    /// ```
957
    ///
958
    /// ```rust, no_run
959
    /// # use bitcoin::Psbt;
960
    /// # use bdk_wallet::Wallet;
961
    /// # let mut wallet: Wallet = todo!();
962
    /// # let mut psbt: Psbt = todo!();
963
    /// let tx = &psbt.clone().extract_tx().expect("tx");
964
    /// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
965
    /// ```
966
    /// [`insert_txout`]: Self::insert_txout
967
    pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
144✔
968
        self.calculate_fee(tx).map(|fee| fee / tx.weight())
144✔
969
    }
144✔
970

971
    /// Compute the `tx`'s sent and received [`Amount`]s.
972
    ///
973
    /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
974
    /// that spend from previous txouts tracked by this wallet. Received is the summation
975
    /// of this tx's outputs that send to script pubkeys tracked by this wallet.
976
    ///
977
    /// # Examples
978
    ///
979
    /// ```rust, no_run
980
    /// # use bitcoin::Txid;
981
    /// # use bdk_wallet::Wallet;
982
    /// # let mut wallet: Wallet = todo!();
983
    /// # let txid:Txid = todo!();
984
    /// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
985
    /// let (sent, received) = wallet.sent_and_received(&tx);
986
    /// ```
987
    ///
988
    /// ```rust, no_run
989
    /// # use bitcoin::Psbt;
990
    /// # use bdk_wallet::Wallet;
991
    /// # let mut wallet: Wallet = todo!();
992
    /// # let mut psbt: Psbt = todo!();
993
    /// let tx = &psbt.clone().extract_tx().expect("tx");
994
    /// let (sent, received) = wallet.sent_and_received(tx);
995
    /// ```
996
    pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) {
224✔
997
        self.indexed_graph.index.sent_and_received(tx, ..)
224✔
998
    }
224✔
999

1000
    /// Get a single transaction from the wallet as a [`CanonicalTx`] (if the transaction exists).
1001
    ///
1002
    /// `CanonicalTx` contains the full transaction alongside meta-data such as:
1003
    /// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
1004
    ///   in the best chain.
1005
    /// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
1006
    ///   confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
1007
    ///   confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
1008
    ///   the transaction was last seen in the mempool is provided.
1009
    ///
1010
    /// ```rust, no_run
1011
    /// use bdk_chain::Anchor;
1012
    /// use bdk_wallet::{chain::ChainPosition, Wallet};
1013
    /// # let wallet: Wallet = todo!();
1014
    /// # let my_txid: bitcoin::Txid = todo!();
1015
    ///
1016
    /// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
1017
    ///
1018
    /// // get reference to full transaction
1019
    /// println!("my tx: {:#?}", canonical_tx.tx_node.tx);
1020
    ///
1021
    /// // list all transaction anchors
1022
    /// for anchor in canonical_tx.tx_node.anchors {
1023
    ///     println!(
1024
    ///         "tx is anchored by block of hash {}",
1025
    ///         anchor.anchor_block().hash
1026
    ///     );
1027
    /// }
1028
    ///
1029
    /// // get confirmation status of transaction
1030
    /// match canonical_tx.chain_position {
1031
    ///     ChainPosition::Confirmed(anchor) => println!(
1032
    ///         "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
1033
    ///         anchor.confirmation_height, anchor.anchor_block.height, anchor.anchor_block.hash,
1034
    ///     ),
1035
    ///     ChainPosition::Unconfirmed(last_seen) => println!(
1036
    ///         "tx is last seen at {}, it is unconfirmed as it is not anchored in the best chain",
1037
    ///         last_seen,
1038
    ///     ),
1039
    /// }
1040
    /// ```
1041
    ///
1042
    /// [`Anchor`]: bdk_chain::Anchor
1043
    pub fn get_tx(
56✔
1044
        &self,
56✔
1045
        txid: Txid,
56✔
1046
    ) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> {
56✔
1047
        let graph = self.indexed_graph.graph();
56✔
1048

56✔
1049
        Some(CanonicalTx {
56✔
1050
            chain_position: graph.get_chain_position(
56✔
1051
                &self.chain,
56✔
1052
                self.chain.tip().block_id(),
56✔
1053
                txid,
56✔
1054
            )?,
56✔
1055
            tx_node: graph.get_tx_node(txid)?,
56✔
1056
        })
1057
    }
56✔
1058

1059
    /// Add a new checkpoint to the wallet's internal view of the chain.
1060
    ///
1061
    /// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
1062
    /// there).
1063
    ///
1064
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
1065
    /// if you need the inserted checkpoint data to be reloaded after closing the wallet.
1066
    /// See [`Wallet::reveal_next_address`].
1067
    ///
1068
    /// [`commit`]: Self::commit
1069
    pub fn insert_checkpoint(
2,158✔
1070
        &mut self,
2,158✔
1071
        block_id: BlockId,
2,158✔
1072
    ) -> Result<bool, local_chain::AlterCheckPointError> {
2,158✔
1073
        let changeset = self.chain.insert_block(block_id)?;
2,158✔
1074
        let changed = !changeset.is_empty();
2,158✔
1075
        self.stage.append(changeset.into());
2,158✔
1076
        Ok(changed)
2,158✔
1077
    }
2,158✔
1078

1079
    /// Add a transaction to the wallet's internal view of the chain. This stages the change,
1080
    /// you must persist it later.
1081
    ///
1082
    /// Returns whether anything changed with the transaction insertion (e.g. `false` if the
1083
    /// transaction was already inserted at the same position).
1084
    ///
1085
    /// A `tx` can be rejected if `position` has a height greater than the [`latest_checkpoint`].
1086
    /// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually
1087
    /// inserting new transactions.
1088
    ///
1089
    /// **WARNING**: If `position` is confirmed, we anchor the `tx` to the lowest checkpoint that
1090
    /// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our
1091
    /// local view of the best chain's history.
1092
    ///
1093
    /// You must persist the changes resulting from one or more calls to this method if you need
1094
    /// the inserted tx to be reloaded after closing the wallet.
1095
    ///
1096
    /// [`commit`]: Self::commit
1097
    /// [`latest_checkpoint`]: Self::latest_checkpoint
1098
    /// [`insert_checkpoint`]: Self::insert_checkpoint
1099
    pub fn insert_tx(
2,414✔
1100
        &mut self,
2,414✔
1101
        tx: Transaction,
2,414✔
1102
        position: ConfirmationTime,
2,414✔
1103
    ) -> Result<bool, InsertTxError> {
2,414✔
1104
        let (anchor, last_seen) = match position {
2,414✔
1105
            ConfirmationTime::Confirmed { height, time } => {
2,230✔
1106
                // anchor tx to checkpoint with lowest height that is >= position's height
1107
                let anchor = self
2,230✔
1108
                    .chain
2,230✔
1109
                    .range(height..)
2,230✔
1110
                    .last()
2,230✔
1111
                    .ok_or(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
2,230✔
1112
                        tip_height: self.chain.tip().height(),
2,230✔
1113
                        tx_height: height,
2,230✔
1114
                    })
2,230✔
1115
                    .map(|anchor_cp| ConfirmationTimeHeightAnchor {
2,230✔
1116
                        anchor_block: anchor_cp.block_id(),
2,230✔
1117
                        confirmation_height: height,
2,230✔
1118
                        confirmation_time: time,
2,230✔
1119
                    })?;
2,230✔
1120

1121
                (Some(anchor), None)
2,230✔
1122
            }
1123
            ConfirmationTime::Unconfirmed { last_seen } => (None, Some(last_seen)),
184✔
1124
        };
1125

1126
        let mut changeset = ChangeSet::default();
2,414✔
1127
        let txid = tx.compute_txid();
2,414✔
1128
        changeset.append(self.indexed_graph.insert_tx(tx).into());
2,414✔
1129
        if let Some(anchor) = anchor {
2,414✔
1130
            changeset.append(self.indexed_graph.insert_anchor(txid, anchor).into());
2,230✔
1131
        }
2,230✔
1132
        if let Some(last_seen) = last_seen {
2,414✔
1133
            changeset.append(self.indexed_graph.insert_seen_at(txid, last_seen).into());
184✔
1134
        }
2,230✔
1135

1136
        let changed = !changeset.is_empty();
2,414✔
1137
        self.stage.append(changeset);
2,414✔
1138
        Ok(changed)
2,414✔
1139
    }
2,414✔
1140

1141
    /// Iterate over the transactions in the wallet.
1142
    pub fn transactions(
38✔
1143
        &self,
38✔
1144
    ) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> + '_
38✔
1145
    {
38✔
1146
        self.indexed_graph
38✔
1147
            .graph()
38✔
1148
            .list_chain_txs(&self.chain, self.chain.tip().block_id())
38✔
1149
    }
38✔
1150

1151
    /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
1152
    /// values.
1153
    pub fn balance(&self) -> Balance {
32✔
1154
        self.indexed_graph.graph().balance(
32✔
1155
            &self.chain,
32✔
1156
            self.chain.tip().block_id(),
32✔
1157
            self.indexed_graph.index.outpoints().iter().cloned(),
32✔
1158
            |&(k, _), _| k == KeychainKind::Internal,
32✔
1159
        )
32✔
1160
    }
32✔
1161

1162
    /// Add an external signer
1163
    ///
1164
    /// See [the `signer` module](signer) for an example.
1165
    pub fn add_signer(
64✔
1166
        &mut self,
64✔
1167
        keychain: KeychainKind,
64✔
1168
        ordering: SignerOrdering,
64✔
1169
        signer: Arc<dyn TransactionSigner>,
64✔
1170
    ) {
64✔
1171
        let signers = match keychain {
64✔
1172
            KeychainKind::External => Arc::make_mut(&mut self.signers),
48✔
1173
            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
16✔
1174
        };
1175

1176
        signers.add_external(signer.id(&self.secp), ordering, signer);
64✔
1177
    }
64✔
1178

1179
    /// Get the signers
1180
    ///
1181
    /// ## Example
1182
    ///
1183
    /// ```
1184
    /// # use bdk_wallet::{Wallet, KeychainKind};
1185
    /// # use bdk_wallet::bitcoin::Network;
1186
    /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
1187
    /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
1188
    /// let wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
1189
    /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
1190
    ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
1191
    ///     println!("secret_key: {}", secret_key);
1192
    /// }
1193
    ///
1194
    /// Ok::<(), Box<dyn std::error::Error>>(())
1195
    /// ```
1196
    pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
36✔
1197
        match keychain {
36✔
1198
            KeychainKind::External => Arc::clone(&self.signers),
22✔
1199
            KeychainKind::Internal => Arc::clone(&self.change_signers),
14✔
1200
        }
1201
    }
36✔
1202

1203
    /// Start building a transaction.
1204
    ///
1205
    /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
1206
    ///
1207
    /// ## Example
1208
    ///
1209
    /// ```
1210
    /// # use std::str::FromStr;
1211
    /// # use bitcoin::*;
1212
    /// # use bdk_wallet::*;
1213
    /// # use bdk_wallet::wallet::ChangeSet;
1214
    /// # use bdk_wallet::wallet::error::CreateTxError;
1215
    /// # use anyhow::Error;
1216
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1217
    /// # let mut wallet = doctest_wallet!();
1218
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1219
    /// let psbt = {
1220
    ///    let mut builder =  wallet.build_tx();
1221
    ///    builder
1222
    ///        .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1223
    ///    builder.finish()?
1224
    /// };
1225
    ///
1226
    /// // sign and broadcast ...
1227
    /// # Ok::<(), anyhow::Error>(())
1228
    /// ```
1229
    ///
1230
    /// [`TxBuilder`]: crate::TxBuilder
1231
    pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm> {
1,096✔
1232
        TxBuilder {
1,096✔
1233
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
1,096✔
1234
            params: TxParams::default(),
1,096✔
1235
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
1,096✔
1236
        }
1,096✔
1237
    }
1,096✔
1238

1239
    pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
146✔
1240
        &mut self,
146✔
1241
        coin_selection: Cs,
146✔
1242
        params: TxParams,
146✔
1243
    ) -> Result<Psbt, CreateTxError> {
146✔
1244
        let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
146✔
1245
        let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
146✔
1246
        let internal_descriptor = keychains.get(&KeychainKind::Internal).expect("must exist");
146✔
1247

1248
        let external_policy = external_descriptor
146✔
1249
            .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
146✔
1250
            .unwrap();
146✔
1251
        let internal_policy = internal_descriptor
146✔
1252
            .extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
146✔
1253
            .unwrap();
146✔
1254

146✔
1255
        // The policy allows spending external outputs, but it requires a policy path that hasn't been
146✔
1256
        // provided
146✔
1257
        if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
146✔
1258
            && external_policy.requires_path()
145✔
1259
            && params.external_policy_path.is_none()
4✔
1260
        {
1261
            return Err(CreateTxError::SpendingPolicyRequired(
1✔
1262
                KeychainKind::External,
1✔
1263
            ));
1✔
1264
        };
145✔
1265
        // Same for the internal_policy path
145✔
1266
        if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
145✔
1267
            && internal_policy.requires_path()
144✔
1268
            && params.internal_policy_path.is_none()
×
1269
        {
1270
            return Err(CreateTxError::SpendingPolicyRequired(
×
1271
                KeychainKind::Internal,
×
1272
            ));
×
1273
        };
145✔
1274

1275
        let external_requirements = external_policy.get_condition(
145✔
1276
            params
145✔
1277
                .external_policy_path
145✔
1278
                .as_ref()
145✔
1279
                .unwrap_or(&BTreeMap::new()),
145✔
1280
        )?;
145✔
1281
        let internal_requirements = internal_policy.get_condition(
145✔
1282
            params
145✔
1283
                .internal_policy_path
145✔
1284
                .as_ref()
145✔
1285
                .unwrap_or(&BTreeMap::new()),
145✔
1286
        )?;
145✔
1287

1288
        let requirements = external_requirements.merge(&internal_requirements)?;
145✔
1289

1290
        let version = match params.version {
143✔
1291
            Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
1✔
1292
            Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
18✔
1293
                return Err(CreateTxError::Version1Csv)
1✔
1294
            }
1295
            Some(tx_builder::Version(x)) => x,
18✔
1296
            None if requirements.csv.is_some() => 2,
125✔
1297
            None => 1,
121✔
1298
        };
1299

1300
        // We use a match here instead of a unwrap_or_else as it's way more readable :)
1301
        let current_height = match params.current_height {
143✔
1302
            // If they didn't tell us the current height, we assume it's the latest sync height.
1303
            None => {
1304
                let tip_height = self.chain.tip().height();
139✔
1305
                absolute::LockTime::from_height(tip_height).expect("invalid height")
139✔
1306
            }
1307
            Some(h) => h,
4✔
1308
        };
1309

1310
        let lock_time = match params.locktime {
142✔
1311
            // When no nLockTime is specified, we try to prevent fee sniping, if possible
1312
            None => {
1313
                // Fee sniping can be partially prevented by setting the timelock
1314
                // to current_height. If we don't know the current_height,
1315
                // we default to 0.
1316
                let fee_sniping_height = current_height;
140✔
1317

1318
                // We choose the biggest between the required nlocktime and the fee sniping
1319
                // height
1320
                match requirements.timelock {
4✔
1321
                    // No requirement, just use the fee_sniping_height
1322
                    None => fee_sniping_height,
136✔
1323
                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
1324
                    Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => {
4✔
1325
                        fee_sniping_height
×
1326
                    }
1327
                    // There's a time-based requirement or a block-based requirement greater
1328
                    // than the fee_sniping_height use that value
1329
                    Some(value) => value,
4✔
1330
                }
1331
            }
1332
            // Specific nLockTime required and we have no constraints, so just set to that value
1333
            Some(x) if requirements.timelock.is_none() => x,
3✔
1334
            // Specific nLockTime required and it's compatible with the constraints
1335
            Some(x)
1✔
1336
                if requirements.timelock.unwrap().is_same_unit(x)
2✔
1337
                    && x >= requirements.timelock.unwrap() =>
2✔
1338
            {
1✔
1339
                x
1✔
1340
            }
1341
            // Invalid nLockTime required
1342
            Some(x) => {
1✔
1343
                return Err(CreateTxError::LockTime {
1✔
1344
                    requested: x,
1✔
1345
                    required: requirements.timelock.unwrap(),
1✔
1346
                })
1✔
1347
            }
1348
        };
1349

1350
        // The nSequence to be by default for inputs unless an explicit sequence is specified.
1351
        let n_sequence = match (params.rbf, requirements.csv) {
142✔
1352
            // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
1353
            (None, None) if lock_time != absolute::LockTime::ZERO => {
117✔
1354
                Sequence::ENABLE_LOCKTIME_NO_RBF
116✔
1355
            }
1356
            // No RBF, CSV or nLockTime, make the transaction final
1357
            (None, None) => Sequence::MAX,
1✔
1358

1359
            // No RBF requested, use the value from CSV. Note that this value is by definition
1360
            // non-final, so even if a timelock is enabled this nSequence is fine, hence why we
1361
            // don't bother checking for it here. The same is true for all the other branches below
1362
            (None, Some(csv)) => csv,
2✔
1363

1364
            // RBF with a specific value but that value is too high
1365
            (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
3✔
1366
                return Err(CreateTxError::RbfSequence)
1✔
1367
            }
1368
            // RBF with a specific value requested, but the value is incompatible with CSV
1369
            (Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
1✔
1370
                if !check_nsequence_rbf(rbf, csv) =>
1✔
1371
            {
1✔
1372
                return Err(CreateTxError::RbfSequenceCsv { rbf, csv })
1✔
1373
            }
1374

1375
            // RBF enabled with the default value with CSV also enabled. CSV takes precedence
1376
            (Some(tx_builder::RbfValue::Default), Some(csv)) => csv,
1✔
1377
            // Valid RBF, either default or with a specific value. We ignore the `CSV` value
1378
            // because we've already checked it before
1379
            (Some(rbf), _) => rbf.get_value(),
20✔
1380
        };
1381

1382
        let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
140✔
1383
            //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
1384
            FeePolicy::FeeAmount(fee) => {
9✔
1385
                if let Some(previous_fee) = params.bumping_fee {
9✔
1386
                    if fee < previous_fee.absolute {
6✔
1387
                        return Err(CreateTxError::FeeTooLow {
2✔
1388
                            required: Amount::from_sat(previous_fee.absolute),
2✔
1389
                        });
2✔
1390
                    }
4✔
1391
                }
3✔
1392
                (FeeRate::ZERO, fee)
7✔
1393
            }
1394
            FeePolicy::FeeRate(rate) => {
131✔
1395
                if let Some(previous_fee) = params.bumping_fee {
131✔
1396
                    let required_feerate = FeeRate::from_sat_per_kwu(
11✔
1397
                        previous_fee.rate.to_sat_per_kwu()
11✔
1398
                            + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
11✔
1399
                    );
11✔
1400
                    if rate < required_feerate {
11✔
1401
                        return Err(CreateTxError::FeeRateTooLow {
1✔
1402
                            required: required_feerate,
1✔
1403
                        });
1✔
1404
                    }
10✔
1405
                }
120✔
1406
                (rate, 0)
130✔
1407
            }
1408
        };
1409

1410
        let mut tx = Transaction {
137✔
1411
            version: transaction::Version::non_standard(version),
137✔
1412
            lock_time,
137✔
1413
            input: vec![],
137✔
1414
            output: vec![],
137✔
1415
        };
137✔
1416

137✔
1417
        if params.manually_selected_only && params.utxos.is_empty() {
137✔
1418
            return Err(CreateTxError::NoUtxosSelected);
1✔
1419
        }
136✔
1420

136✔
1421
        let mut outgoing = Amount::ZERO;
136✔
1422
        let mut received = Amount::ZERO;
136✔
1423

136✔
1424
        let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
136✔
1425

1426
        for (index, (script_pubkey, value)) in recipients.enumerate() {
136✔
1427
            if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() {
85✔
1428
                return Err(CreateTxError::OutputBelowDustLimit(index));
1✔
1429
            }
84✔
1430

84✔
1431
            if self.is_mine(script_pubkey) {
84✔
1432
                received += Amount::from_sat(value);
42✔
1433
            }
46✔
1434

1435
            let new_out = TxOut {
84✔
1436
                script_pubkey: script_pubkey.clone(),
84✔
1437
                value: Amount::from_sat(value),
84✔
1438
            };
84✔
1439

84✔
1440
            tx.output.push(new_out);
84✔
1441

84✔
1442
            outgoing += Amount::from_sat(value);
84✔
1443
        }
1444

1445
        fee_amount += (fee_rate * tx.weight()).to_sat();
135✔
1446

135✔
1447
        let (required_utxos, optional_utxos) =
135✔
1448
            self.preselect_utxos(&params, Some(current_height.to_consensus_u32()));
135✔
1449

1450
        // get drain script
1451
        let drain_script = match params.drain_to {
135✔
1452
            Some(ref drain_recipient) => drain_recipient.clone(),
53✔
1453
            None => {
1454
                let change_keychain = KeychainKind::Internal;
82✔
1455
                let ((index, spk), index_changeset) = self
82✔
1456
                    .indexed_graph
82✔
1457
                    .index
82✔
1458
                    .next_unused_spk(&change_keychain)
82✔
1459
                    .expect("keychain must exist");
82✔
1460
                self.indexed_graph.index.mark_used(change_keychain, index);
82✔
1461
                self.stage.append(index_changeset.into());
82✔
1462
                spk
82✔
1463
            }
1464
        };
1465

1466
        let (required_utxos, optional_utxos) =
135✔
1467
            coin_selection::filter_duplicates(required_utxos, optional_utxos);
135✔
1468

1469
        let coin_selection = coin_selection.coin_select(
135✔
1470
            required_utxos,
135✔
1471
            optional_utxos,
135✔
1472
            fee_rate,
135✔
1473
            outgoing.to_sat() + fee_amount,
135✔
1474
            &drain_script,
135✔
1475
        )?;
135✔
1476
        fee_amount += coin_selection.fee_amount;
128✔
1477
        let excess = &coin_selection.excess;
128✔
1478

128✔
1479
        tx.input = coin_selection
128✔
1480
            .selected
128✔
1481
            .iter()
128✔
1482
            .map(|u| bitcoin::TxIn {
144✔
1483
                previous_output: u.outpoint(),
144✔
1484
                script_sig: ScriptBuf::default(),
144✔
1485
                sequence: u.sequence().unwrap_or(n_sequence),
144✔
1486
                witness: Witness::new(),
144✔
1487
            })
144✔
1488
            .collect();
128✔
1489

128✔
1490
        if tx.output.is_empty() {
128✔
1491
            // Uh oh, our transaction has no outputs.
1492
            // We allow this when:
1493
            // - We have a drain_to address and the utxos we must spend (this happens,
1494
            // for example, when we RBF)
1495
            // - We have a drain_to address and drain_wallet set
1496
            // Otherwise, we don't know who we should send the funds to, and how much
1497
            // we should send!
1498
            if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
51✔
1499
                if let NoChange {
1500
                    dust_threshold,
1✔
1501
                    remaining_amount,
1✔
1502
                    change_fee,
1✔
1503
                } = excess
49✔
1504
                {
1505
                    return Err(CreateTxError::CoinSelection(Error::InsufficientFunds {
1✔
1506
                        needed: *dust_threshold,
1✔
1507
                        available: remaining_amount.saturating_sub(*change_fee),
1✔
1508
                    }));
1✔
1509
                }
48✔
1510
            } else {
1511
                return Err(CreateTxError::NoRecipients);
2✔
1512
            }
1513
        }
77✔
1514

1515
        match excess {
125✔
1516
            NoChange {
1517
                remaining_amount, ..
3✔
1518
            } => fee_amount += remaining_amount,
3✔
1519
            Change { amount, fee } => {
122✔
1520
                if self.is_mine(&drain_script) {
122✔
1521
                    received += Amount::from_sat(*amount);
112✔
1522
                }
112✔
1523
                fee_amount += fee;
122✔
1524

122✔
1525
                // create drain output
122✔
1526
                let drain_output = TxOut {
122✔
1527
                    value: Amount::from_sat(*amount),
122✔
1528
                    script_pubkey: drain_script,
122✔
1529
                };
122✔
1530

122✔
1531
                // TODO: We should pay attention when adding a new output: this might increase
122✔
1532
                // the length of the "number of vouts" parameter by 2 bytes, potentially making
122✔
1533
                // our feerate too low
122✔
1534
                tx.output.push(drain_output);
122✔
1535
            }
1536
        };
1537

1538
        // sort input/outputs according to the chosen algorithm
1539
        params.ordering.sort_tx(&mut tx);
125✔
1540

1541
        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
125✔
1542
        Ok(psbt)
123✔
1543
    }
146✔
1544

1545
    /// Bump the fee of a transaction previously created with this wallet.
1546
    ///
1547
    /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
1548
    /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
1549
    /// pre-populated with the inputs and outputs of the original transaction.
1550
    ///
1551
    /// ## Example
1552
    ///
1553
    /// ```no_run
1554
    /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
1555
    /// # use std::str::FromStr;
1556
    /// # use bitcoin::*;
1557
    /// # use bdk_wallet::*;
1558
    /// # use bdk_wallet::wallet::ChangeSet;
1559
    /// # use bdk_wallet::wallet::error::CreateTxError;
1560
    /// # use anyhow::Error;
1561
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1562
    /// # let mut wallet = doctest_wallet!();
1563
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1564
    /// let mut psbt = {
1565
    ///     let mut builder = wallet.build_tx();
1566
    ///     builder
1567
    ///         .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
1568
    ///         .enable_rbf();
1569
    ///     builder.finish()?
1570
    /// };
1571
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1572
    /// let tx = psbt.clone().extract_tx().expect("tx");
1573
    /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
1574
    /// let mut psbt =  {
1575
    ///     let mut builder = wallet.build_fee_bump(tx.compute_txid())?;
1576
    ///     builder
1577
    ///         .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
1578
    ///     builder.finish()?
1579
    /// };
1580
    ///
1581
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1582
    /// let fee_bumped_tx = psbt.extract_tx();
1583
    /// // broadcast fee_bumped_tx to replace original
1584
    /// # Ok::<(), anyhow::Error>(())
1585
    /// ```
1586
    // TODO: support for merging multiple transactions while bumping the fees
1587
    pub fn build_fee_bump(
152✔
1588
        &mut self,
152✔
1589
        txid: Txid,
152✔
1590
    ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
152✔
1591
        let graph = self.indexed_graph.graph();
152✔
1592
        let txout_index = &self.indexed_graph.index;
152✔
1593
        let chain_tip = self.chain.tip().block_id();
152✔
1594

1595
        let mut tx = graph
152✔
1596
            .get_tx(txid)
152✔
1597
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
152✔
1598
            .as_ref()
152✔
1599
            .clone();
152✔
1600

1601
        let pos = graph
152✔
1602
            .get_chain_position(&self.chain, chain_tip, txid)
152✔
1603
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?;
152✔
1604
        if let ChainPosition::Confirmed(_) = pos {
152✔
1605
            return Err(BuildFeeBumpError::TransactionConfirmed(txid));
8✔
1606
        }
144✔
1607

144✔
1608
        if !tx
144✔
1609
            .input
144✔
1610
            .iter()
144✔
1611
            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
144✔
1612
        {
1613
            return Err(BuildFeeBumpError::IrreplaceableTransaction(
8✔
1614
                tx.compute_txid(),
8✔
1615
            ));
8✔
1616
        }
136✔
1617

1618
        let fee = self
136✔
1619
            .calculate_fee(&tx)
136✔
1620
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1621
        let fee_rate = self
136✔
1622
            .calculate_fee_rate(&tx)
136✔
1623
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1624

1625
        // remove the inputs from the tx and process them
1626
        let original_txin = tx.input.drain(..).collect::<Vec<_>>();
136✔
1627
        let original_utxos = original_txin
136✔
1628
            .iter()
136✔
1629
            .map(|txin| -> Result<_, BuildFeeBumpError> {
144✔
1630
                let prev_tx = graph
144✔
1631
                    .get_tx(txin.previous_output.txid)
144✔
1632
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
144✔
1633
                let txout = &prev_tx.output[txin.previous_output.vout as usize];
144✔
1634

1635
                let confirmation_time: ConfirmationTime = graph
144✔
1636
                    .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
144✔
1637
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?
144✔
1638
                    .cloned()
144✔
1639
                    .into();
144✔
1640

1641
                let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
144✔
1642
                    Some(&(keychain, derivation_index)) => {
144✔
1643
                        let satisfaction_weight = self
144✔
1644
                            .get_descriptor_for_keychain(keychain)
144✔
1645
                            .max_weight_to_satisfy()
144✔
1646
                            .unwrap()
144✔
1647
                            .to_wu() as usize;
144✔
1648
                        WeightedUtxo {
144✔
1649
                            utxo: Utxo::Local(LocalOutput {
144✔
1650
                                outpoint: txin.previous_output,
144✔
1651
                                txout: txout.clone(),
144✔
1652
                                keychain,
144✔
1653
                                is_spent: true,
144✔
1654
                                derivation_index,
144✔
1655
                                confirmation_time,
144✔
1656
                            }),
144✔
1657
                            satisfaction_weight,
144✔
1658
                        }
144✔
1659
                    }
1660
                    None => {
1661
                        let satisfaction_weight =
×
1662
                            serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len();
×
1663
                        WeightedUtxo {
×
1664
                            utxo: Utxo::Foreign {
×
1665
                                outpoint: txin.previous_output,
×
1666
                                sequence: Some(txin.sequence),
×
1667
                                psbt_input: Box::new(psbt::Input {
×
1668
                                    witness_utxo: Some(txout.clone()),
×
1669
                                    non_witness_utxo: Some(prev_tx.as_ref().clone()),
×
1670
                                    ..Default::default()
×
1671
                                }),
×
1672
                            },
×
1673
                            satisfaction_weight,
×
1674
                        }
×
1675
                    }
1676
                };
1677

1678
                Ok(weighted_utxo)
144✔
1679
            })
144✔
1680
            .collect::<Result<Vec<_>, _>>()?;
136✔
1681

1682
        if tx.output.len() > 1 {
136✔
1683
            let mut change_index = None;
80✔
1684
            for (index, txout) in tx.output.iter().enumerate() {
160✔
1685
                let change_keychain = KeychainKind::Internal;
160✔
1686
                match txout_index.index_of_spk(&txout.script_pubkey) {
160✔
1687
                    Some((keychain, _)) if *keychain == change_keychain => {
104✔
1688
                        change_index = Some(index)
80✔
1689
                    }
1690
                    _ => {}
80✔
1691
                }
1692
            }
1693

1694
            if let Some(change_index) = change_index {
80✔
1695
                tx.output.remove(change_index);
80✔
1696
            }
80✔
1697
        }
56✔
1698

1699
        let params = TxParams {
136✔
1700
            // TODO: figure out what rbf option should be?
136✔
1701
            version: Some(tx_builder::Version(tx.version.0)),
136✔
1702
            recipients: tx
136✔
1703
                .output
136✔
1704
                .into_iter()
136✔
1705
                .map(|txout| (txout.script_pubkey, txout.value.to_sat()))
136✔
1706
                .collect(),
136✔
1707
            utxos: original_utxos,
136✔
1708
            bumping_fee: Some(tx_builder::PreviousFee {
136✔
1709
                absolute: fee.to_sat(),
136✔
1710
                rate: fee_rate,
136✔
1711
            }),
136✔
1712
            ..Default::default()
136✔
1713
        };
136✔
1714

136✔
1715
        Ok(TxBuilder {
136✔
1716
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
136✔
1717
            params,
136✔
1718
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
136✔
1719
        })
136✔
1720
    }
152✔
1721

1722
    /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
1723
    /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
1724
    ///
1725
    /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
1726
    /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
1727
    /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
1728
    /// in this library will.
1729
    ///
1730
    /// ## Example
1731
    ///
1732
    /// ```
1733
    /// # use std::str::FromStr;
1734
    /// # use bitcoin::*;
1735
    /// # use bdk_wallet::*;
1736
    /// # use bdk_wallet::wallet::ChangeSet;
1737
    /// # use bdk_wallet::wallet::error::CreateTxError;
1738
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1739
    /// # let mut wallet = doctest_wallet!();
1740
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1741
    /// let mut psbt = {
1742
    ///     let mut builder = wallet.build_tx();
1743
    ///     builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1744
    ///     builder.finish()?
1745
    /// };
1746
    /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
1747
    /// assert!(finalized, "we should have signed all the inputs");
1748
    /// # Ok::<(),anyhow::Error>(())
1749
    pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, SignerError> {
344✔
1750
        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
344✔
1751
        // to derive our keys
344✔
1752
        self.update_psbt_with_descriptor(psbt)
344✔
1753
            .map_err(SignerError::MiniscriptPsbt)?;
344✔
1754

1755
        // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
1756
        // has the `non_witness_utxo`
1757
        if !sign_options.trust_witness_utxo
344✔
1758
            && psbt
296✔
1759
                .inputs
296✔
1760
                .iter()
296✔
1761
                .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
304✔
1762
                .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
296✔
1763
                .any(|i| i.non_witness_utxo.is_none())
296✔
1764
        {
1765
            return Err(SignerError::MissingNonWitnessUtxo);
×
1766
        }
344✔
1767

344✔
1768
        // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
344✔
1769
        // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
344✔
1770
        if !sign_options.allow_all_sighashes
344✔
1771
            && !psbt.inputs.iter().all(|i| {
352✔
1772
                i.sighash_type.is_none()
352✔
1773
                    || i.sighash_type == Some(EcdsaSighashType::All.into())
24✔
1774
                    || i.sighash_type == Some(TapSighashType::All.into())
16✔
1775
                    || i.sighash_type == Some(TapSighashType::Default.into())
16✔
1776
            })
352✔
1777
        {
1778
            return Err(SignerError::NonStandardSighash);
16✔
1779
        }
328✔
1780

1781
        for signer in self
680✔
1782
            .signers
328✔
1783
            .signers()
328✔
1784
            .iter()
328✔
1785
            .chain(self.change_signers.signers().iter())
328✔
1786
        {
1787
            signer.sign_transaction(psbt, &sign_options, &self.secp)?;
680✔
1788
        }
1789

1790
        // attempt to finalize
1791
        if sign_options.try_finalize {
296✔
1792
            self.finalize_psbt(psbt, sign_options)
272✔
1793
        } else {
1794
            Ok(false)
24✔
1795
        }
1796
    }
344✔
1797

1798
    /// Return the spending policies for the wallet's descriptor
1799
    pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, DescriptorError> {
24✔
1800
        let signers = match keychain {
24✔
1801
            KeychainKind::External => &self.signers,
24✔
1802
            KeychainKind::Internal => &self.change_signers,
×
1803
        };
1804

1805
        self.public_descriptor(keychain).extract_policy(
24✔
1806
            signers,
24✔
1807
            BuildSatisfaction::None,
24✔
1808
            &self.secp,
24✔
1809
        )
24✔
1810
    }
24✔
1811

1812
    /// Return the "public" version of the wallet's descriptor, meaning a new descriptor that has
1813
    /// the same structure but with every secret key removed
1814
    ///
1815
    /// This can be used to build a watch-only version of a wallet
1816
    pub fn public_descriptor(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
6,268✔
1817
        self.indexed_graph
6,268✔
1818
            .index
6,268✔
1819
            .keychains()
6,268✔
1820
            .find(|(k, _)| *k == &keychain)
7,098✔
1821
            .map(|(_, d)| d)
6,268✔
1822
            .expect("keychain must exist")
6,268✔
1823
    }
6,268✔
1824

1825
    /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
1826
    /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
1827
    /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer)
1828
    /// for further information.
1829
    ///
1830
    /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
1831
    ///
1832
    /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
1833
    pub fn finalize_psbt(
272✔
1834
        &self,
272✔
1835
        psbt: &mut Psbt,
272✔
1836
        sign_options: SignOptions,
272✔
1837
    ) -> Result<bool, SignerError> {
272✔
1838
        let chain_tip = self.chain.tip().block_id();
272✔
1839

272✔
1840
        let tx = &psbt.unsigned_tx;
272✔
1841
        let mut finished = true;
272✔
1842

1843
        for (n, input) in tx.input.iter().enumerate() {
312✔
1844
            let psbt_input = &psbt
312✔
1845
                .inputs
312✔
1846
                .get(n)
312✔
1847
                .ok_or(SignerError::InputIndexOutOfRange)?;
312✔
1848
            if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
304✔
1849
                continue;
16✔
1850
            }
288✔
1851
            let confirmation_height = self
288✔
1852
                .indexed_graph
288✔
1853
                .graph()
288✔
1854
                .get_chain_position(&self.chain, chain_tip, input.previous_output.txid)
288✔
1855
                .map(|chain_position| match chain_position {
288✔
1856
                    ChainPosition::Confirmed(a) => a.confirmation_height,
264✔
1857
                    ChainPosition::Unconfirmed(_) => u32::MAX,
×
1858
                });
288✔
1859
            let current_height = sign_options
288✔
1860
                .assume_height
288✔
1861
                .unwrap_or_else(|| self.chain.tip().height());
288✔
1862

288✔
1863
            // - Try to derive the descriptor by looking at the txout. If it's in our database, we
288✔
1864
            //   know exactly which `keychain` to use, and which derivation index it is
288✔
1865
            // - If that fails, try to derive it by looking at the psbt input: the complete logic
288✔
1866
            //   is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
288✔
1867
            //   `redeem_script` and `witness_script` to determine the right derivation
288✔
1868
            // - If that also fails, it will try it on the internal descriptor, if present
288✔
1869
            let desc = psbt
288✔
1870
                .get_utxo_for(n)
288✔
1871
                .and_then(|txout| self.get_descriptor_for_txout(&txout))
288✔
1872
                .or_else(|| {
288✔
1873
                    self.indexed_graph.index.keychains().find_map(|(_, desc)| {
32✔
1874
                        desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
32✔
1875
                    })
32✔
1876
                });
288✔
1877

288✔
1878
            match desc {
288✔
1879
                Some(desc) => {
272✔
1880
                    let mut tmp_input = bitcoin::TxIn::default();
272✔
1881
                    match desc.satisfy(
272✔
1882
                        &mut tmp_input,
272✔
1883
                        (
272✔
1884
                            PsbtInputSatisfier::new(psbt, n),
272✔
1885
                            After::new(Some(current_height), false),
272✔
1886
                            Older::new(Some(current_height), confirmation_height, false),
272✔
1887
                        ),
272✔
1888
                    ) {
272✔
1889
                        Ok(_) => {
1890
                            let psbt_input = &mut psbt.inputs[n];
264✔
1891
                            psbt_input.final_script_sig = Some(tmp_input.script_sig);
264✔
1892
                            psbt_input.final_script_witness = Some(tmp_input.witness);
264✔
1893
                            if sign_options.remove_partial_sigs {
264✔
1894
                                psbt_input.partial_sigs.clear();
240✔
1895
                            }
240✔
1896
                            if sign_options.remove_taproot_extras {
264✔
1897
                                // We just constructed the final witness, clear these fields.
264✔
1898
                                psbt_input.tap_key_sig = None;
264✔
1899
                                psbt_input.tap_script_sigs.clear();
264✔
1900
                                psbt_input.tap_scripts.clear();
264✔
1901
                                psbt_input.tap_key_origins.clear();
264✔
1902
                                psbt_input.tap_internal_key = None;
264✔
1903
                                psbt_input.tap_merkle_root = None;
264✔
1904
                            }
264✔
1905
                        }
1906
                        Err(_) => finished = false,
8✔
1907
                    }
1908
                }
1909
                None => finished = false,
16✔
1910
            }
1911
        }
1912

1913
        if finished && sign_options.remove_taproot_extras {
264✔
1914
            for output in &mut psbt.outputs {
568✔
1915
                output.tap_key_origins.clear();
328✔
1916
            }
328✔
1917
        }
24✔
1918

1919
        Ok(finished)
264✔
1920
    }
272✔
1921

1922
    /// Return the secp256k1 context used for all signing operations
1923
    pub fn secp_ctx(&self) -> &SecpCtx {
28✔
1924
        &self.secp
28✔
1925
    }
28✔
1926

1927
    /// Returns the descriptor used to create addresses for a particular `keychain`.
1928
    pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
6,100✔
1929
        self.public_descriptor(keychain)
6,100✔
1930
    }
6,100✔
1931

1932
    /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
1933
    /// Otherwise, it will return the index of the highest address it has derived.
1934
    pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
40✔
1935
        self.indexed_graph.index.last_revealed_index(&keychain)
40✔
1936
    }
40✔
1937

1938
    /// The index of the next address that you would get if you were to ask the wallet for a new address
1939
    pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
×
1940
        self.indexed_graph
×
1941
            .index
×
1942
            .next_index(&keychain)
×
1943
            .expect("keychain must exist")
×
1944
            .0
×
1945
    }
×
1946

1947
    /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
1948
    ///
1949
    /// This frees up the change address used when creating the tx for use in future transactions.
1950
    // TODO: Make this free up reserved utxos when that's implemented
1951
    pub fn cancel_tx(&mut self, tx: &Transaction) {
16✔
1952
        let txout_index = &mut self.indexed_graph.index;
16✔
1953
        for txout in &tx.output {
48✔
1954
            if let Some((keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
32✔
1955
                // NOTE: unmark_used will **not** make something unused if it has actually been used
16✔
1956
                // by a tx in the tracker. It only removes the superficial marking.
16✔
1957
                txout_index.unmark_used(*keychain, *index);
16✔
1958
            }
16✔
1959
        }
1960
    }
16✔
1961

1962
    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
288✔
1963
        let &(keychain, child) = self
288✔
1964
            .indexed_graph
288✔
1965
            .index
288✔
1966
            .index_of_spk(&txout.script_pubkey)?;
288✔
1967
        let descriptor = self.get_descriptor_for_keychain(keychain);
272✔
1968
        descriptor.at_derivation_index(child).ok()
272✔
1969
    }
288✔
1970

1971
    fn get_available_utxos(&self) -> Vec<(LocalOutput, usize)> {
1,136✔
1972
        self.list_unspent()
1,136✔
1973
            .map(|utxo| {
1,248✔
1974
                let keychain = utxo.keychain;
1,248✔
1975
                (utxo, {
1,248✔
1976
                    self.get_descriptor_for_keychain(keychain)
1,248✔
1977
                        .max_weight_to_satisfy()
1,248✔
1978
                        .unwrap()
1,248✔
1979
                        .to_wu() as usize
1,248✔
1980
                })
1,248✔
1981
            })
1,248✔
1982
            .collect()
1,136✔
1983
    }
1,136✔
1984

1985
    /// Given the options returns the list of utxos that must be used to form the
1986
    /// transaction and any further that may be used if needed.
1987
    fn preselect_utxos(
1,136✔
1988
        &self,
1,136✔
1989
        params: &TxParams,
1,136✔
1990
        current_height: Option<u32>,
1,136✔
1991
    ) -> (Vec<WeightedUtxo>, Vec<WeightedUtxo>) {
1,136✔
1992
        let TxParams {
1,136✔
1993
            change_policy,
1,136✔
1994
            unspendable,
1,136✔
1995
            utxos,
1,136✔
1996
            drain_wallet,
1,136✔
1997
            manually_selected_only,
1,136✔
1998
            bumping_fee,
1,136✔
1999
            ..
1,136✔
2000
        } = params;
1,136✔
2001

1,136✔
2002
        let manually_selected = utxos.clone();
1,136✔
2003
        // we mandate confirmed transactions if we're bumping the fee
1,136✔
2004
        let must_only_use_confirmed_tx = bumping_fee.is_some();
1,136✔
2005
        let must_use_all_available = *drain_wallet;
1,136✔
2006

1,136✔
2007
        let chain_tip = self.chain.tip().block_id();
1,136✔
2008
        //    must_spend <- manually selected utxos
1,136✔
2009
        //    may_spend  <- all other available utxos
1,136✔
2010
        let mut may_spend = self.get_available_utxos();
1,136✔
2011

1,136✔
2012
        may_spend.retain(|may_spend| {
1,248✔
2013
            !manually_selected
1,248✔
2014
                .iter()
1,248✔
2015
                .any(|manually_selected| manually_selected.utxo.outpoint() == may_spend.0.outpoint)
1,248✔
2016
        });
1,248✔
2017
        let mut must_spend = manually_selected;
1,136✔
2018

1,136✔
2019
        // NOTE: we are intentionally ignoring `unspendable` here. i.e manual
1,136✔
2020
        // selection overrides unspendable.
1,136✔
2021
        if *manually_selected_only {
1,136✔
2022
            return (must_spend, vec![]);
40✔
2023
        }
1,096✔
2024

1,096✔
2025
        let satisfies_confirmed = may_spend
1,096✔
2026
            .iter()
1,096✔
2027
            .map(|u| -> bool {
1,136✔
2028
                let txid = u.0.outpoint.txid;
1,136✔
2029
                let tx = match self.indexed_graph.graph().get_tx(txid) {
1,136✔
2030
                    Some(tx) => tx,
1,136✔
2031
                    None => return false,
×
2032
                };
2033
                let confirmation_time: ConfirmationTime = match self
1,136✔
2034
                    .indexed_graph
1,136✔
2035
                    .graph()
1,136✔
2036
                    .get_chain_position(&self.chain, chain_tip, txid)
1,136✔
2037
                {
2038
                    Some(chain_position) => chain_position.cloned().into(),
1,136✔
2039
                    None => return false,
×
2040
                };
2041

2042
                // Whether the UTXO is mature and, if needed, confirmed
2043
                let mut spendable = true;
1,136✔
2044
                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
1,136✔
2045
                    return false;
64✔
2046
                }
1,072✔
2047
                if tx.is_coinbase() {
1,072✔
2048
                    debug_assert!(
24✔
2049
                        confirmation_time.is_confirmed(),
24✔
2050
                        "coinbase must always be confirmed"
×
2051
                    );
2052
                    if let Some(current_height) = current_height {
24✔
2053
                        match confirmation_time {
24✔
2054
                            ConfirmationTime::Confirmed { height, .. } => {
24✔
2055
                                // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
24✔
2056
                                spendable &=
24✔
2057
                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
24✔
2058
                            }
24✔
2059
                            ConfirmationTime::Unconfirmed { .. } => spendable = false,
×
2060
                        }
2061
                    }
×
2062
                }
1,048✔
2063
                spendable
1,072✔
2064
            })
1,136✔
2065
            .collect::<Vec<_>>();
1,096✔
2066

1,096✔
2067
        let mut i = 0;
1,096✔
2068
        may_spend.retain(|u| {
1,136✔
2069
            let retain = change_policy.is_satisfied_by(&u.0)
1,136✔
2070
                && !unspendable.contains(&u.0.outpoint)
1,128✔
2071
                && satisfies_confirmed[i];
1,128✔
2072
            i += 1;
1,136✔
2073
            retain
1,136✔
2074
        });
1,136✔
2075

1,096✔
2076
        let mut may_spend = may_spend
1,096✔
2077
            .into_iter()
1,096✔
2078
            .map(|(local_utxo, satisfaction_weight)| WeightedUtxo {
1,096✔
2079
                satisfaction_weight,
1,048✔
2080
                utxo: Utxo::Local(local_utxo),
1,048✔
2081
            })
1,096✔
2082
            .collect();
1,096✔
2083

1,096✔
2084
        if must_use_all_available {
1,096✔
2085
            must_spend.append(&mut may_spend);
400✔
2086
        }
696✔
2087

2088
        (must_spend, may_spend)
1,096✔
2089
    }
1,136✔
2090

2091
    fn complete_transaction(
1,056✔
2092
        &self,
1,056✔
2093
        tx: Transaction,
1,056✔
2094
        selected: Vec<Utxo>,
1,056✔
2095
        params: TxParams,
1,056✔
2096
    ) -> Result<Psbt, CreateTxError> {
1,056✔
2097
        let mut psbt = Psbt::from_unsigned_tx(tx)?;
1,056✔
2098

2099
        if params.add_global_xpubs {
1,056✔
2100
            let all_xpubs = self
24✔
2101
                .keychains()
24✔
2102
                .flat_map(|(_, desc)| desc.get_extended_keys())
48✔
2103
                .collect::<Vec<_>>();
24✔
2104

2105
            for xpub in all_xpubs {
56✔
2106
                let origin = match xpub.origin {
32✔
2107
                    Some(origin) => origin,
24✔
2108
                    None if xpub.xkey.depth == 0 => {
16✔
2109
                        (xpub.root_fingerprint(&self.secp), vec![].into())
8✔
2110
                    }
2111
                    _ => return Err(CreateTxError::MissingKeyOrigin(xpub.xkey.to_string())),
8✔
2112
                };
2113

2114
                psbt.xpub.insert(xpub.xkey, origin);
32✔
2115
            }
2116
        }
1,032✔
2117

2118
        let mut lookup_output = selected
1,048✔
2119
            .into_iter()
1,048✔
2120
            .map(|utxo| (utxo.outpoint(), utxo))
1,176✔
2121
            .collect::<HashMap<_, _>>();
1,048✔
2122

2123
        // add metadata for the inputs
2124
        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
1,176✔
2125
            let utxo = match lookup_output.remove(&input.previous_output) {
1,176✔
2126
                Some(utxo) => utxo,
1,176✔
2127
                None => continue,
×
2128
            };
2129

2130
            match utxo {
1,176✔
2131
                Utxo::Local(utxo) => {
1,128✔
2132
                    *psbt_input =
1,128✔
2133
                        match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
1,128✔
2134
                            Ok(psbt_input) => psbt_input,
1,128✔
2135
                            Err(e) => match e {
×
2136
                                CreateTxError::UnknownUtxo => psbt::Input {
×
2137
                                    sighash_type: params.sighash,
×
2138
                                    ..psbt::Input::default()
×
2139
                                },
×
2140
                                _ => return Err(e),
×
2141
                            },
2142
                        }
2143
                }
2144
                Utxo::Foreign {
2145
                    outpoint,
48✔
2146
                    psbt_input: foreign_psbt_input,
48✔
2147
                    ..
48✔
2148
                } => {
48✔
2149
                    let is_taproot = foreign_psbt_input
48✔
2150
                        .witness_utxo
48✔
2151
                        .as_ref()
48✔
2152
                        .map(|txout| txout.script_pubkey.is_p2tr())
48✔
2153
                        .unwrap_or(false);
48✔
2154
                    if !is_taproot
48✔
2155
                        && !params.only_witness_utxo
40✔
2156
                        && foreign_psbt_input.non_witness_utxo.is_none()
16✔
2157
                    {
2158
                        return Err(CreateTxError::MissingNonWitnessUtxo(outpoint));
8✔
2159
                    }
40✔
2160
                    *psbt_input = *foreign_psbt_input;
40✔
2161
                }
2162
            }
2163
        }
2164

2165
        self.update_psbt_with_descriptor(&mut psbt)?;
1,040✔
2166

2167
        Ok(psbt)
1,040✔
2168
    }
1,056✔
2169

2170
    /// get the corresponding PSBT Input for a LocalUtxo
2171
    pub fn get_psbt_input(
1,144✔
2172
        &self,
1,144✔
2173
        utxo: LocalOutput,
1,144✔
2174
        sighash_type: Option<psbt::PsbtSighashType>,
1,144✔
2175
        only_witness_utxo: bool,
1,144✔
2176
    ) -> Result<psbt::Input, CreateTxError> {
1,144✔
2177
        // Try to find the prev_script in our db to figure out if this is internal or external,
2178
        // and the derivation index
2179
        let &(keychain, child) = self
1,144✔
2180
            .indexed_graph
1,144✔
2181
            .index
1,144✔
2182
            .index_of_spk(&utxo.txout.script_pubkey)
1,144✔
2183
            .ok_or(CreateTxError::UnknownUtxo)?;
1,144✔
2184

2185
        let mut psbt_input = psbt::Input {
1,144✔
2186
            sighash_type,
1,144✔
2187
            ..psbt::Input::default()
1,144✔
2188
        };
1,144✔
2189

1,144✔
2190
        let desc = self.get_descriptor_for_keychain(keychain);
1,144✔
2191
        let derived_descriptor = desc
1,144✔
2192
            .at_derivation_index(child)
1,144✔
2193
            .expect("child can't be hardened");
1,144✔
2194

1,144✔
2195
        psbt_input
1,144✔
2196
            .update_with_descriptor_unchecked(&derived_descriptor)
1,144✔
2197
            .map_err(MiniscriptPsbtError::Conversion)?;
1,144✔
2198

2199
        let prev_output = utxo.outpoint;
1,144✔
2200
        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
1,144✔
2201
            if desc.is_witness() || desc.is_taproot() {
1,144✔
2202
                psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
1,112✔
2203
            }
1,112✔
2204
            if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
1,144✔
2205
                psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
912✔
2206
            }
912✔
2207
        }
×
2208
        Ok(psbt_input)
1,144✔
2209
    }
1,144✔
2210

2211
    fn update_psbt_with_descriptor(&self, psbt: &mut Psbt) -> Result<(), MiniscriptPsbtError> {
1,384✔
2212
        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
1,384✔
2213
        // the input utxos and outputs
1,384✔
2214
        let utxos = (0..psbt.inputs.len())
1,384✔
2215
            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
1,552✔
2216
            .chain(
1,384✔
2217
                psbt.unsigned_tx
1,384✔
2218
                    .output
1,384✔
2219
                    .iter()
1,384✔
2220
                    .enumerate()
1,384✔
2221
                    .map(|(i, out)| (false, i, out.clone())),
2,184✔
2222
            )
1,384✔
2223
            .collect::<Vec<_>>();
1,384✔
2224

2225
        // Try to figure out the keychain and derivation for every input and output
2226
        for (is_input, index, out) in utxos.into_iter() {
3,696✔
2227
            if let Some(&(keychain, child)) =
3,120✔
2228
                self.indexed_graph.index.index_of_spk(&out.script_pubkey)
3,696✔
2229
            {
2230
                let desc = self.get_descriptor_for_keychain(keychain);
3,120✔
2231
                let desc = desc
3,120✔
2232
                    .at_derivation_index(child)
3,120✔
2233
                    .expect("child can't be hardened");
3,120✔
2234

3,120✔
2235
                if is_input {
3,120✔
2236
                    psbt.update_input_with_descriptor(index, &desc)
1,440✔
2237
                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
1,440✔
2238
                } else {
2239
                    psbt.update_output_with_descriptor(index, &desc)
1,680✔
2240
                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
1,680✔
2241
                }
2242
            }
576✔
2243
        }
2244

2245
        Ok(())
1,384✔
2246
    }
1,384✔
2247

2248
    /// Return the checksum of the public descriptor associated to `keychain`
2249
    ///
2250
    /// Internally calls [`Self::get_descriptor_for_keychain`] to fetch the right descriptor
2251
    pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
8✔
2252
        self.get_descriptor_for_keychain(keychain)
8✔
2253
            .to_string()
8✔
2254
            .split_once('#')
8✔
2255
            .unwrap()
8✔
2256
            .1
8✔
2257
            .to_string()
8✔
2258
    }
8✔
2259

2260
    /// Applies an update to the wallet and stages the changes (but does not persist them).
2261
    ///
2262
    /// Usually you create an `update` by interacting with some blockchain data source and inserting
2263
    /// transactions related to your wallet into it.
2264
    ///
2265
    /// After applying updates you should persist the staged wallet changes. For an example of how
2266
    /// to persist staged wallet changes see [`Wallet::reveal_next_address`]. `
2267
    ///
2268
    /// [`commit`]: Self::commit
2269
    pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
×
2270
        let update = update.into();
×
2271
        let mut changeset = match update.chain {
×
2272
            Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
×
2273
            None => ChangeSet::default(),
×
2274
        };
2275

2276
        let index_changeset = self
×
2277
            .indexed_graph
×
2278
            .index
×
2279
            .reveal_to_target_multi(&update.last_active_indices);
×
NEW
2280
        changeset.append(index_changeset.into());
×
NEW
2281
        changeset.append(self.indexed_graph.apply_update(update.graph).into());
×
NEW
2282
        self.stage.append(changeset);
×
2283
        Ok(())
×
2284
    }
×
2285

2286
    /// Commits all currently [`staged`](Wallet::staged) changes to the `persist_backend`.
2287
    ///
2288
    /// This returns whether anything was persisted.
2289
    ///
2290
    /// # Error
2291
    ///
2292
    /// Returns a backend-defined error if this fails.
2293
    pub fn commit_to<B>(&mut self, persist_backend: &mut B) -> Result<bool, B::WriteError>
4✔
2294
    where
4✔
2295
        B: PersistBackend<ChangeSet>,
4✔
2296
    {
4✔
2297
        let committed = StageExt::commit_to(&mut self.stage, persist_backend)?;
4✔
2298
        Ok(committed.is_some())
4✔
2299
    }
4✔
2300

2301
    /// Commits all currently [`staged`](Wallet::staged) changes to the async `persist_backend`.
2302
    ///
2303
    /// This returns whether anything was persisted.
2304
    ///
2305
    /// # Error
2306
    ///
2307
    /// Returns a backend-defined error if this fails.
2308
    #[cfg(feature = "async")]
NEW
2309
    pub async fn commit_to_async<B>(
×
NEW
2310
        &mut self,
×
NEW
2311
        persist_backend: &mut B,
×
NEW
2312
    ) -> Result<bool, B::WriteError>
×
NEW
2313
    where
×
NEW
2314
        B: bdk_chain::persist::PersistBackendAsync<ChangeSet> + Send + Sync,
×
NEW
2315
    {
×
NEW
2316
        let committed =
×
NEW
2317
            bdk_chain::persist::StageExtAsync::commit_to(&mut self.stage, persist_backend).await?;
×
NEW
2318
        Ok(committed.is_some())
×
NEW
2319
    }
×
2320

2321
    /// Get the staged [`ChangeSet`] that is yet to be committed.
2322
    pub fn staged(&self) -> &ChangeSet {
×
NEW
2323
        &self.stage
×
2324
    }
×
2325

2326
    /// Get a reference to the inner [`TxGraph`].
2327
    pub fn tx_graph(&self) -> &TxGraph<ConfirmationTimeHeightAnchor> {
×
2328
        self.indexed_graph.graph()
×
2329
    }
×
2330

2331
    /// Get a reference to the inner [`KeychainTxOutIndex`].
2332
    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
48✔
2333
        &self.indexed_graph.index
48✔
2334
    }
48✔
2335

2336
    /// Get a reference to the inner [`LocalChain`].
2337
    pub fn local_chain(&self) -> &LocalChain {
×
2338
        &self.chain
×
2339
    }
×
2340

2341
    /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
2342
    /// `prev_blockhash` of the block's header.
2343
    ///
2344
    /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
2345
    /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
2346
    ///
2347
    /// [`apply_block_connected_to`]: Self::apply_block_connected_to
2348
    pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
×
2349
        let connected_to = match height.checked_sub(1) {
×
2350
            Some(prev_height) => BlockId {
×
2351
                height: prev_height,
×
2352
                hash: block.header.prev_blockhash,
×
2353
            },
×
2354
            None => BlockId {
×
2355
                height,
×
2356
                hash: block.block_hash(),
×
2357
            },
×
2358
        };
2359
        self.apply_block_connected_to(block, height, connected_to)
×
2360
            .map_err(|err| match err {
×
2361
                ApplyHeaderError::InconsistentBlocks => {
2362
                    unreachable!("connected_to is derived from the block so must be consistent")
×
2363
                }
2364
                ApplyHeaderError::CannotConnect(err) => err,
×
2365
            })
×
2366
    }
×
2367

2368
    /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
2369
    /// block to the internal chain.
2370
    ///
2371
    /// The `connected_to` parameter informs the wallet how this block connects to the internal
2372
    /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
2373
    /// internal [`TxGraph`].
2374
    ///
2375
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2376
    /// if you need the inserted block data to be reloaded after closing the wallet.
2377
    /// See [`Wallet::reveal_next_address`].
2378
    pub fn apply_block_connected_to(
×
2379
        &mut self,
×
2380
        block: &Block,
×
2381
        height: u32,
×
2382
        connected_to: BlockId,
×
2383
    ) -> Result<(), ApplyHeaderError> {
×
2384
        let mut changeset = ChangeSet::default();
×
2385
        changeset.append(
×
2386
            self.chain
×
2387
                .apply_header_connected_to(&block.header, height, connected_to)?
×
2388
                .into(),
×
2389
        );
×
2390
        changeset.append(
×
2391
            self.indexed_graph
×
2392
                .apply_block_relevant(block, height)
×
2393
                .into(),
×
2394
        );
×
NEW
2395
        self.stage.append(changeset);
×
2396
        Ok(())
×
2397
    }
×
2398

2399
    /// Apply relevant unconfirmed transactions to the wallet.
2400
    ///
2401
    /// Transactions that are not relevant are filtered out.
2402
    ///
2403
    /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
2404
    /// when the transaction was last seen in the mempool. This is used for conflict resolution
2405
    /// when there is conflicting unconfirmed transactions. The transaction with the later
2406
    /// `last_seen` is prioritized.
2407
    ///
2408
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2409
    /// if you need the applied unconfirmed transactions to be reloaded after closing the wallet.
2410
    /// See [`Wallet::reveal_next_address`].
2411
    pub fn apply_unconfirmed_txs<'t>(
×
2412
        &mut self,
×
2413
        unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
×
2414
    ) {
×
2415
        let indexed_graph_changeset = self
×
2416
            .indexed_graph
×
2417
            .batch_insert_relevant_unconfirmed(unconfirmed_txs);
×
NEW
2418
        self.stage.append(indexed_graph_changeset.into());
×
2419
    }
×
2420
}
2421

2422
/// Methods to construct sync/full-scan requests for spk-based chain sources.
2423
impl Wallet {
2424
    /// Create a partial [`SyncRequest`] for this wallet for all revealed spks.
2425
    ///
2426
    /// This is the first step when performing a spk-based wallet partial sync, the returned
2427
    /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
2428
    /// start a blockchain sync with a spk based blockchain client.
2429
    pub fn start_sync_with_revealed_spks(&self) -> SyncRequest {
×
2430
        SyncRequest::from_chain_tip(self.chain.tip())
×
2431
            .populate_with_revealed_spks(&self.indexed_graph.index, ..)
×
2432
    }
×
2433

2434
    /// Create a [`FullScanRequest] for this wallet.
2435
    ///
2436
    /// This is the first step when performing a spk-based wallet full scan, the returned
2437
    /// [`FullScanRequest] collects iterators for the wallet's keychain script pub keys needed to
2438
    /// start a blockchain full scan with a spk based blockchain client.
2439
    ///
2440
    /// This operation is generally only used when importing or restoring a previously used wallet
2441
    /// in which the list of used scripts is not known.
2442
    pub fn start_full_scan(&self) -> FullScanRequest<KeychainKind> {
×
2443
        FullScanRequest::from_keychain_txout_index(self.chain.tip(), &self.indexed_graph.index)
×
2444
    }
×
2445
}
2446

2447
impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet {
2448
    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor> {
×
2449
        self.indexed_graph.graph()
×
2450
    }
×
2451
}
2452

2453
/// Deterministically generate a unique name given the descriptors defining the wallet
2454
///
2455
/// Compatible with [`wallet_name_from_descriptor`]
2456
pub fn wallet_name_from_descriptor<T>(
×
2457
    descriptor: T,
×
2458
    change_descriptor: Option<T>,
×
2459
    network: Network,
×
2460
    secp: &SecpCtx,
×
2461
) -> Result<String, DescriptorError>
×
2462
where
×
2463
    T: IntoWalletDescriptor,
×
2464
{
×
2465
    //TODO check descriptors contains only public keys
2466
    let descriptor = descriptor
×
2467
        .into_wallet_descriptor(secp, network)?
×
2468
        .0
2469
        .to_string();
×
2470
    let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
×
2471
    if let Some(change_descriptor) = change_descriptor {
×
2472
        let change_descriptor = change_descriptor
×
2473
            .into_wallet_descriptor(secp, network)?
×
2474
            .0
2475
            .to_string();
×
2476
        wallet_name.push_str(
×
2477
            calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
×
2478
        );
2479
    }
×
2480

2481
    Ok(wallet_name)
×
2482
}
×
2483

2484
fn new_local_utxo(
1,400✔
2485
    keychain: KeychainKind,
1,400✔
2486
    derivation_index: u32,
1,400✔
2487
    full_txo: FullTxOut<ConfirmationTimeHeightAnchor>,
1,400✔
2488
) -> LocalOutput {
1,400✔
2489
    LocalOutput {
1,400✔
2490
        outpoint: full_txo.outpoint,
1,400✔
2491
        txout: full_txo.txout,
1,400✔
2492
        is_spent: full_txo.spent_by.is_some(),
1,400✔
2493
        confirmation_time: full_txo.chain_position.into(),
1,400✔
2494
        keychain,
1,400✔
2495
        derivation_index,
1,400✔
2496
    }
1,400✔
2497
}
1,400✔
2498

2499
fn create_signers<E: IntoWalletDescriptor>(
249✔
2500
    index: &mut KeychainTxOutIndex<KeychainKind>,
249✔
2501
    secp: &Secp256k1<All>,
249✔
2502
    descriptor: E,
249✔
2503
    change_descriptor: E,
249✔
2504
    network: Network,
249✔
2505
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), DescriptorError> {
249✔
2506
    let descriptor = into_wallet_descriptor_checked(descriptor, secp, network)?;
249✔
2507
    let change_descriptor = into_wallet_descriptor_checked(change_descriptor, secp, network)?;
249✔
2508
    let (descriptor, keymap) = descriptor;
249✔
2509
    let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
249✔
2510
    let _ = index
249✔
2511
        .insert_descriptor(KeychainKind::External, descriptor)
249✔
2512
        .expect("this is the first descriptor we're inserting");
249✔
2513

249✔
2514
    let (descriptor, keymap) = change_descriptor;
249✔
2515
    let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
249✔
2516
    let _ = index
249✔
2517
        .insert_descriptor(KeychainKind::Internal, descriptor)
249✔
2518
        .map_err(|e| {
249✔
2519
            use bdk_chain::keychain::InsertDescriptorError;
2✔
2520
            match e {
2✔
2521
                InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2522
                    crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2✔
2523
                }
2524
                InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2525
                    unreachable!("this is the first time we're assigning internal")
×
2526
                }
2527
            }
2528
        })?;
249✔
2529

2530
    Ok((signers, change_signers))
247✔
2531
}
249✔
2532

2533
/// Transforms a [`FeeRate`] to `f64` with unit as sat/vb.
2534
#[macro_export]
2535
#[doc(hidden)]
2536
macro_rules! floating_rate {
2537
    ($rate:expr) => {{
2538
        use $crate::bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
2539
        // sat_kwu / 250.0 -> sat_vb
2540
        $rate.to_sat_per_kwu() as f64 / ((1000 / WITNESS_SCALE_FACTOR) as f64)
2541
    }};
2542
}
2543

2544
#[macro_export]
2545
#[doc(hidden)]
2546
/// Macro for getting a wallet for use in a doctest
2547
macro_rules! doctest_wallet {
2548
    () => {{
2549
        use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
2550
        use $crate::chain::{ConfirmationTime, BlockId};
2551
        use $crate::{KeychainKind, wallet::Wallet};
2552
        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
2553
        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
2554

2555
        let mut wallet = Wallet::new(
2556
            descriptor,
2557
            change_descriptor,
2558
            Network::Regtest,
2559
        )
2560
        .unwrap();
2561
        let address = wallet.peek_address(KeychainKind::External, 0).address;
2562
        let tx = Transaction {
2563
            version: transaction::Version::ONE,
2564
            lock_time: absolute::LockTime::ZERO,
2565
            input: vec![],
2566
            output: vec![TxOut {
2567
                value: Amount::from_sat(500_000),
2568
                script_pubkey: address.script_pubkey(),
2569
            }],
2570
        };
2571
        let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() });
2572
        let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed {
2573
            height: 500,
2574
            time: 50_000
2575
        });
2576

2577
        wallet
2578
    }}
2579
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc