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

bitcoindevkit / bdk / 10028288592

21 Jul 2024 12:25PM UTC coverage: 83.573% (+0.1%) from 83.434%
10028288592

Pull #1478

github

web-flow
Merge 04b364869 into d99b3ef4b
Pull Request #1478: Make `bdk_esplora` more modular

336 of 398 new or added lines in 3 files covered. (84.42%)

190 existing lines in 6 files now uncovered.

11137 of 13326 relevant lines covered (83.57%)

16459.27 hits per line

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

83.99
/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::Balance;
23
use bdk_chain::{
24
    indexed_tx_graph,
25
    indexer::keychain_txout::KeychainTxOutIndex,
26
    local_chain::{
27
        self, ApplyHeaderError, CannotConnectError, CheckPoint, CheckPointIter, LocalChain,
28
    },
29
    spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult},
30
    tx_graph::{CanonicalTx, TxGraph, TxNode},
31
    BlockId, ChainPosition, ConfirmationBlockTime, ConfirmationTime, FullTxOut, Indexed,
32
    IndexedTxGraph, Merge,
33
};
34
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
35
use bitcoin::{
36
    absolute, psbt, Address, Block, FeeRate, Network, OutPoint, Script, ScriptBuf, Sequence,
37
    Transaction, TxOut, Txid, Witness,
38
};
39
use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
40
use bitcoin::{constants::genesis_block, Amount};
41
use bitcoin::{
42
    secp256k1::{All, Secp256k1},
43
    Weight,
44
};
45
use core::fmt;
46
use core::mem;
47
use core::ops::Deref;
48
use rand_core::RngCore;
49

50
use descriptor::error::Error as DescriptorError;
51
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
52

53
use bdk_chain::tx_graph::CalculateFeeError;
54

55
pub mod coin_selection;
56
pub mod export;
57
pub mod signer;
58
pub mod tx_builder;
59
pub(crate) mod utils;
60

61
pub mod error;
62

63
pub use utils::IsDust;
64

65
use coin_selection::DefaultCoinSelectionAlgorithm;
66
use signer::{SignOptions, SignerOrdering, SignersContainer, TransactionSigner};
67
use tx_builder::{FeePolicy, TxBuilder, TxParams};
68
use utils::{check_nsequence_rbf, After, Older, SecpCtx};
69

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

81
use self::coin_selection::Error;
82

83
const COINBASE_MATURITY: u32 = 100;
84

85
/// A Bitcoin wallet
86
///
87
/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
88
/// Its main components are:
89
///
90
/// 1. output *descriptors* from which it can derive addresses.
91
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
92
///
93
/// The user is responsible for loading and writing wallet changes which are represented as
94
/// [`ChangeSet`]s (see [`take_staged`]). Also see individual functions and example for instructions
95
/// on when [`Wallet`] state needs to be persisted.
96
///
97
/// The `Wallet` descriptor (external) and change descriptor (internal) must not derive the same
98
/// script pubkeys. See [`KeychainTxOutIndex::insert_descriptor()`] for more details.
99
///
100
/// [`signer`]: crate::signer
101
/// [`take_staged`]: Wallet::take_staged
102
#[derive(Debug)]
103
pub struct Wallet {
104
    signers: Arc<SignersContainer>,
105
    change_signers: Arc<SignersContainer>,
106
    chain: LocalChain,
107
    indexed_graph: IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>,
108
    stage: ChangeSet,
109
    network: Network,
110
    secp: SecpCtx,
111
}
112

113
/// An update to [`Wallet`].
114
///
115
/// It updates [`KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
116
#[derive(Debug, Clone, Default)]
117
pub struct Update {
118
    /// Contains the last active derivation indices per keychain (`K`), which is used to update the
119
    /// [`KeychainTxOutIndex`].
120
    pub last_active_indices: BTreeMap<KeychainKind, u32>,
121

122
    /// Update for the wallet's internal [`TxGraph`].
123
    pub graph: TxGraph<ConfirmationBlockTime>,
124

125
    /// Update for the wallet's internal [`LocalChain`].
126
    ///
127
    /// [`LocalChain`]: local_chain::LocalChain
128
    pub chain: Option<CheckPoint>,
129
}
130

131
impl From<FullScanResult<KeychainKind>> for Update {
UNCOV
132
    fn from(value: FullScanResult<KeychainKind>) -> Self {
×
UNCOV
133
        Self {
×
UNCOV
134
            last_active_indices: value.last_active_indices,
×
135
            graph: value.graph_update,
×
136
            chain: Some(value.chain_update),
×
137
        }
×
138
    }
×
139
}
140

141
impl From<SyncResult> for Update {
UNCOV
142
    fn from(value: SyncResult) -> Self {
×
UNCOV
143
        Self {
×
UNCOV
144
            last_active_indices: BTreeMap::new(),
×
145
            graph: value.graph_update,
×
146
            chain: Some(value.chain_update),
×
147
        }
×
148
    }
×
149
}
150

151
/// The changes made to a wallet by applying an [`Update`].
152
pub type ChangeSet = bdk_chain::CombinedChangeSet<KeychainKind, ConfirmationBlockTime>;
153

154
/// A derived address and the index it was found at.
155
/// For convenience this automatically derefs to `Address`
156
#[derive(Debug, PartialEq, Eq)]
157
pub struct AddressInfo {
158
    /// Child index of this address
159
    pub index: u32,
160
    /// Address
161
    pub address: Address,
162
    /// Type of keychain
163
    pub keychain: KeychainKind,
164
}
165

166
impl Deref for AddressInfo {
167
    type Target = Address;
168

169
    fn deref(&self) -> &Self::Target {
800✔
170
        &self.address
800✔
171
    }
800✔
172
}
173

174
impl fmt::Display for AddressInfo {
175
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208✔
176
        write!(f, "{}", self.address)
208✔
177
    }
208✔
178
}
179

180
/// The error type when constructing a fresh [`Wallet`].
181
///
182
/// Methods [`new`] and [`new_with_genesis_hash`] may return this error.
183
///
184
/// [`new`]: Wallet::new
185
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
186
#[derive(Debug)]
187
pub enum NewError {
188
    /// There was problem with the passed-in descriptor(s).
189
    Descriptor(crate::descriptor::DescriptorError),
190
}
191

192
impl fmt::Display for NewError {
UNCOV
193
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
194
        match self {
×
UNCOV
195
            NewError::Descriptor(e) => e.fmt(f),
×
196
        }
×
197
    }
×
198
}
199

200
#[cfg(feature = "std")]
201
impl std::error::Error for NewError {}
202

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

220
impl fmt::Display for LoadError {
UNCOV
221
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
222
        match self {
×
UNCOV
223
            LoadError::Descriptor(e) => e.fmt(f),
×
224
            LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
×
225
            LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
×
226
            LoadError::MissingDescriptor(k) => {
×
227
                write!(f, "loaded data is missing descriptor for keychain {k:?}")
×
228
            }
229
        }
230
    }
×
231
}
232

233
#[cfg(feature = "std")]
234
impl std::error::Error for LoadError {}
235

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

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

290
#[cfg(feature = "std")]
291
impl std::error::Error for NewOrLoadError {}
292

293
/// An error that may occur when applying a block to [`Wallet`].
294
#[derive(Debug)]
295
pub enum ApplyBlockError {
296
    /// Occurs when the update chain cannot connect with original chain.
297
    CannotConnect(CannotConnectError),
298
    /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
299
    UnexpectedConnectedToHash {
300
        /// Block hash of `connected_to`.
301
        connected_to_hash: BlockHash,
302
        /// Expected block hash of `connected_to`, as derived from `block`.
303
        expected_hash: BlockHash,
304
    },
305
}
306

307
impl fmt::Display for ApplyBlockError {
UNCOV
308
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
UNCOV
309
        match self {
×
UNCOV
310
            ApplyBlockError::CannotConnect(err) => err.fmt(f),
×
311
            ApplyBlockError::UnexpectedConnectedToHash {
312
                expected_hash: block_hash,
×
313
                connected_to_hash: checkpoint_hash,
×
UNCOV
314
            } => write!(
×
315
                f,
×
316
                "`connected_to` hash {} differs from the expected hash {} (which is derived from `block`)",
×
317
                checkpoint_hash, block_hash
×
318
            ),
×
319
        }
320
    }
×
321
}
322

323
#[cfg(feature = "std")]
324
impl std::error::Error for ApplyBlockError {}
325

326
impl Wallet {
327
    /// Initialize an empty [`Wallet`].
328
    pub fn new<E: IntoWalletDescriptor>(
152✔
329
        descriptor: E,
152✔
330
        change_descriptor: E,
152✔
331
        network: Network,
152✔
332
    ) -> Result<Self, NewError> {
152✔
333
        let genesis_hash = genesis_block(network).block_hash();
152✔
334
        Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
152✔
335
    }
152✔
336

337
    /// Initialize an empty [`Wallet`] with a custom genesis hash.
338
    ///
339
    /// This is like [`Wallet::new`] with an additional `genesis_hash` parameter. This is useful
340
    /// for syncing from alternative networks.
341
    pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
154✔
342
        descriptor: E,
154✔
343
        change_descriptor: E,
154✔
344
        network: Network,
154✔
345
        genesis_hash: BlockHash,
154✔
346
    ) -> Result<Self, NewError> {
154✔
347
        let secp = Secp256k1::new();
154✔
348
        let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
154✔
349
        let mut index = KeychainTxOutIndex::<KeychainKind>::default();
154✔
350

351
        let (signers, change_signers) =
152✔
352
            create_signers(&mut index, &secp, descriptor, change_descriptor, network)
154✔
353
                .map_err(NewError::Descriptor)?;
154✔
354

355
        let indexed_graph = IndexedTxGraph::new(index);
152✔
356

152✔
357
        let staged = ChangeSet {
152✔
358
            chain: chain_changeset,
152✔
359
            indexed_tx_graph: indexed_graph.initial_changeset(),
152✔
360
            network: Some(network),
152✔
361
        };
152✔
362

152✔
363
        Ok(Wallet {
152✔
364
            signers,
152✔
365
            change_signers,
152✔
366
            network,
152✔
367
            chain,
152✔
368
            indexed_graph,
152✔
369
            stage: staged,
152✔
370
            secp,
152✔
371
        })
152✔
372
    }
154✔
373

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

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

96✔
441
        let mut indexed_graph = IndexedTxGraph::new(index);
96✔
442
        indexed_graph.apply_changeset(changeset.indexed_tx_graph);
96✔
443

96✔
444
        let stage = ChangeSet::default();
96✔
445

96✔
446
        Ok(Wallet {
96✔
447
            signers,
96✔
448
            change_signers,
96✔
449
            chain,
96✔
450
            indexed_graph,
96✔
451
            stage,
96✔
452
            network,
96✔
453
            secp,
96✔
454
        })
96✔
455
    }
96✔
456

457
    /// Either loads [`Wallet`] from the given [`ChangeSet`] or initializes it if one does not exist.
458
    ///
459
    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
460
    ///
461
    /// ```rust,no_run
462
    /// # use bdk_wallet::Wallet;
463
    /// use bdk_sqlite::{Store, rusqlite::Connection};
464
    /// # use bitcoin::Network::Testnet;
465
    /// let conn = Connection::open_in_memory().expect("must open connection");
466
    /// let mut db = Store::new(conn).expect("must create db");
467
    /// let changeset = db.read()?;
468
    ///
469
    /// let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
470
    /// let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
471
    ///
472
    /// let mut wallet = Wallet::new_or_load(external_descriptor, internal_descriptor, changeset, Testnet)?;
473
    /// # Ok::<(), anyhow::Error>(())
474
    /// ```
475
    pub fn new_or_load<E: IntoWalletDescriptor>(
10✔
476
        descriptor: E,
10✔
477
        change_descriptor: E,
10✔
478
        changeset: Option<ChangeSet>,
10✔
479
        network: Network,
10✔
480
    ) -> Result<Self, NewOrLoadError> {
10✔
481
        let genesis_hash = genesis_block(network).block_hash();
10✔
482
        Self::new_or_load_with_genesis_hash(
10✔
483
            descriptor,
10✔
484
            change_descriptor,
10✔
485
            changeset,
10✔
486
            network,
10✔
487
            genesis_hash,
10✔
488
        )
10✔
489
    }
10✔
490

491
    /// Either loads [`Wallet`] from a [`ChangeSet`] or initializes it if one does not exist, using the
492
    /// provided descriptor, change descriptor, network, and custom genesis hash.
493
    ///
494
    /// This method will fail if the loaded [`ChangeSet`] has different parameters to those provided.
495
    /// This is like [`Wallet::new_or_load`] with an additional `genesis_hash` parameter. This is
496
    /// useful for syncing from alternative networks.
497
    pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
12✔
498
        descriptor: E,
12✔
499
        change_descriptor: E,
12✔
500
        changeset: Option<ChangeSet>,
12✔
501
        network: Network,
12✔
502
        genesis_hash: BlockHash,
12✔
503
    ) -> Result<Self, NewOrLoadError> {
12✔
504
        if let Some(changeset) = changeset {
12✔
505
            let mut wallet = Self::load_from_changeset(changeset).map_err(|e| match e {
10✔
UNCOV
506
                LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
UNCOV
507
                LoadError::MissingNetwork => NewOrLoadError::LoadedNetworkDoesNotMatch {
×
UNCOV
508
                    expected: network,
×
509
                    got: None,
×
510
                },
×
511
                LoadError::MissingGenesis => NewOrLoadError::LoadedGenesisDoesNotMatch {
×
512
                    expected: genesis_hash,
×
513
                    got: None,
×
514
                },
×
515
                LoadError::MissingDescriptor(keychain) => {
×
516
                    NewOrLoadError::LoadedDescriptorDoesNotMatch {
×
517
                        got: None,
×
518
                        keychain,
×
519
                    }
×
520
                }
521
            })?;
10✔
522
            if wallet.network != network {
10✔
523
                return Err(NewOrLoadError::LoadedNetworkDoesNotMatch {
2✔
524
                    expected: network,
2✔
525
                    got: Some(wallet.network),
2✔
526
                });
2✔
527
            }
8✔
528
            if wallet.chain.genesis_hash() != genesis_hash {
8✔
529
                return Err(NewOrLoadError::LoadedGenesisDoesNotMatch {
2✔
530
                    expected: genesis_hash,
2✔
531
                    got: Some(wallet.chain.genesis_hash()),
2✔
532
                });
2✔
533
            }
6✔
534

535
            let (expected_descriptor, expected_descriptor_keymap) = descriptor
6✔
536
                .into_wallet_descriptor(&wallet.secp, network)
6✔
537
                .map_err(NewOrLoadError::Descriptor)?;
6✔
538
            let wallet_descriptor = wallet.public_descriptor(KeychainKind::External);
6✔
539
            if wallet_descriptor != &expected_descriptor {
6✔
540
                return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
2✔
541
                    got: Some(wallet_descriptor.clone()),
2✔
542
                    keychain: KeychainKind::External,
2✔
543
                });
2✔
544
            }
4✔
545
            // if expected descriptor has private keys add them as new signers
4✔
546
            if !expected_descriptor_keymap.is_empty() {
4✔
547
                let signer_container = SignersContainer::build(
4✔
548
                    expected_descriptor_keymap,
4✔
549
                    &expected_descriptor,
4✔
550
                    &wallet.secp,
4✔
551
                );
4✔
552
                signer_container.signers().into_iter().for_each(|signer| {
4✔
553
                    wallet.add_signer(
4✔
554
                        KeychainKind::External,
4✔
555
                        SignerOrdering::default(),
4✔
556
                        signer.clone(),
4✔
557
                    )
4✔
558
                });
4✔
559
            }
4✔
560

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

587
            Ok(wallet)
2✔
588
        } else {
589
            Self::new_with_genesis_hash(descriptor, change_descriptor, network, genesis_hash)
2✔
590
                .map_err(|e| match e {
2✔
UNCOV
591
                    NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
592
                })
2✔
593
        }
594
    }
12✔
595

596
    /// Get the Bitcoin network the wallet is using.
597
    pub fn network(&self) -> Network {
48✔
598
        self.network
48✔
599
    }
48✔
600

601
    /// Iterator over all keychains in this wallet
602
    pub fn keychains(&self) -> impl Iterator<Item = (&KeychainKind, &ExtendedDescriptor)> {
64✔
603
        self.indexed_graph.index.keychains()
64✔
604
    }
64✔
605

606
    /// Peek an address of the given `keychain` at `index` without revealing it.
607
    ///
608
    /// For non-wildcard descriptors this returns the same address at every provided index.
609
    ///
610
    /// # Panics
611
    ///
612
    /// This panics when the caller requests for an address of derivation index greater than the
613
    /// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
614
    pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
1,248✔
615
        let mut spk_iter = self
1,248✔
616
            .indexed_graph
1,248✔
617
            .index
1,248✔
618
            .unbounded_spk_iter(&keychain)
1,248✔
619
            .expect("keychain must exist");
1,248✔
620
        if !spk_iter.descriptor().has_wildcard() {
1,248✔
621
            index = 0;
952✔
622
        }
952✔
623
        let (index, spk) = spk_iter
1,248✔
624
            .nth(index as usize)
1,248✔
625
            .expect("derivation index is out of bounds");
1,248✔
626

1,248✔
627
        AddressInfo {
1,248✔
628
            index,
1,248✔
629
            address: Address::from_script(&spk, self.network).expect("must have address form"),
1,248✔
630
            keychain,
1,248✔
631
        }
1,248✔
632
    }
1,248✔
633

634
    /// Attempt to reveal the next address of the given `keychain`.
635
    ///
636
    /// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
637
    /// contain a wildcard or every address is already revealed up to the maximum derivation
638
    /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
639
    /// then the last revealed address will be returned.
640
    ///
641
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
642
    /// calls to this method before closing the wallet. For example:
643
    ///
644
    /// ```rust,no_run
645
    /// # use bdk_wallet::wallet::{Wallet, ChangeSet};
646
    /// # use bdk_wallet::KeychainKind;
647
    /// use bdk_sqlite::{rusqlite::Connection, Store};
648
    /// let conn = Connection::open_in_memory().expect("must open connection");
649
    /// let mut db = Store::new(conn).expect("must create store");
650
    /// # let changeset = ChangeSet::default();
651
    /// # let mut wallet = Wallet::load_from_changeset(changeset).expect("load wallet");
652
    /// let next_address = wallet.reveal_next_address(KeychainKind::External);
653
    /// if let Some(changeset) = wallet.take_staged() {
654
    ///     db.write(&changeset)?;
655
    /// }
656
    ///
657
    /// // Now it's safe to show the user their next address!
658
    /// println!("Next address: {}", next_address.address);
659
    /// # Ok::<(), anyhow::Error>(())
660
    /// ```
661
    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo {
120✔
662
        let index = &mut self.indexed_graph.index;
120✔
663
        let stage = &mut self.stage;
120✔
664

120✔
665
        let ((index, spk), index_changeset) = index
120✔
666
            .reveal_next_spk(&keychain)
120✔
667
            .expect("keychain must exist");
120✔
668

120✔
669
        stage.merge(indexed_tx_graph::ChangeSet::from(index_changeset).into());
120✔
670

120✔
671
        AddressInfo {
120✔
672
            index,
120✔
673
            address: Address::from_script(spk.as_script(), self.network)
120✔
674
                .expect("must have address form"),
120✔
675
            keychain,
120✔
676
        }
120✔
677
    }
120✔
678

679
    /// Reveal addresses up to and including the target `index` and return an iterator
680
    /// of newly revealed addresses.
681
    ///
682
    /// If the target `index` is unreachable, we make a best effort to reveal up to the last
683
    /// possible index. If all addresses up to the given `index` are already revealed, then
684
    /// no new addresses are returned.
685
    ///
686
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
687
    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
688
    pub fn reveal_addresses_to(
16✔
689
        &mut self,
16✔
690
        keychain: KeychainKind,
16✔
691
        index: u32,
16✔
692
    ) -> impl Iterator<Item = AddressInfo> + '_ {
16✔
693
        let (spks, index_changeset) = self
16✔
694
            .indexed_graph
16✔
695
            .index
16✔
696
            .reveal_to_target(&keychain, index)
16✔
697
            .expect("keychain must exist");
16✔
698

16✔
699
        self.stage.merge(index_changeset.into());
16✔
700

16✔
701
        spks.into_iter().map(move |(index, spk)| AddressInfo {
24✔
702
            index,
10✔
703
            address: Address::from_script(&spk, self.network).expect("must have address form"),
10✔
704
            keychain,
10✔
705
        })
24✔
706
    }
16✔
707

708
    /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
709
    /// derivation index that hasn't been used.
710
    ///
711
    /// This will attempt to derive and reveal a new address if no newly revealed addresses
712
    /// are available. See also [`reveal_next_address`](Self::reveal_next_address).
713
    ///
714
    /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
715
    /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
716
    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo {
888✔
717
        let index = &mut self.indexed_graph.index;
888✔
718

888✔
719
        let ((index, spk), index_changeset) = index
888✔
720
            .next_unused_spk(&keychain)
888✔
721
            .expect("keychain must exist");
888✔
722

888✔
723
        self.stage
888✔
724
            .merge(indexed_tx_graph::ChangeSet::from(index_changeset).into());
888✔
725

888✔
726
        AddressInfo {
888✔
727
            index,
888✔
728
            address: Address::from_script(spk.as_script(), self.network)
888✔
729
                .expect("must have address form"),
888✔
730
            keychain,
888✔
731
        }
888✔
732
    }
888✔
733

734
    /// Marks an address used of the given `keychain` at `index`.
735
    ///
736
    /// Returns whether the given index was present and then removed from the unused set.
737
    pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
8✔
738
        self.indexed_graph.index.mark_used(keychain, index)
8✔
739
    }
8✔
740

741
    /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted
742
    /// back into the unused set.
743
    ///
744
    /// Since this is only a superficial marker, it will have no effect if the address at the given
745
    /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the
746
    /// derived spk.
747
    ///
748
    /// [`mark_used`]: Self::mark_used
749
    pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
16✔
750
        self.indexed_graph.index.unmark_used(keychain, index)
16✔
751
    }
16✔
752

753
    /// List addresses that are revealed but unused.
754
    ///
755
    /// Note if the returned iterator is empty you can reveal more addresses
756
    /// by using [`reveal_next_address`](Self::reveal_next_address) or
757
    /// [`reveal_addresses_to`](Self::reveal_addresses_to).
758
    pub fn list_unused_addresses(
24✔
759
        &self,
24✔
760
        keychain: KeychainKind,
24✔
761
    ) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
24✔
762
        self.indexed_graph
24✔
763
            .index
24✔
764
            .unused_keychain_spks(&keychain)
24✔
765
            .map(move |(index, spk)| AddressInfo {
32✔
766
                index,
11✔
767
                address: Address::from_script(spk, self.network).expect("must have address form"),
11✔
768
                keychain,
11✔
769
            })
32✔
770
    }
24✔
771

772
    /// Return whether or not a `script` is part of this wallet (either internal or external)
773
    pub fn is_mine(&self, script: &Script) -> bool {
1,792✔
774
        self.indexed_graph.index.index_of_spk(script).is_some()
1,792✔
775
    }
1,792✔
776

777
    /// Finds how the wallet derived the script pubkey `spk`.
778
    ///
779
    /// Will only return `Some(_)` if the wallet has given out the spk.
780
    pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
56✔
781
        self.indexed_graph.index.index_of_spk(spk).cloned()
56✔
782
    }
56✔
783

784
    /// Return the list of unspent outputs of this wallet
785
    pub fn list_unspent(&self) -> impl Iterator<Item = LocalOutput> + '_ {
1,248✔
786
        self.indexed_graph
1,248✔
787
            .graph()
1,248✔
788
            .filter_chain_unspents(
1,248✔
789
                &self.chain,
1,248✔
790
                self.chain.tip().block_id(),
1,248✔
791
                self.indexed_graph.index.outpoints().iter().cloned(),
1,248✔
792
            )
1,248✔
793
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
1,352✔
794
    }
1,248✔
795

796
    /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
797
    ///
798
    /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
799
    pub fn list_output(&self) -> impl Iterator<Item = LocalOutput> + '_ {
8✔
800
        self.indexed_graph
8✔
801
            .graph()
8✔
802
            .filter_chain_txouts(
8✔
803
                &self.chain,
8✔
804
                self.chain.tip().block_id(),
8✔
805
                self.indexed_graph.index.outpoints().iter().cloned(),
8✔
806
            )
8✔
807
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
9✔
808
    }
8✔
809

810
    /// Get all the checkpoints the wallet is currently storing indexed by height.
UNCOV
811
    pub fn checkpoints(&self) -> CheckPointIter {
×
UNCOV
812
        self.chain.iter_checkpoints()
×
UNCOV
813
    }
×
814

815
    /// Returns the latest checkpoint.
816
    pub fn latest_checkpoint(&self) -> CheckPoint {
72✔
817
        self.chain.tip()
72✔
818
    }
72✔
819

820
    /// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
821
    ///
822
    /// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
823
    /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
824
    /// electrum server) which will go through each address until it reaches a *stop gap*.
825
    ///
826
    /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
827
    /// script pubkeys the wallet is storing internally).
UNCOV
828
    pub fn all_unbounded_spk_iters(
×
UNCOV
829
        &self,
×
UNCOV
830
    ) -> BTreeMap<KeychainKind, impl Iterator<Item = Indexed<ScriptBuf>> + Clone> {
×
831
        self.indexed_graph.index.all_unbounded_spk_iters()
×
832
    }
×
833

834
    /// Get an unbounded script pubkey iterator for the given `keychain`.
835
    ///
836
    /// See [`all_unbounded_spk_iters`] for more documentation
837
    ///
838
    /// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
UNCOV
839
    pub fn unbounded_spk_iter(
×
UNCOV
840
        &self,
×
UNCOV
841
        keychain: KeychainKind,
×
842
    ) -> impl Iterator<Item = Indexed<ScriptBuf>> + Clone {
×
843
        self.indexed_graph
×
844
            .index
×
845
            .unbounded_spk_iter(&keychain)
×
846
            .expect("keychain must exist")
×
847
    }
×
848

849
    /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
850
    /// wallet's database.
851
    pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
72✔
852
        let ((keychain, index), _) = self.indexed_graph.index.txout(op)?;
72✔
853
        self.indexed_graph
72✔
854
            .graph()
72✔
855
            .filter_chain_unspents(
72✔
856
                &self.chain,
72✔
857
                self.chain.tip().block_id(),
72✔
858
                core::iter::once(((), op)),
72✔
859
            )
72✔
860
            .map(|(_, full_txo)| new_local_utxo(keychain, index, full_txo))
72✔
861
            .next()
72✔
862
    }
72✔
863

864
    /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
865
    ///
866
    /// This is used for providing a previous output's value so that we can use [`calculate_fee`]
867
    /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
868
    /// not be returned in [`list_unspent`] or [`list_output`].
869
    ///
870
    /// **WARNINGS:** This should only be used to add `TxOut`s that the wallet does not own. Only
871
    /// insert `TxOut`s that you trust the values for!
872
    ///
873
    /// You must persist the changes resulting from one or more calls to this method if you need
874
    /// the inserted `TxOut` data to be reloaded after closing the wallet.
875
    /// See [`Wallet::reveal_next_address`].
876
    ///
877
    /// [`calculate_fee`]: Self::calculate_fee
878
    /// [`calculate_fee_rate`]: Self::calculate_fee_rate
879
    /// [`list_unspent`]: Self::list_unspent
880
    /// [`list_output`]: Self::list_output
881
    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
16✔
882
        let additions = self.indexed_graph.insert_txout(outpoint, txout);
16✔
883
        self.stage.merge(additions.into());
16✔
884
    }
16✔
885

886
    /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction.
887
    ///
888
    /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
889
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
890
    ///
891
    /// Note `tx` does not have to be in the graph for this to work.
892
    ///
893
    /// # Examples
894
    ///
895
    /// ```rust, no_run
896
    /// # use bitcoin::Txid;
897
    /// # use bdk_wallet::Wallet;
898
    /// # let mut wallet: Wallet = todo!();
899
    /// # let txid:Txid = todo!();
900
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
901
    /// let fee = wallet.calculate_fee(&tx).expect("fee");
902
    /// ```
903
    ///
904
    /// ```rust, no_run
905
    /// # use bitcoin::Psbt;
906
    /// # use bdk_wallet::Wallet;
907
    /// # let mut wallet: Wallet = todo!();
908
    /// # let mut psbt: Psbt = todo!();
909
    /// let tx = &psbt.clone().extract_tx().expect("tx");
910
    /// let fee = wallet.calculate_fee(tx).expect("fee");
911
    /// ```
912
    /// [`insert_txout`]: Self::insert_txout
913
    pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
536✔
914
        self.indexed_graph.graph().calculate_fee(tx)
536✔
915
    }
536✔
916

917
    /// Calculate the [`FeeRate`] for a given transaction.
918
    ///
919
    /// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you must
920
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
921
    ///
922
    /// Note `tx` does not have to be in the graph for this to work.
923
    ///
924
    /// # Examples
925
    ///
926
    /// ```rust, no_run
927
    /// # use bitcoin::Txid;
928
    /// # use bdk_wallet::Wallet;
929
    /// # let mut wallet: Wallet = todo!();
930
    /// # let txid:Txid = todo!();
931
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
932
    /// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
933
    /// ```
934
    ///
935
    /// ```rust, no_run
936
    /// # use bitcoin::Psbt;
937
    /// # use bdk_wallet::Wallet;
938
    /// # let mut wallet: Wallet = todo!();
939
    /// # let mut psbt: Psbt = todo!();
940
    /// let tx = &psbt.clone().extract_tx().expect("tx");
941
    /// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
942
    /// ```
943
    /// [`insert_txout`]: Self::insert_txout
944
    pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
144✔
945
        self.calculate_fee(tx).map(|fee| fee / tx.weight())
144✔
946
    }
144✔
947

948
    /// Compute the `tx`'s sent and received [`Amount`]s.
949
    ///
950
    /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
951
    /// that spend from previous txouts tracked by this wallet. Received is the summation
952
    /// of this tx's outputs that send to script pubkeys tracked by this wallet.
953
    ///
954
    /// # Examples
955
    ///
956
    /// ```rust, no_run
957
    /// # use bitcoin::Txid;
958
    /// # use bdk_wallet::Wallet;
959
    /// # let mut wallet: Wallet = todo!();
960
    /// # let txid:Txid = todo!();
961
    /// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
962
    /// let (sent, received) = wallet.sent_and_received(&tx);
963
    /// ```
964
    ///
965
    /// ```rust, no_run
966
    /// # use bitcoin::Psbt;
967
    /// # use bdk_wallet::Wallet;
968
    /// # let mut wallet: Wallet = todo!();
969
    /// # let mut psbt: Psbt = todo!();
970
    /// let tx = &psbt.clone().extract_tx().expect("tx");
971
    /// let (sent, received) = wallet.sent_and_received(tx);
972
    /// ```
973
    pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) {
224✔
974
        self.indexed_graph.index.sent_and_received(tx, ..)
224✔
975
    }
224✔
976

977
    /// Get a single transaction from the wallet as a [`CanonicalTx`] (if the transaction exists).
978
    ///
979
    /// `CanonicalTx` contains the full transaction alongside meta-data such as:
980
    /// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
981
    ///   in the best chain.
982
    /// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
983
    ///   confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
984
    ///   confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
985
    ///   the transaction was last seen in the mempool is provided.
986
    ///
987
    /// ```rust, no_run
988
    /// use bdk_chain::Anchor;
989
    /// use bdk_wallet::{chain::ChainPosition, Wallet};
990
    /// # let wallet: Wallet = todo!();
991
    /// # let my_txid: bitcoin::Txid = todo!();
992
    ///
993
    /// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
994
    ///
995
    /// // get reference to full transaction
996
    /// println!("my tx: {:#?}", canonical_tx.tx_node.tx);
997
    ///
998
    /// // list all transaction anchors
999
    /// for anchor in canonical_tx.tx_node.anchors {
1000
    ///     println!(
1001
    ///         "tx is anchored by block of hash {}",
1002
    ///         anchor.anchor_block().hash
1003
    ///     );
1004
    /// }
1005
    ///
1006
    /// // get confirmation status of transaction
1007
    /// match canonical_tx.chain_position {
1008
    ///     ChainPosition::Confirmed(anchor) => println!(
1009
    ///         "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
1010
    ///         anchor.block_id.height, anchor.block_id.height, anchor.block_id.hash,
1011
    ///     ),
1012
    ///     ChainPosition::Unconfirmed(last_seen) => println!(
1013
    ///         "tx is last seen at {}, it is unconfirmed as it is not anchored in the best chain",
1014
    ///         last_seen,
1015
    ///     ),
1016
    /// }
1017
    /// ```
1018
    ///
1019
    /// [`Anchor`]: bdk_chain::Anchor
1020
    pub fn get_tx(
56✔
1021
        &self,
56✔
1022
        txid: Txid,
56✔
1023
    ) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationBlockTime>> {
56✔
1024
        let graph = self.indexed_graph.graph();
56✔
1025

56✔
1026
        Some(CanonicalTx {
56✔
1027
            chain_position: graph.get_chain_position(
56✔
1028
                &self.chain,
56✔
1029
                self.chain.tip().block_id(),
56✔
1030
                txid,
56✔
1031
            )?,
56✔
1032
            tx_node: graph.get_tx_node(txid)?,
56✔
1033
        })
1034
    }
56✔
1035

1036
    /// Add a new checkpoint to the wallet's internal view of the chain.
1037
    ///
1038
    /// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
1039
    /// there).
1040
    ///
1041
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
1042
    /// if you need the inserted checkpoint data to be reloaded after closing the wallet.
1043
    /// See [`Wallet::reveal_next_address`].
1044
    ///
1045
    /// [`commit`]: Self::commit
1046
    pub fn insert_checkpoint(
3,260✔
1047
        &mut self,
3,260✔
1048
        block_id: BlockId,
3,260✔
1049
    ) -> Result<bool, local_chain::AlterCheckPointError> {
3,260✔
1050
        let changeset = self.chain.insert_block(block_id)?;
3,260✔
1051
        let changed = !changeset.is_empty();
3,260✔
1052
        self.stage.merge(changeset.into());
3,260✔
1053
        Ok(changed)
3,260✔
1054
    }
3,260✔
1055

1056
    /// Add a transaction to the wallet's internal view of the chain. This stages the change,
1057
    /// you must persist it later.
1058
    ///
1059
    /// This method inserts the given `tx` and returns whether anything changed after insertion,
1060
    /// which will be false if the same transaction already exists in the wallet's transaction
1061
    /// graph. Any changes are staged but not committed.
1062
    ///
1063
    /// # Note
1064
    ///
1065
    /// By default the inserted `tx` won't be considered "canonical" because it's not known
1066
    /// whether the transaction exists in the best chain. To know whether it exists, the tx
1067
    /// must be broadcast to the network and the wallet synced via a chain source.
1068
    pub fn insert_tx(&mut self, tx: Transaction) -> bool {
2,446✔
1069
        let mut changeset = ChangeSet::default();
2,446✔
1070
        changeset.merge(self.indexed_graph.insert_tx(tx).into());
2,446✔
1071
        let ret = !changeset.is_empty();
2,446✔
1072
        self.stage.merge(changeset);
2,446✔
1073
        ret
2,446✔
1074
    }
2,446✔
1075

1076
    /// Iterate over the transactions in the wallet.
1077
    pub fn transactions(
38✔
1078
        &self,
38✔
1079
    ) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationBlockTime>> + '_ {
38✔
1080
        self.indexed_graph
38✔
1081
            .graph()
38✔
1082
            .list_canonical_txs(&self.chain, self.chain.tip().block_id())
38✔
1083
    }
38✔
1084

1085
    /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
1086
    /// values.
1087
    pub fn balance(&self) -> Balance {
56✔
1088
        self.indexed_graph.graph().balance(
56✔
1089
            &self.chain,
56✔
1090
            self.chain.tip().block_id(),
56✔
1091
            self.indexed_graph.index.outpoints().iter().cloned(),
56✔
1092
            |&(k, _), _| k == KeychainKind::Internal,
56✔
1093
        )
56✔
1094
    }
56✔
1095

1096
    /// Add an external signer
1097
    ///
1098
    /// See [the `signer` module](signer) for an example.
1099
    pub fn add_signer(
64✔
1100
        &mut self,
64✔
1101
        keychain: KeychainKind,
64✔
1102
        ordering: SignerOrdering,
64✔
1103
        signer: Arc<dyn TransactionSigner>,
64✔
1104
    ) {
64✔
1105
        let signers = match keychain {
64✔
1106
            KeychainKind::External => Arc::make_mut(&mut self.signers),
48✔
1107
            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
16✔
1108
        };
1109

1110
        signers.add_external(signer.id(&self.secp), ordering, signer);
64✔
1111
    }
64✔
1112

1113
    /// Get the signers
1114
    ///
1115
    /// ## Example
1116
    ///
1117
    /// ```
1118
    /// # use bdk_wallet::{Wallet, KeychainKind};
1119
    /// # use bdk_wallet::bitcoin::Network;
1120
    /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
1121
    /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
1122
    /// let wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
1123
    /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
1124
    ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
1125
    ///     println!("secret_key: {}", secret_key);
1126
    /// }
1127
    ///
1128
    /// Ok::<(), Box<dyn std::error::Error>>(())
1129
    /// ```
1130
    pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
36✔
1131
        match keychain {
36✔
1132
            KeychainKind::External => Arc::clone(&self.signers),
22✔
1133
            KeychainKind::Internal => Arc::clone(&self.change_signers),
14✔
1134
        }
1135
    }
36✔
1136

1137
    /// Start building a transaction.
1138
    ///
1139
    /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
1140
    ///
1141
    /// ## Example
1142
    ///
1143
    /// ```
1144
    /// # use std::str::FromStr;
1145
    /// # use bitcoin::*;
1146
    /// # use bdk_wallet::*;
1147
    /// # use bdk_wallet::wallet::ChangeSet;
1148
    /// # use bdk_wallet::wallet::error::CreateTxError;
1149
    /// # use anyhow::Error;
1150
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1151
    /// # let mut wallet = doctest_wallet!();
1152
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1153
    /// let psbt = {
1154
    ///    let mut builder =  wallet.build_tx();
1155
    ///    builder
1156
    ///        .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1157
    ///    builder.finish()?
1158
    /// };
1159
    ///
1160
    /// // sign and broadcast ...
1161
    /// # Ok::<(), anyhow::Error>(())
1162
    /// ```
1163
    ///
1164
    /// [`TxBuilder`]: crate::TxBuilder
1165
    pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm> {
1,120✔
1166
        TxBuilder {
1,120✔
1167
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
1,120✔
1168
            params: TxParams::default(),
1,120✔
1169
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
1,120✔
1170
        }
1,120✔
1171
    }
1,120✔
1172

1173
    pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
149✔
1174
        &mut self,
149✔
1175
        coin_selection: Cs,
149✔
1176
        params: TxParams,
149✔
1177
        rng: &mut impl RngCore,
149✔
1178
    ) -> Result<Psbt, CreateTxError> {
149✔
1179
        let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
149✔
1180
        let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
149✔
1181
        let internal_descriptor = keychains.get(&KeychainKind::Internal).expect("must exist");
149✔
1182

1183
        let external_policy = external_descriptor
149✔
1184
            .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
149✔
1185
            .unwrap();
149✔
1186
        let internal_policy = internal_descriptor
149✔
1187
            .extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
149✔
1188
            .unwrap();
149✔
1189

149✔
1190
        // The policy allows spending external outputs, but it requires a policy path that hasn't been
149✔
1191
        // provided
149✔
1192
        if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
149✔
1193
            && external_policy.requires_path()
148✔
1194
            && params.external_policy_path.is_none()
4✔
1195
        {
1196
            return Err(CreateTxError::SpendingPolicyRequired(
1✔
1197
                KeychainKind::External,
1✔
1198
            ));
1✔
1199
        };
148✔
1200
        // Same for the internal_policy path
148✔
1201
        if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
148✔
1202
            && internal_policy.requires_path()
147✔
UNCOV
1203
            && params.internal_policy_path.is_none()
×
1204
        {
UNCOV
1205
            return Err(CreateTxError::SpendingPolicyRequired(
×
1206
                KeychainKind::Internal,
×
UNCOV
1207
            ));
×
1208
        };
148✔
1209

1210
        let external_requirements = external_policy.get_condition(
148✔
1211
            params
148✔
1212
                .external_policy_path
148✔
1213
                .as_ref()
148✔
1214
                .unwrap_or(&BTreeMap::new()),
148✔
1215
        )?;
148✔
1216
        let internal_requirements = internal_policy.get_condition(
148✔
1217
            params
148✔
1218
                .internal_policy_path
148✔
1219
                .as_ref()
148✔
1220
                .unwrap_or(&BTreeMap::new()),
148✔
1221
        )?;
148✔
1222

1223
        let requirements = external_requirements.merge(&internal_requirements)?;
148✔
1224

1225
        let version = match params.version {
146✔
1226
            Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
1✔
1227
            Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
18✔
1228
                return Err(CreateTxError::Version1Csv)
1✔
1229
            }
1230
            Some(tx_builder::Version(x)) => x,
18✔
1231
            None if requirements.csv.is_some() => 2,
128✔
1232
            None => 1,
124✔
1233
        };
1234

1235
        // We use a match here instead of a unwrap_or_else as it's way more readable :)
1236
        let current_height = match params.current_height {
146✔
1237
            // If they didn't tell us the current height, we assume it's the latest sync height.
1238
            None => {
1239
                let tip_height = self.chain.tip().height();
142✔
1240
                absolute::LockTime::from_height(tip_height).expect("invalid height")
142✔
1241
            }
1242
            Some(h) => h,
4✔
1243
        };
1244

1245
        let lock_time = match params.locktime {
145✔
1246
            // When no nLockTime is specified, we try to prevent fee sniping, if possible
1247
            None => {
1248
                // Fee sniping can be partially prevented by setting the timelock
1249
                // to current_height. If we don't know the current_height,
1250
                // we default to 0.
1251
                let fee_sniping_height = current_height;
143✔
1252

1253
                // We choose the biggest between the required nlocktime and the fee sniping
1254
                // height
1255
                match requirements.timelock {
4✔
1256
                    // No requirement, just use the fee_sniping_height
1257
                    None => fee_sniping_height,
139✔
1258
                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
1259
                    Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => {
4✔
UNCOV
1260
                        fee_sniping_height
×
1261
                    }
1262
                    // There's a time-based requirement or a block-based requirement greater
1263
                    // than the fee_sniping_height use that value
1264
                    Some(value) => value,
4✔
1265
                }
1266
            }
1267
            // Specific nLockTime required and we have no constraints, so just set to that value
1268
            Some(x) if requirements.timelock.is_none() => x,
3✔
1269
            // Specific nLockTime required and it's compatible with the constraints
1270
            Some(x)
1✔
1271
                if requirements.timelock.unwrap().is_same_unit(x)
2✔
1272
                    && x >= requirements.timelock.unwrap() =>
2✔
1273
            {
1✔
1274
                x
1✔
1275
            }
1276
            // Invalid nLockTime required
1277
            Some(x) => {
1✔
1278
                return Err(CreateTxError::LockTime {
1✔
1279
                    requested: x,
1✔
1280
                    required: requirements.timelock.unwrap(),
1✔
1281
                })
1✔
1282
            }
1283
        };
1284

1285
        // The nSequence to be by default for inputs unless an explicit sequence is specified.
1286
        let n_sequence = match (params.rbf, requirements.csv) {
145✔
1287
            // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
1288
            (None, None) if lock_time != absolute::LockTime::ZERO => {
120✔
1289
                Sequence::ENABLE_LOCKTIME_NO_RBF
119✔
1290
            }
1291
            // No RBF, CSV or nLockTime, make the transaction final
1292
            (None, None) => Sequence::MAX,
1✔
1293

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

1299
            // RBF with a specific value but that value is too high
1300
            (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
3✔
1301
                return Err(CreateTxError::RbfSequence)
1✔
1302
            }
1303
            // RBF with a specific value requested, but the value is incompatible with CSV
1304
            (Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
1✔
1305
                if !check_nsequence_rbf(rbf, csv) =>
1✔
1306
            {
1✔
1307
                return Err(CreateTxError::RbfSequenceCsv { rbf, csv })
1✔
1308
            }
1309

1310
            // RBF enabled with the default value with CSV also enabled. CSV takes precedence
1311
            (Some(tx_builder::RbfValue::Default), Some(csv)) => csv,
1✔
1312
            // Valid RBF, either default or with a specific value. We ignore the `CSV` value
1313
            // because we've already checked it before
1314
            (Some(rbf), _) => rbf.get_value(),
20✔
1315
        };
1316

1317
        let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
143✔
1318
            //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
1319
            FeePolicy::FeeAmount(fee) => {
9✔
1320
                if let Some(previous_fee) = params.bumping_fee {
9✔
1321
                    if fee < previous_fee.absolute {
6✔
1322
                        return Err(CreateTxError::FeeTooLow {
2✔
1323
                            required: Amount::from_sat(previous_fee.absolute),
2✔
1324
                        });
2✔
1325
                    }
4✔
1326
                }
3✔
1327
                (FeeRate::ZERO, fee)
7✔
1328
            }
1329
            FeePolicy::FeeRate(rate) => {
134✔
1330
                if let Some(previous_fee) = params.bumping_fee {
134✔
1331
                    let required_feerate = FeeRate::from_sat_per_kwu(
11✔
1332
                        previous_fee.rate.to_sat_per_kwu()
11✔
1333
                            + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
11✔
1334
                    );
11✔
1335
                    if rate < required_feerate {
11✔
1336
                        return Err(CreateTxError::FeeRateTooLow {
1✔
1337
                            required: required_feerate,
1✔
1338
                        });
1✔
1339
                    }
10✔
1340
                }
123✔
1341
                (rate, 0)
133✔
1342
            }
1343
        };
1344

1345
        let mut tx = Transaction {
140✔
1346
            version: transaction::Version::non_standard(version),
140✔
1347
            lock_time,
140✔
1348
            input: vec![],
140✔
1349
            output: vec![],
140✔
1350
        };
140✔
1351

140✔
1352
        if params.manually_selected_only && params.utxos.is_empty() {
140✔
1353
            return Err(CreateTxError::NoUtxosSelected);
1✔
1354
        }
139✔
1355

139✔
1356
        let mut outgoing = Amount::ZERO;
139✔
1357
        let mut received = Amount::ZERO;
139✔
1358

139✔
1359
        let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
139✔
1360

1361
        for (index, (script_pubkey, value)) in recipients.enumerate() {
139✔
1362
            if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() {
89✔
1363
                return Err(CreateTxError::OutputBelowDustLimit(index));
1✔
1364
            }
88✔
1365

88✔
1366
            if self.is_mine(script_pubkey) {
88✔
1367
                received += Amount::from_sat(value);
42✔
1368
            }
50✔
1369

1370
            let new_out = TxOut {
88✔
1371
                script_pubkey: script_pubkey.clone(),
88✔
1372
                value: Amount::from_sat(value),
88✔
1373
            };
88✔
1374

88✔
1375
            tx.output.push(new_out);
88✔
1376

88✔
1377
            outgoing += Amount::from_sat(value);
88✔
1378
        }
1379

1380
        fee_amount += (fee_rate * tx.weight()).to_sat();
138✔
1381

138✔
1382
        let (required_utxos, optional_utxos) =
138✔
1383
            self.preselect_utxos(&params, Some(current_height.to_consensus_u32()));
138✔
1384

1385
        // get drain script
1386
        let drain_script = match params.drain_to {
138✔
1387
            Some(ref drain_recipient) => drain_recipient.clone(),
52✔
1388
            None => {
1389
                let change_keychain = KeychainKind::Internal;
86✔
1390
                let ((index, spk), index_changeset) = self
86✔
1391
                    .indexed_graph
86✔
1392
                    .index
86✔
1393
                    .next_unused_spk(&change_keychain)
86✔
1394
                    .expect("keychain must exist");
86✔
1395
                self.indexed_graph.index.mark_used(change_keychain, index);
86✔
1396
                self.stage.merge(index_changeset.into());
86✔
1397
                spk
86✔
1398
            }
1399
        };
1400

1401
        let (required_utxos, optional_utxos) =
138✔
1402
            coin_selection::filter_duplicates(required_utxos, optional_utxos);
138✔
1403

1404
        let coin_selection = match coin_selection.coin_select(
138✔
1405
            required_utxos.clone(),
138✔
1406
            optional_utxos.clone(),
138✔
1407
            fee_rate,
138✔
1408
            outgoing.to_sat() + fee_amount,
138✔
1409
            &drain_script,
138✔
1410
        ) {
138✔
1411
            Ok(res) => res,
62✔
1412
            Err(e) => match e {
76✔
1413
                coin_selection::Error::InsufficientFunds { .. } => {
1414
                    return Err(CreateTxError::CoinSelection(e));
7✔
1415
                }
1416
                coin_selection::Error::BnBNoExactMatch
1417
                | coin_selection::Error::BnBTotalTriesExceeded => {
1418
                    coin_selection::single_random_draw(
69✔
1419
                        required_utxos,
69✔
1420
                        optional_utxos,
69✔
1421
                        outgoing.to_sat() + fee_amount,
69✔
1422
                        &drain_script,
69✔
1423
                        fee_rate,
69✔
1424
                        rng,
69✔
1425
                    )
69✔
1426
                }
1427
            },
1428
        };
1429
        fee_amount += coin_selection.fee_amount;
131✔
1430
        let excess = &coin_selection.excess;
131✔
1431

131✔
1432
        tx.input = coin_selection
131✔
1433
            .selected
131✔
1434
            .iter()
131✔
1435
            .map(|u| bitcoin::TxIn {
147✔
1436
                previous_output: u.outpoint(),
147✔
1437
                script_sig: ScriptBuf::default(),
147✔
1438
                sequence: u.sequence().unwrap_or(n_sequence),
147✔
1439
                witness: Witness::new(),
147✔
1440
            })
147✔
1441
            .collect();
131✔
1442

131✔
1443
        if tx.output.is_empty() {
131✔
1444
            // Uh oh, our transaction has no outputs.
1445
            // We allow this when:
1446
            // - We have a drain_to address and the utxos we must spend (this happens,
1447
            // for example, when we RBF)
1448
            // - We have a drain_to address and drain_wallet set
1449
            // Otherwise, we don't know who we should send the funds to, and how much
1450
            // we should send!
1451
            if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
50✔
1452
                if let NoChange {
1453
                    dust_threshold,
1✔
1454
                    remaining_amount,
1✔
1455
                    change_fee,
1✔
1456
                } = excess
48✔
1457
                {
1458
                    return Err(CreateTxError::CoinSelection(Error::InsufficientFunds {
1✔
1459
                        needed: *dust_threshold,
1✔
1460
                        available: remaining_amount.saturating_sub(*change_fee),
1✔
1461
                    }));
1✔
1462
                }
47✔
1463
            } else {
1464
                return Err(CreateTxError::NoRecipients);
2✔
1465
            }
1466
        }
81✔
1467

1468
        match excess {
128✔
1469
            NoChange {
1470
                remaining_amount, ..
7✔
1471
            } => fee_amount += remaining_amount,
7✔
1472
            Change { amount, fee } => {
121✔
1473
                if self.is_mine(&drain_script) {
121✔
1474
                    received += Amount::from_sat(*amount);
111✔
1475
                }
111✔
1476
                fee_amount += fee;
121✔
1477

121✔
1478
                // create drain output
121✔
1479
                let drain_output = TxOut {
121✔
1480
                    value: Amount::from_sat(*amount),
121✔
1481
                    script_pubkey: drain_script,
121✔
1482
                };
121✔
1483

121✔
1484
                // TODO: We should pay attention when adding a new output: this might increase
121✔
1485
                // the length of the "number of vouts" parameter by 2 bytes, potentially making
121✔
1486
                // our feerate too low
121✔
1487
                tx.output.push(drain_output);
121✔
1488
            }
1489
        };
1490

1491
        // sort input/outputs according to the chosen algorithm
1492
        params.ordering.sort_tx_with_aux_rand(&mut tx, rng);
128✔
1493

1494
        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
128✔
1495
        Ok(psbt)
126✔
1496
    }
149✔
1497

1498
    /// Bump the fee of a transaction previously created with this wallet.
1499
    ///
1500
    /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
1501
    /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
1502
    /// pre-populated with the inputs and outputs of the original transaction.
1503
    ///
1504
    /// ## Example
1505
    ///
1506
    /// ```no_run
1507
    /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
1508
    /// # use std::str::FromStr;
1509
    /// # use bitcoin::*;
1510
    /// # use bdk_wallet::*;
1511
    /// # use bdk_wallet::wallet::ChangeSet;
1512
    /// # use bdk_wallet::wallet::error::CreateTxError;
1513
    /// # use anyhow::Error;
1514
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1515
    /// # let mut wallet = doctest_wallet!();
1516
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1517
    /// let mut psbt = {
1518
    ///     let mut builder = wallet.build_tx();
1519
    ///     builder
1520
    ///         .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
1521
    ///         .enable_rbf();
1522
    ///     builder.finish()?
1523
    /// };
1524
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1525
    /// let tx = psbt.clone().extract_tx().expect("tx");
1526
    /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
1527
    /// let mut psbt =  {
1528
    ///     let mut builder = wallet.build_fee_bump(tx.compute_txid())?;
1529
    ///     builder
1530
    ///         .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
1531
    ///     builder.finish()?
1532
    /// };
1533
    ///
1534
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1535
    /// let fee_bumped_tx = psbt.extract_tx();
1536
    /// // broadcast fee_bumped_tx to replace original
1537
    /// # Ok::<(), anyhow::Error>(())
1538
    /// ```
1539
    // TODO: support for merging multiple transactions while bumping the fees
1540
    pub fn build_fee_bump(
152✔
1541
        &mut self,
152✔
1542
        txid: Txid,
152✔
1543
    ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
152✔
1544
        let graph = self.indexed_graph.graph();
152✔
1545
        let txout_index = &self.indexed_graph.index;
152✔
1546
        let chain_tip = self.chain.tip().block_id();
152✔
1547

1548
        let mut tx = graph
152✔
1549
            .get_tx(txid)
152✔
1550
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
152✔
1551
            .as_ref()
152✔
1552
            .clone();
152✔
1553

1554
        let pos = graph
152✔
1555
            .get_chain_position(&self.chain, chain_tip, txid)
152✔
1556
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?;
152✔
1557
        if let ChainPosition::Confirmed(_) = pos {
152✔
1558
            return Err(BuildFeeBumpError::TransactionConfirmed(txid));
8✔
1559
        }
144✔
1560

144✔
1561
        if !tx
144✔
1562
            .input
144✔
1563
            .iter()
144✔
1564
            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
144✔
1565
        {
1566
            return Err(BuildFeeBumpError::IrreplaceableTransaction(
8✔
1567
                tx.compute_txid(),
8✔
1568
            ));
8✔
1569
        }
136✔
1570

1571
        let fee = self
136✔
1572
            .calculate_fee(&tx)
136✔
1573
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1574
        let fee_rate = self
136✔
1575
            .calculate_fee_rate(&tx)
136✔
1576
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1577

1578
        // remove the inputs from the tx and process them
1579
        let original_txin = tx.input.drain(..).collect::<Vec<_>>();
136✔
1580
        let original_utxos = original_txin
136✔
1581
            .iter()
136✔
1582
            .map(|txin| -> Result<_, BuildFeeBumpError> {
144✔
1583
                let prev_tx = graph
144✔
1584
                    .get_tx(txin.previous_output.txid)
144✔
1585
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
144✔
1586
                let txout = &prev_tx.output[txin.previous_output.vout as usize];
144✔
1587

1588
                let confirmation_time: ConfirmationTime = graph
144✔
1589
                    .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
144✔
1590
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?
144✔
1591
                    .cloned()
144✔
1592
                    .into();
144✔
1593

1594
                let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
144✔
1595
                    Some(&(keychain, derivation_index)) => {
144✔
1596
                        let satisfaction_weight = self
144✔
1597
                            .public_descriptor(keychain)
144✔
1598
                            .max_weight_to_satisfy()
144✔
1599
                            .unwrap();
144✔
1600
                        WeightedUtxo {
144✔
1601
                            utxo: Utxo::Local(LocalOutput {
144✔
1602
                                outpoint: txin.previous_output,
144✔
1603
                                txout: txout.clone(),
144✔
1604
                                keychain,
144✔
1605
                                is_spent: true,
144✔
1606
                                derivation_index,
144✔
1607
                                confirmation_time,
144✔
1608
                            }),
144✔
1609
                            satisfaction_weight,
144✔
1610
                        }
144✔
1611
                    }
1612
                    None => {
UNCOV
1613
                        let satisfaction_weight = Weight::from_wu_usize(
×
UNCOV
1614
                            serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len(),
×
UNCOV
1615
                        );
×
1616
                        WeightedUtxo {
×
1617
                            utxo: Utxo::Foreign {
×
1618
                                outpoint: txin.previous_output,
×
1619
                                sequence: Some(txin.sequence),
×
1620
                                psbt_input: Box::new(psbt::Input {
×
1621
                                    witness_utxo: Some(txout.clone()),
×
1622
                                    non_witness_utxo: Some(prev_tx.as_ref().clone()),
×
1623
                                    ..Default::default()
×
1624
                                }),
×
1625
                            },
×
1626
                            satisfaction_weight,
×
1627
                        }
×
1628
                    }
1629
                };
1630

1631
                Ok(weighted_utxo)
144✔
1632
            })
144✔
1633
            .collect::<Result<Vec<_>, _>>()?;
136✔
1634

1635
        if tx.output.len() > 1 {
136✔
1636
            let mut change_index = None;
80✔
1637
            for (index, txout) in tx.output.iter().enumerate() {
160✔
1638
                let change_keychain = KeychainKind::Internal;
160✔
1639
                match txout_index.index_of_spk(&txout.script_pubkey) {
160✔
1640
                    Some((keychain, _)) if *keychain == change_keychain => {
104✔
1641
                        change_index = Some(index)
80✔
1642
                    }
1643
                    _ => {}
80✔
1644
                }
1645
            }
1646

1647
            if let Some(change_index) = change_index {
80✔
1648
                tx.output.remove(change_index);
80✔
1649
            }
80✔
1650
        }
56✔
1651

1652
        let params = TxParams {
136✔
1653
            // TODO: figure out what rbf option should be?
136✔
1654
            version: Some(tx_builder::Version(tx.version.0)),
136✔
1655
            recipients: tx
136✔
1656
                .output
136✔
1657
                .into_iter()
136✔
1658
                .map(|txout| (txout.script_pubkey, txout.value.to_sat()))
136✔
1659
                .collect(),
136✔
1660
            utxos: original_utxos,
136✔
1661
            bumping_fee: Some(tx_builder::PreviousFee {
136✔
1662
                absolute: fee.to_sat(),
136✔
1663
                rate: fee_rate,
136✔
1664
            }),
136✔
1665
            ..Default::default()
136✔
1666
        };
136✔
1667

136✔
1668
        Ok(TxBuilder {
136✔
1669
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
136✔
1670
            params,
136✔
1671
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
136✔
1672
        })
136✔
1673
    }
152✔
1674

1675
    /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
1676
    /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
1677
    ///
1678
    /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
1679
    /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
1680
    /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
1681
    /// in this library will.
1682
    ///
1683
    /// ## Example
1684
    ///
1685
    /// ```
1686
    /// # use std::str::FromStr;
1687
    /// # use bitcoin::*;
1688
    /// # use bdk_wallet::*;
1689
    /// # use bdk_wallet::wallet::ChangeSet;
1690
    /// # use bdk_wallet::wallet::error::CreateTxError;
1691
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1692
    /// # let mut wallet = doctest_wallet!();
1693
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1694
    /// let mut psbt = {
1695
    ///     let mut builder = wallet.build_tx();
1696
    ///     builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1697
    ///     builder.finish()?
1698
    /// };
1699
    /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
1700
    /// assert!(finalized, "we should have signed all the inputs");
1701
    /// # Ok::<(),anyhow::Error>(())
1702
    pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, SignerError> {
360✔
1703
        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
360✔
1704
        // to derive our keys
360✔
1705
        self.update_psbt_with_descriptor(psbt)
360✔
1706
            .map_err(SignerError::MiniscriptPsbt)?;
360✔
1707

1708
        // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
1709
        // has the `non_witness_utxo`
1710
        if !sign_options.trust_witness_utxo
360✔
1711
            && psbt
312✔
1712
                .inputs
312✔
1713
                .iter()
312✔
1714
                .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
320✔
1715
                .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
312✔
1716
                .any(|i| i.non_witness_utxo.is_none())
312✔
1717
        {
UNCOV
1718
            return Err(SignerError::MissingNonWitnessUtxo);
×
1719
        }
360✔
1720

360✔
1721
        // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
360✔
1722
        // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
360✔
1723
        if !sign_options.allow_all_sighashes
360✔
1724
            && !psbt.inputs.iter().all(|i| {
368✔
1725
                i.sighash_type.is_none()
368✔
1726
                    || i.sighash_type == Some(EcdsaSighashType::All.into())
24✔
1727
                    || i.sighash_type == Some(TapSighashType::All.into())
16✔
1728
                    || i.sighash_type == Some(TapSighashType::Default.into())
16✔
1729
            })
368✔
1730
        {
1731
            return Err(SignerError::NonStandardSighash);
16✔
1732
        }
344✔
1733

1734
        for signer in self
712✔
1735
            .signers
344✔
1736
            .signers()
344✔
1737
            .iter()
344✔
1738
            .chain(self.change_signers.signers().iter())
344✔
1739
        {
1740
            signer.sign_transaction(psbt, &sign_options, &self.secp)?;
712✔
1741
        }
1742

1743
        // attempt to finalize
1744
        if sign_options.try_finalize {
312✔
1745
            self.finalize_psbt(psbt, sign_options)
280✔
1746
        } else {
1747
            Ok(false)
32✔
1748
        }
1749
    }
360✔
1750

1751
    /// Return the spending policies for the wallet's descriptor
1752
    pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, DescriptorError> {
24✔
1753
        let signers = match keychain {
24✔
1754
            KeychainKind::External => &self.signers,
24✔
UNCOV
1755
            KeychainKind::Internal => &self.change_signers,
×
1756
        };
1757

1758
        self.public_descriptor(keychain).extract_policy(
24✔
1759
            signers,
24✔
1760
            BuildSatisfaction::None,
24✔
1761
            &self.secp,
24✔
1762
        )
24✔
1763
    }
24✔
1764

1765
    /// Returns the descriptor used to create addresses for a particular `keychain`.
1766
    /// It's the "public" version of the wallet's descriptor, meaning a new descriptor that has
1767
    /// the same structure but with the all secret keys replaced by their corresponding public key.
1768
    ///
1769
    /// This can be used to build a watch-only version of a wallet.
1770
    pub fn public_descriptor(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
6,340✔
1771
        self.indexed_graph
6,340✔
1772
            .index
6,340✔
1773
            .get_descriptor(&keychain)
6,340✔
1774
            .expect("keychain must exist")
6,340✔
1775
    }
6,340✔
1776

1777
    /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
1778
    /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
1779
    /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer),
1780
    /// and [BIP371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki)
1781
    /// for further information.
1782
    ///
1783
    /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
1784
    ///
1785
    /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
1786
    pub fn finalize_psbt(
280✔
1787
        &self,
280✔
1788
        psbt: &mut Psbt,
280✔
1789
        sign_options: SignOptions,
280✔
1790
    ) -> Result<bool, SignerError> {
280✔
1791
        let chain_tip = self.chain.tip().block_id();
280✔
1792

280✔
1793
        let tx = &psbt.unsigned_tx;
280✔
1794
        let mut finished = true;
280✔
1795

1796
        for (n, input) in tx.input.iter().enumerate() {
320✔
1797
            let psbt_input = &psbt
320✔
1798
                .inputs
320✔
1799
                .get(n)
320✔
1800
                .ok_or(SignerError::InputIndexOutOfRange)?;
320✔
1801
            if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
312✔
1802
                continue;
16✔
1803
            }
296✔
1804
            let confirmation_height = self
296✔
1805
                .indexed_graph
296✔
1806
                .graph()
296✔
1807
                .get_chain_position(&self.chain, chain_tip, input.previous_output.txid)
296✔
1808
                .map(|chain_position| match chain_position {
296✔
1809
                    ChainPosition::Confirmed(a) => a.block_id.height,
272✔
UNCOV
1810
                    ChainPosition::Unconfirmed(_) => u32::MAX,
×
1811
                });
296✔
1812
            let current_height = sign_options
296✔
1813
                .assume_height
296✔
1814
                .unwrap_or_else(|| self.chain.tip().height());
296✔
1815

296✔
1816
            // - Try to derive the descriptor by looking at the txout. If it's in our database, we
296✔
1817
            //   know exactly which `keychain` to use, and which derivation index it is
296✔
1818
            // - If that fails, try to derive it by looking at the psbt input: the complete logic
296✔
1819
            //   is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
296✔
1820
            //   `redeem_script` and `witness_script` to determine the right derivation
296✔
1821
            // - If that also fails, it will try it on the internal descriptor, if present
296✔
1822
            let desc = psbt
296✔
1823
                .get_utxo_for(n)
296✔
1824
                .and_then(|txout| self.get_descriptor_for_txout(&txout))
296✔
1825
                .or_else(|| {
296✔
1826
                    self.indexed_graph.index.keychains().find_map(|(_, desc)| {
32✔
1827
                        desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
32✔
1828
                    })
32✔
1829
                });
296✔
1830

296✔
1831
            match desc {
296✔
1832
                Some(desc) => {
280✔
1833
                    let mut tmp_input = bitcoin::TxIn::default();
280✔
1834
                    match desc.satisfy(
280✔
1835
                        &mut tmp_input,
280✔
1836
                        (
280✔
1837
                            PsbtInputSatisfier::new(psbt, n),
280✔
1838
                            After::new(Some(current_height), false),
280✔
1839
                            Older::new(Some(current_height), confirmation_height, false),
280✔
1840
                        ),
280✔
1841
                    ) {
280✔
1842
                        Ok(_) => {
1843
                            // Set the UTXO fields, final script_sig and witness
1844
                            // and clear everything else.
1845
                            let original = mem::take(&mut psbt.inputs[n]);
272✔
1846
                            let psbt_input = &mut psbt.inputs[n];
272✔
1847
                            psbt_input.non_witness_utxo = original.non_witness_utxo;
272✔
1848
                            psbt_input.witness_utxo = original.witness_utxo;
272✔
1849
                            if !tmp_input.script_sig.is_empty() {
272✔
1850
                                psbt_input.final_script_sig = Some(tmp_input.script_sig);
16✔
1851
                            }
256✔
1852
                            if !tmp_input.witness.is_empty() {
272✔
1853
                                psbt_input.final_script_witness = Some(tmp_input.witness);
264✔
1854
                            }
264✔
1855
                        }
1856
                        Err(_) => finished = false,
8✔
1857
                    }
1858
                }
1859
                None => finished = false,
16✔
1860
            }
1861
        }
1862

1863
        // Clear derivation paths from outputs
1864
        if finished {
272✔
1865
            for output in &mut psbt.outputs {
584✔
1866
                output.bip32_derivation.clear();
336✔
1867
                output.tap_key_origins.clear();
336✔
1868
            }
336✔
1869
        }
24✔
1870

1871
        Ok(finished)
272✔
1872
    }
280✔
1873

1874
    /// Return the secp256k1 context used for all signing operations
1875
    pub fn secp_ctx(&self) -> &SecpCtx {
28✔
1876
        &self.secp
28✔
1877
    }
28✔
1878

1879
    /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
1880
    /// Otherwise, it will return the index of the highest address it has derived.
1881
    pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
40✔
1882
        self.indexed_graph.index.last_revealed_index(&keychain)
40✔
1883
    }
40✔
1884

1885
    /// The index of the next address that you would get if you were to ask the wallet for a new address
UNCOV
1886
    pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
×
UNCOV
1887
        self.indexed_graph
×
UNCOV
1888
            .index
×
1889
            .next_index(&keychain)
×
1890
            .expect("keychain must exist")
×
1891
            .0
×
1892
    }
×
1893

1894
    /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
1895
    ///
1896
    /// This frees up the change address used when creating the tx for use in future transactions.
1897
    // TODO: Make this free up reserved utxos when that's implemented
1898
    pub fn cancel_tx(&mut self, tx: &Transaction) {
16✔
1899
        let txout_index = &mut self.indexed_graph.index;
16✔
1900
        for txout in &tx.output {
48✔
1901
            if let Some((keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
32✔
1902
                // NOTE: unmark_used will **not** make something unused if it has actually been used
16✔
1903
                // by a tx in the tracker. It only removes the superficial marking.
16✔
1904
                txout_index.unmark_used(*keychain, *index);
16✔
1905
            }
16✔
1906
        }
1907
    }
16✔
1908

1909
    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
296✔
1910
        let &(keychain, child) = self
296✔
1911
            .indexed_graph
296✔
1912
            .index
296✔
1913
            .index_of_spk(&txout.script_pubkey)?;
296✔
1914
        let descriptor = self.public_descriptor(keychain);
280✔
1915
        descriptor.at_derivation_index(child).ok()
280✔
1916
    }
296✔
1917

1918
    fn get_available_utxos(&self) -> Vec<(LocalOutput, Weight)> {
1,160✔
1919
        self.list_unspent()
1,160✔
1920
            .map(|utxo| {
1,272✔
1921
                let keychain = utxo.keychain;
1,272✔
1922
                (utxo, {
1,272✔
1923
                    self.public_descriptor(keychain)
1,272✔
1924
                        .max_weight_to_satisfy()
1,272✔
1925
                        .unwrap()
1,272✔
1926
                })
1,272✔
1927
            })
1,272✔
1928
            .collect()
1,160✔
1929
    }
1,160✔
1930

1931
    /// Given the options returns the list of utxos that must be used to form the
1932
    /// transaction and any further that may be used if needed.
1933
    fn preselect_utxos(
1,160✔
1934
        &self,
1,160✔
1935
        params: &TxParams,
1,160✔
1936
        current_height: Option<u32>,
1,160✔
1937
    ) -> (Vec<WeightedUtxo>, Vec<WeightedUtxo>) {
1,160✔
1938
        let TxParams {
1,160✔
1939
            change_policy,
1,160✔
1940
            unspendable,
1,160✔
1941
            utxos,
1,160✔
1942
            drain_wallet,
1,160✔
1943
            manually_selected_only,
1,160✔
1944
            bumping_fee,
1,160✔
1945
            ..
1,160✔
1946
        } = params;
1,160✔
1947

1,160✔
1948
        let manually_selected = utxos.clone();
1,160✔
1949
        // we mandate confirmed transactions if we're bumping the fee
1,160✔
1950
        let must_only_use_confirmed_tx = bumping_fee.is_some();
1,160✔
1951
        let must_use_all_available = *drain_wallet;
1,160✔
1952

1,160✔
1953
        let chain_tip = self.chain.tip().block_id();
1,160✔
1954
        //    must_spend <- manually selected utxos
1,160✔
1955
        //    may_spend  <- all other available utxos
1,160✔
1956
        let mut may_spend = self.get_available_utxos();
1,160✔
1957

1,160✔
1958
        may_spend.retain(|may_spend| {
1,272✔
1959
            !manually_selected
1,272✔
1960
                .iter()
1,272✔
1961
                .any(|manually_selected| manually_selected.utxo.outpoint() == may_spend.0.outpoint)
1,272✔
1962
        });
1,272✔
1963
        let mut must_spend = manually_selected;
1,160✔
1964

1,160✔
1965
        // NOTE: we are intentionally ignoring `unspendable` here. i.e manual
1,160✔
1966
        // selection overrides unspendable.
1,160✔
1967
        if *manually_selected_only {
1,160✔
1968
            return (must_spend, vec![]);
40✔
1969
        }
1,120✔
1970

1,120✔
1971
        let satisfies_confirmed = may_spend
1,120✔
1972
            .iter()
1,120✔
1973
            .map(|u| -> bool {
1,160✔
1974
                let txid = u.0.outpoint.txid;
1,160✔
1975
                let tx = match self.indexed_graph.graph().get_tx(txid) {
1,160✔
1976
                    Some(tx) => tx,
1,160✔
UNCOV
1977
                    None => return false,
×
1978
                };
1979
                let confirmation_time: ConfirmationTime = match self
1,160✔
1980
                    .indexed_graph
1,160✔
1981
                    .graph()
1,160✔
1982
                    .get_chain_position(&self.chain, chain_tip, txid)
1,160✔
1983
                {
1984
                    Some(chain_position) => chain_position.cloned().into(),
1,160✔
UNCOV
1985
                    None => return false,
×
1986
                };
1987

1988
                // Whether the UTXO is mature and, if needed, confirmed
1989
                let mut spendable = true;
1,160✔
1990
                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
1,160✔
1991
                    return false;
64✔
1992
                }
1,096✔
1993
                if tx.is_coinbase() {
1,096✔
1994
                    debug_assert!(
24✔
1995
                        confirmation_time.is_confirmed(),
24✔
UNCOV
1996
                        "coinbase must always be confirmed"
×
1997
                    );
1998
                    if let Some(current_height) = current_height {
24✔
1999
                        match confirmation_time {
24✔
2000
                            ConfirmationTime::Confirmed { height, .. } => {
24✔
2001
                                // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
24✔
2002
                                spendable &=
24✔
2003
                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
24✔
2004
                            }
24✔
UNCOV
2005
                            ConfirmationTime::Unconfirmed { .. } => spendable = false,
×
2006
                        }
UNCOV
2007
                    }
×
2008
                }
1,072✔
2009
                spendable
1,096✔
2010
            })
1,160✔
2011
            .collect::<Vec<_>>();
1,120✔
2012

1,120✔
2013
        let mut i = 0;
1,120✔
2014
        may_spend.retain(|u| {
1,160✔
2015
            let retain = change_policy.is_satisfied_by(&u.0)
1,160✔
2016
                && !unspendable.contains(&u.0.outpoint)
1,152✔
2017
                && satisfies_confirmed[i];
1,152✔
2018
            i += 1;
1,160✔
2019
            retain
1,160✔
2020
        });
1,160✔
2021

1,120✔
2022
        let mut may_spend = may_spend
1,120✔
2023
            .into_iter()
1,120✔
2024
            .map(|(local_utxo, satisfaction_weight)| WeightedUtxo {
1,120✔
2025
                satisfaction_weight,
1,072✔
2026
                utxo: Utxo::Local(local_utxo),
1,072✔
2027
            })
1,120✔
2028
            .collect();
1,120✔
2029

1,120✔
2030
        if must_use_all_available {
1,120✔
2031
            must_spend.append(&mut may_spend);
392✔
2032
        }
728✔
2033

2034
        (must_spend, may_spend)
1,120✔
2035
    }
1,160✔
2036

2037
    fn complete_transaction(
1,080✔
2038
        &self,
1,080✔
2039
        tx: Transaction,
1,080✔
2040
        selected: Vec<Utxo>,
1,080✔
2041
        params: TxParams,
1,080✔
2042
    ) -> Result<Psbt, CreateTxError> {
1,080✔
2043
        let mut psbt = Psbt::from_unsigned_tx(tx)?;
1,080✔
2044

2045
        if params.add_global_xpubs {
1,080✔
2046
            let all_xpubs = self
24✔
2047
                .keychains()
24✔
2048
                .flat_map(|(_, desc)| desc.get_extended_keys())
48✔
2049
                .collect::<Vec<_>>();
24✔
2050

2051
            for xpub in all_xpubs {
56✔
2052
                let origin = match xpub.origin {
32✔
2053
                    Some(origin) => origin,
24✔
2054
                    None if xpub.xkey.depth == 0 => {
16✔
2055
                        (xpub.root_fingerprint(&self.secp), vec![].into())
8✔
2056
                    }
2057
                    _ => return Err(CreateTxError::MissingKeyOrigin(xpub.xkey.to_string())),
8✔
2058
                };
2059

2060
                psbt.xpub.insert(xpub.xkey, origin);
32✔
2061
            }
2062
        }
1,056✔
2063

2064
        let mut lookup_output = selected
1,072✔
2065
            .into_iter()
1,072✔
2066
            .map(|utxo| (utxo.outpoint(), utxo))
1,200✔
2067
            .collect::<HashMap<_, _>>();
1,072✔
2068

2069
        // add metadata for the inputs
2070
        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
1,200✔
2071
            let utxo = match lookup_output.remove(&input.previous_output) {
1,200✔
2072
                Some(utxo) => utxo,
1,200✔
UNCOV
2073
                None => continue,
×
2074
            };
2075

2076
            match utxo {
1,200✔
2077
                Utxo::Local(utxo) => {
1,152✔
2078
                    *psbt_input =
1,152✔
2079
                        match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
1,152✔
2080
                            Ok(psbt_input) => psbt_input,
1,152✔
UNCOV
2081
                            Err(e) => match e {
×
UNCOV
2082
                                CreateTxError::UnknownUtxo => psbt::Input {
×
UNCOV
2083
                                    sighash_type: params.sighash,
×
2084
                                    ..psbt::Input::default()
×
2085
                                },
×
2086
                                _ => return Err(e),
×
2087
                            },
2088
                        }
2089
                }
2090
                Utxo::Foreign {
2091
                    outpoint,
48✔
2092
                    psbt_input: foreign_psbt_input,
48✔
2093
                    ..
48✔
2094
                } => {
48✔
2095
                    let is_taproot = foreign_psbt_input
48✔
2096
                        .witness_utxo
48✔
2097
                        .as_ref()
48✔
2098
                        .map(|txout| txout.script_pubkey.is_p2tr())
48✔
2099
                        .unwrap_or(false);
48✔
2100
                    if !is_taproot
48✔
2101
                        && !params.only_witness_utxo
40✔
2102
                        && foreign_psbt_input.non_witness_utxo.is_none()
16✔
2103
                    {
2104
                        return Err(CreateTxError::MissingNonWitnessUtxo(outpoint));
8✔
2105
                    }
40✔
2106
                    *psbt_input = *foreign_psbt_input;
40✔
2107
                }
2108
            }
2109
        }
2110

2111
        self.update_psbt_with_descriptor(&mut psbt)?;
1,064✔
2112

2113
        Ok(psbt)
1,064✔
2114
    }
1,080✔
2115

2116
    /// get the corresponding PSBT Input for a LocalUtxo
2117
    pub fn get_psbt_input(
1,168✔
2118
        &self,
1,168✔
2119
        utxo: LocalOutput,
1,168✔
2120
        sighash_type: Option<psbt::PsbtSighashType>,
1,168✔
2121
        only_witness_utxo: bool,
1,168✔
2122
    ) -> Result<psbt::Input, CreateTxError> {
1,168✔
2123
        // Try to find the prev_script in our db to figure out if this is internal or external,
2124
        // and the derivation index
2125
        let &(keychain, child) = self
1,168✔
2126
            .indexed_graph
1,168✔
2127
            .index
1,168✔
2128
            .index_of_spk(&utxo.txout.script_pubkey)
1,168✔
2129
            .ok_or(CreateTxError::UnknownUtxo)?;
1,168✔
2130

2131
        let mut psbt_input = psbt::Input {
1,168✔
2132
            sighash_type,
1,168✔
2133
            ..psbt::Input::default()
1,168✔
2134
        };
1,168✔
2135

1,168✔
2136
        let desc = self.public_descriptor(keychain);
1,168✔
2137
        let derived_descriptor = desc
1,168✔
2138
            .at_derivation_index(child)
1,168✔
2139
            .expect("child can't be hardened");
1,168✔
2140

1,168✔
2141
        psbt_input
1,168✔
2142
            .update_with_descriptor_unchecked(&derived_descriptor)
1,168✔
2143
            .map_err(MiniscriptPsbtError::Conversion)?;
1,168✔
2144

2145
        let prev_output = utxo.outpoint;
1,168✔
2146
        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
1,168✔
2147
            if desc.is_witness() || desc.is_taproot() {
1,168✔
2148
                psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
1,136✔
2149
            }
1,136✔
2150
            if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
1,168✔
2151
                psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
896✔
2152
            }
896✔
UNCOV
2153
        }
×
2154
        Ok(psbt_input)
1,168✔
2155
    }
1,168✔
2156

2157
    fn update_psbt_with_descriptor(&self, psbt: &mut Psbt) -> Result<(), MiniscriptPsbtError> {
1,424✔
2158
        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
1,424✔
2159
        // the input utxos and outputs
1,424✔
2160
        let utxos = (0..psbt.inputs.len())
1,424✔
2161
            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
1,592✔
2162
            .chain(
1,424✔
2163
                psbt.unsigned_tx
1,424✔
2164
                    .output
1,424✔
2165
                    .iter()
1,424✔
2166
                    .enumerate()
1,424✔
2167
                    .map(|(i, out)| (false, i, out.clone())),
2,216✔
2168
            )
1,424✔
2169
            .collect::<Vec<_>>();
1,424✔
2170

2171
        // Try to figure out the keychain and derivation for every input and output
2172
        for (is_input, index, out) in utxos.into_iter() {
3,768✔
2173
            if let Some(&(keychain, child)) =
3,136✔
2174
                self.indexed_graph.index.index_of_spk(&out.script_pubkey)
3,768✔
2175
            {
2176
                let desc = self.public_descriptor(keychain);
3,136✔
2177
                let desc = desc
3,136✔
2178
                    .at_derivation_index(child)
3,136✔
2179
                    .expect("child can't be hardened");
3,136✔
2180

3,136✔
2181
                if is_input {
3,136✔
2182
                    psbt.update_input_with_descriptor(index, &desc)
1,480✔
2183
                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
1,480✔
2184
                } else {
2185
                    psbt.update_output_with_descriptor(index, &desc)
1,656✔
2186
                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
1,656✔
2187
                }
2188
            }
632✔
2189
        }
2190

2191
        Ok(())
1,424✔
2192
    }
1,424✔
2193

2194
    /// Return the checksum of the public descriptor associated to `keychain`
2195
    ///
2196
    /// Internally calls [`Self::public_descriptor`] to fetch the right descriptor
2197
    pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
8✔
2198
        self.public_descriptor(keychain)
8✔
2199
            .to_string()
8✔
2200
            .split_once('#')
8✔
2201
            .unwrap()
8✔
2202
            .1
8✔
2203
            .to_string()
8✔
2204
    }
8✔
2205

2206
    /// Applies an update to the wallet and stages the changes (but does not persist them).
2207
    ///
2208
    /// Usually you create an `update` by interacting with some blockchain data source and inserting
2209
    /// transactions related to your wallet into it.
2210
    ///
2211
    /// After applying updates you should persist the staged wallet changes. For an example of how
2212
    /// to persist staged wallet changes see [`Wallet::reveal_next_address`]. `
2213
    ///
2214
    /// [`commit`]: Self::commit
2215
    pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
301✔
2216
        let update = update.into();
301✔
2217
        let mut changeset = match update.chain {
301✔
UNCOV
2218
            Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
×
2219
            None => ChangeSet::default(),
301✔
2220
        };
2221

2222
        let index_changeset = self
301✔
2223
            .indexed_graph
301✔
2224
            .index
301✔
2225
            .reveal_to_target_multi(&update.last_active_indices);
301✔
2226
        changeset.merge(index_changeset.into());
301✔
2227
        changeset.merge(self.indexed_graph.apply_update(update.graph).into());
301✔
2228
        self.stage.merge(changeset);
301✔
2229
        Ok(())
301✔
2230
    }
301✔
2231

2232
    /// Get a reference of the staged [`ChangeSet`] that are yet to be committed (if any).
UNCOV
2233
    pub fn staged(&self) -> Option<&ChangeSet> {
×
UNCOV
2234
        if self.stage.is_empty() {
×
UNCOV
2235
            None
×
2236
        } else {
2237
            Some(&self.stage)
×
2238
        }
UNCOV
2239
    }
×
2240

2241
    /// Take the staged [`ChangeSet`] to be persisted now (if any).
2242
    pub fn take_staged(&mut self) -> Option<ChangeSet> {
32✔
2243
        self.stage.take()
32✔
2244
    }
32✔
2245

2246
    /// Get a reference to the inner [`TxGraph`].
UNCOV
2247
    pub fn tx_graph(&self) -> &TxGraph<ConfirmationBlockTime> {
×
UNCOV
2248
        self.indexed_graph.graph()
×
UNCOV
2249
    }
×
2250

2251
    /// Iterate over transactions in the wallet that are unseen and unanchored likely
2252
    /// because they haven't been broadcast.
UNCOV
2253
    pub fn unbroadcast_transactions(
×
UNCOV
2254
        &self,
×
UNCOV
2255
    ) -> impl Iterator<Item = TxNode<'_, Arc<Transaction>, ConfirmationBlockTime>> {
×
2256
        self.tx_graph().txs_with_no_anchor_or_last_seen()
×
2257
    }
×
2258

2259
    /// Get a reference to the inner [`KeychainTxOutIndex`].
2260
    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
48✔
2261
        &self.indexed_graph.index
48✔
2262
    }
48✔
2263

2264
    /// Get a reference to the inner [`LocalChain`].
2265
    pub fn local_chain(&self) -> &LocalChain {
2,184✔
2266
        &self.chain
2,184✔
2267
    }
2,184✔
2268

2269
    /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
2270
    /// `prev_blockhash` of the block's header.
2271
    ///
2272
    /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
2273
    /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
2274
    ///
2275
    /// [`apply_block_connected_to`]: Self::apply_block_connected_to
UNCOV
2276
    pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
×
UNCOV
2277
        let connected_to = match height.checked_sub(1) {
×
UNCOV
2278
            Some(prev_height) => BlockId {
×
2279
                height: prev_height,
×
2280
                hash: block.header.prev_blockhash,
×
2281
            },
×
2282
            None => BlockId {
×
2283
                height,
×
2284
                hash: block.block_hash(),
×
2285
            },
×
2286
        };
2287
        self.apply_block_connected_to(block, height, connected_to)
×
2288
            .map_err(|err| match err {
×
2289
                ApplyHeaderError::InconsistentBlocks => {
2290
                    unreachable!("connected_to is derived from the block so must be consistent")
×
2291
                }
UNCOV
2292
                ApplyHeaderError::CannotConnect(err) => err,
×
2293
            })
×
UNCOV
2294
    }
×
2295

2296
    /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
2297
    /// block to the internal chain.
2298
    ///
2299
    /// The `connected_to` parameter informs the wallet how this block connects to the internal
2300
    /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
2301
    /// internal [`TxGraph`].
2302
    ///
2303
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2304
    /// if you need the inserted block data to be reloaded after closing the wallet.
2305
    /// See [`Wallet::reveal_next_address`].
UNCOV
2306
    pub fn apply_block_connected_to(
×
UNCOV
2307
        &mut self,
×
UNCOV
2308
        block: &Block,
×
2309
        height: u32,
×
2310
        connected_to: BlockId,
×
2311
    ) -> Result<(), ApplyHeaderError> {
×
2312
        let mut changeset = ChangeSet::default();
×
2313
        changeset.merge(
×
2314
            self.chain
×
2315
                .apply_header_connected_to(&block.header, height, connected_to)?
×
2316
                .into(),
×
2317
        );
×
2318
        changeset.merge(
×
2319
            self.indexed_graph
×
2320
                .apply_block_relevant(block, height)
×
2321
                .into(),
×
2322
        );
×
2323
        self.stage.merge(changeset);
×
2324
        Ok(())
×
2325
    }
×
2326

2327
    /// Apply relevant unconfirmed transactions to the wallet.
2328
    ///
2329
    /// Transactions that are not relevant are filtered out.
2330
    ///
2331
    /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
2332
    /// when the transaction was last seen in the mempool. This is used for conflict resolution
2333
    /// when there is conflicting unconfirmed transactions. The transaction with the later
2334
    /// `last_seen` is prioritized.
2335
    ///
2336
    /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2337
    /// if you need the applied unconfirmed transactions to be reloaded after closing the wallet.
2338
    /// See [`Wallet::reveal_next_address`].
UNCOV
2339
    pub fn apply_unconfirmed_txs<'t>(
×
UNCOV
2340
        &mut self,
×
UNCOV
2341
        unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
×
2342
    ) {
×
2343
        let indexed_graph_changeset = self
×
2344
            .indexed_graph
×
2345
            .batch_insert_relevant_unconfirmed(unconfirmed_txs);
×
2346
        self.stage.merge(indexed_graph_changeset.into());
×
2347
    }
×
2348
}
2349

2350
/// Methods to construct sync/full-scan requests for spk-based chain sources.
2351
impl Wallet {
2352
    /// Create a partial [`SyncRequest`] for this wallet for all revealed spks.
2353
    ///
2354
    /// This is the first step when performing a spk-based wallet partial sync, the returned
2355
    /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
2356
    /// start a blockchain sync with a spk based blockchain client.
UNCOV
2357
    pub fn start_sync_with_revealed_spks(&self) -> SyncRequest {
×
UNCOV
2358
        SyncRequest::from_chain_tip(self.chain.tip())
×
UNCOV
2359
            .populate_with_revealed_spks(&self.indexed_graph.index, ..)
×
2360
    }
×
2361

2362
    /// Create a [`FullScanRequest] for this wallet.
2363
    ///
2364
    /// This is the first step when performing a spk-based wallet full scan, the returned
2365
    /// [`FullScanRequest] collects iterators for the wallet's keychain script pub keys needed to
2366
    /// start a blockchain full scan with a spk based blockchain client.
2367
    ///
2368
    /// This operation is generally only used when importing or restoring a previously used wallet
2369
    /// in which the list of used scripts is not known.
UNCOV
2370
    pub fn start_full_scan(&self) -> FullScanRequest<KeychainKind> {
×
UNCOV
2371
        FullScanRequest::from_keychain_txout_index(self.chain.tip(), &self.indexed_graph.index)
×
UNCOV
2372
    }
×
2373
}
2374

2375
impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime>> for Wallet {
UNCOV
2376
    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime> {
×
UNCOV
2377
        self.indexed_graph.graph()
×
UNCOV
2378
    }
×
2379
}
2380

2381
/// Deterministically generate a unique name given the descriptors defining the wallet
2382
///
2383
/// Compatible with [`wallet_name_from_descriptor`]
UNCOV
2384
pub fn wallet_name_from_descriptor<T>(
×
UNCOV
2385
    descriptor: T,
×
UNCOV
2386
    change_descriptor: Option<T>,
×
2387
    network: Network,
×
2388
    secp: &SecpCtx,
×
2389
) -> Result<String, DescriptorError>
×
2390
where
×
2391
    T: IntoWalletDescriptor,
×
2392
{
×
2393
    //TODO check descriptors contains only public keys
2394
    let descriptor = descriptor
×
2395
        .into_wallet_descriptor(secp, network)?
×
2396
        .0
2397
        .to_string();
×
2398
    let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
×
UNCOV
2399
    if let Some(change_descriptor) = change_descriptor {
×
2400
        let change_descriptor = change_descriptor
×
2401
            .into_wallet_descriptor(secp, network)?
×
2402
            .0
2403
            .to_string();
×
2404
        wallet_name.push_str(
×
UNCOV
2405
            calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
×
2406
        );
2407
    }
×
2408

UNCOV
2409
    Ok(wallet_name)
×
2410
}
×
2411

2412
fn new_local_utxo(
1,440✔
2413
    keychain: KeychainKind,
1,440✔
2414
    derivation_index: u32,
1,440✔
2415
    full_txo: FullTxOut<ConfirmationBlockTime>,
1,440✔
2416
) -> LocalOutput {
1,440✔
2417
    LocalOutput {
1,440✔
2418
        outpoint: full_txo.outpoint,
1,440✔
2419
        txout: full_txo.txout,
1,440✔
2420
        is_spent: full_txo.spent_by.is_some(),
1,440✔
2421
        confirmation_time: full_txo.chain_position.into(),
1,440✔
2422
        keychain,
1,440✔
2423
        derivation_index,
1,440✔
2424
    }
1,440✔
2425
}
1,440✔
2426

2427
fn create_signers<E: IntoWalletDescriptor>(
250✔
2428
    index: &mut KeychainTxOutIndex<KeychainKind>,
250✔
2429
    secp: &Secp256k1<All>,
250✔
2430
    descriptor: E,
250✔
2431
    change_descriptor: E,
250✔
2432
    network: Network,
250✔
2433
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), DescriptorError> {
250✔
2434
    let descriptor = into_wallet_descriptor_checked(descriptor, secp, network)?;
250✔
2435
    let change_descriptor = into_wallet_descriptor_checked(change_descriptor, secp, network)?;
250✔
2436
    let (descriptor, keymap) = descriptor;
250✔
2437
    let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
250✔
2438
    let _ = index
250✔
2439
        .insert_descriptor(KeychainKind::External, descriptor)
250✔
2440
        .expect("this is the first descriptor we're inserting");
250✔
2441

250✔
2442
    let (descriptor, keymap) = change_descriptor;
250✔
2443
    let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
250✔
2444
    let _ = index
250✔
2445
        .insert_descriptor(KeychainKind::Internal, descriptor)
250✔
2446
        .map_err(|e| {
250✔
2447
            use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2✔
2448
            match e {
2✔
2449
                InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2450
                    crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2✔
2451
                }
2452
                InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
UNCOV
2453
                    unreachable!("this is the first time we're assigning internal")
×
2454
                }
2455
            }
2456
        })?;
250✔
2457

2458
    Ok((signers, change_signers))
247✔
2459
}
249✔
2460

2461
/// Transforms a [`FeeRate`] to `f64` with unit as sat/vb.
2462
#[macro_export]
2463
#[doc(hidden)]
2464
macro_rules! floating_rate {
2465
    ($rate:expr) => {{
2466
        use $crate::bitcoin::constants::WITNESS_SCALE_FACTOR;
2467
        // sat_kwu / 250.0 -> sat_vb
2468
        $rate.to_sat_per_kwu() as f64 / ((1000 / WITNESS_SCALE_FACTOR) as f64)
2469
    }};
2470
}
2471

2472
#[macro_export]
2473
#[doc(hidden)]
2474
/// Macro for getting a wallet for use in a doctest
2475
macro_rules! doctest_wallet {
2476
    () => {{
2477
        use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
2478
        use $crate::chain::{ConfirmationBlockTime, BlockId, TxGraph};
2479
        use $crate::wallet::{Update, Wallet};
2480
        use $crate::KeychainKind;
2481
        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
2482
        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
2483

2484
        let mut wallet = Wallet::new(
2485
            descriptor,
2486
            change_descriptor,
2487
            Network::Regtest,
2488
        )
2489
        .unwrap();
2490
        let address = wallet.peek_address(KeychainKind::External, 0).address;
2491
        let tx = Transaction {
2492
            version: transaction::Version::ONE,
2493
            lock_time: absolute::LockTime::ZERO,
2494
            input: vec![],
2495
            output: vec![TxOut {
2496
                value: Amount::from_sat(500_000),
2497
                script_pubkey: address.script_pubkey(),
2498
            }],
2499
        };
2500
        let txid = tx.txid();
2501
        let block_id = BlockId { height: 500, hash: BlockHash::all_zeros() };
2502
        let _ = wallet.insert_checkpoint(block_id);
2503
        let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() });
2504
        let _ = wallet.insert_tx(tx);
2505
        let anchor = ConfirmationBlockTime {
2506
            confirmation_time: 50_000,
2507
            block_id,
2508
        };
2509
        let mut graph = TxGraph::default();
2510
        let _ = graph.insert_anchor(txid, anchor);
2511
        let update = Update { graph, ..Default::default() };
2512
        wallet.apply_update(update).unwrap();
2513
        wallet
2514
    }}
2515
}
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