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

bitcoindevkit / bdk / 9344636364

03 Jun 2024 05:14AM UTC coverage: 83.138% (-0.05%) from 83.186%
9344636364

Pull #1451

github

web-flow
Merge cd12fa248 into 4a8452f9b
Pull Request #1451: Various improvements not included in #1438

164 of 204 new or added lines in 5 files covered. (80.39%)

2 existing lines in 1 file now uncovered.

11301 of 13593 relevant lines covered (83.14%)

16475.86 hits per line

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

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

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

48
use bdk_chain::tx_graph::CalculateFeeError;
49

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

56
pub mod error;
57

58
pub use utils::IsDust;
59

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

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

76
const COINBASE_MATURITY: u32 = 100;
77

78
/// A Bitcoin wallet
79
///
80
/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related transactions.
81
/// Its main components are:
82
///
83
/// 1. output *descriptors* from which it can derive addresses.
84
/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
85
///
86
/// [`signer`]: crate::signer
87
#[derive(Debug)]
88
pub struct Wallet {
89
    signers: Arc<SignersContainer>,
90
    change_signers: Arc<SignersContainer>,
91
    chain: LocalChain,
92
    indexed_graph: IndexedTxGraph<ConfirmationTimeHeightAnchor, KeychainTxOutIndex<KeychainKind>>,
93
    persist: Persist<ChangeSet>,
94
    network: Network,
95
    secp: SecpCtx,
96
}
97

98
/// An update to [`Wallet`].
99
///
100
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
101
#[derive(Debug, Clone, Default)]
102
pub struct Update {
103
    /// Contains the last active derivation indices per keychain (`K`), which is used to update the
104
    /// [`KeychainTxOutIndex`].
105
    pub last_active_indices: BTreeMap<KeychainKind, u32>,
106

107
    /// Update for the wallet's internal [`TxGraph`].
108
    pub graph: TxGraph<ConfirmationTimeHeightAnchor>,
109

110
    /// Update for the wallet's internal [`LocalChain`].
111
    ///
112
    /// [`LocalChain`]: local_chain::LocalChain
113
    pub chain: Option<CheckPoint>,
114
}
115

116
impl From<FullScanResult<KeychainKind>> for Update {
117
    fn from(value: FullScanResult<KeychainKind>) -> Self {
×
118
        Self {
×
119
            last_active_indices: value.last_active_indices,
×
120
            graph: value.graph_update,
×
121
            chain: Some(value.chain_update),
×
122
        }
×
123
    }
×
124
}
125

126
impl From<SyncResult> for Update {
127
    fn from(value: SyncResult) -> Self {
×
128
        Self {
×
129
            last_active_indices: BTreeMap::new(),
×
130
            graph: value.graph_update,
×
131
            chain: Some(value.chain_update),
×
132
        }
×
133
    }
×
134
}
135

136
/// The changes made to a wallet by applying an [`Update`].
137
pub type ChangeSet = bdk_persist::CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>;
138

139
/// A derived address and the index it was found at.
140
/// For convenience this automatically derefs to `Address`
141
#[derive(Debug, PartialEq, Eq)]
142
pub struct AddressInfo {
143
    /// Child index of this address
144
    pub index: u32,
145
    /// Address
146
    pub address: Address,
147
    /// Type of keychain
148
    pub keychain: KeychainKind,
149
}
150

151
impl Deref for AddressInfo {
152
    type Target = Address;
153

154
    fn deref(&self) -> &Self::Target {
872✔
155
        &self.address
872✔
156
    }
872✔
157
}
158

159
impl fmt::Display for AddressInfo {
160
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208✔
161
        write!(f, "{}", self.address)
208✔
162
    }
208✔
163
}
164

165
impl Wallet {
166
    /// Creates a wallet that does not persist data.
167
    pub fn new_no_persist<E: IntoWalletDescriptor>(
148✔
168
        descriptor: E,
148✔
169
        change_descriptor: Option<E>,
148✔
170
        network: Network,
148✔
171
    ) -> Result<Self, DescriptorError> {
148✔
172
        Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
148✔
173
            NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
×
174
            NewError::Descriptor(e) => e,
×
175
            NewError::Persist(_) => unreachable!("mock-write must always succeed"),
×
176
        })
148✔
177
    }
148✔
178

179
    /// Creates a wallet that does not persist data, with a custom genesis hash.
180
    pub fn new_no_persist_with_genesis_hash<E: IntoWalletDescriptor>(
×
181
        descriptor: E,
×
182
        change_descriptor: Option<E>,
×
183
        network: Network,
×
184
        genesis_hash: BlockHash,
×
185
    ) -> Result<Self, crate::descriptor::DescriptorError> {
×
186
        Self::new_with_genesis_hash(descriptor, change_descriptor, (), network, genesis_hash)
×
187
            .map_err(|e| match e {
×
188
                NewError::NonEmptyDatabase => unreachable!("mock-database cannot have data"),
×
189
                NewError::Descriptor(e) => e,
×
190
                NewError::Persist(_) => unreachable!("mock-write must always succeed"),
×
191
            })
×
192
    }
×
193
}
194

195
/// The error type when constructing a fresh [`Wallet`].
196
///
197
/// Methods [`new`] and [`new_with_genesis_hash`] may return this error.
198
///
199
/// [`new`]: Wallet::new
200
/// [`new_with_genesis_hash`]: Wallet::new_with_genesis_hash
201
#[derive(Debug)]
202
pub enum NewError {
203
    /// Database already has data.
204
    NonEmptyDatabase,
205
    /// There was problem with the passed-in descriptor(s).
206
    Descriptor(crate::descriptor::DescriptorError),
207
    /// We were unable to write the wallet's data to the persistence backend.
208
    Persist(anyhow::Error),
209
}
210

211
impl fmt::Display for NewError {
212
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
213
        match self {
×
214
            NewError::NonEmptyDatabase => write!(
×
215
                f,
×
216
                "database already has data - use `load` or `new_or_load` methods instead"
×
217
            ),
×
218
            NewError::Descriptor(e) => e.fmt(f),
×
219
            NewError::Persist(e) => e.fmt(f),
×
220
        }
221
    }
×
222
}
223

224
#[cfg(feature = "std")]
225
impl std::error::Error for NewError {}
226

227
/// The error type when loading a [`Wallet`] from persistence.
228
///
229
/// Method [`load`] may return this error.
230
///
231
/// [`load`]: Wallet::load
232
#[derive(Debug)]
233
pub enum LoadError {
234
    /// There was a problem with the passed-in descriptor(s).
235
    Descriptor(crate::descriptor::DescriptorError),
236
    /// Loading data from the persistence backend failed.
237
    Persist(anyhow::Error),
238
    /// Wallet not initialized, persistence backend is empty.
239
    NotInitialized,
240
    /// Data loaded from persistence is missing network type.
241
    MissingNetwork,
242
    /// Data loaded from persistence is missing genesis hash.
243
    MissingGenesis,
244
    /// Data loaded from persistence is missing descriptor.
245
    MissingDescriptor,
246
}
247

248
impl fmt::Display for LoadError {
249
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
250
        match self {
×
251
            LoadError::Descriptor(e) => e.fmt(f),
×
252
            LoadError::Persist(e) => e.fmt(f),
×
253
            LoadError::NotInitialized => {
254
                write!(f, "wallet is not initialized, persistence backend is empty")
×
255
            }
256
            LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
×
257
            LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
×
258
            LoadError::MissingDescriptor => write!(f, "loaded data is missing descriptor"),
×
259
        }
260
    }
×
261
}
262

263
#[cfg(feature = "std")]
264
impl std::error::Error for LoadError {}
265

266
/// Error type for when we try load a [`Wallet`] from persistence and creating it if non-existent.
267
///
268
/// Methods [`new_or_load`] and [`new_or_load_with_genesis_hash`] may return this error.
269
///
270
/// [`new_or_load`]: Wallet::new_or_load
271
/// [`new_or_load_with_genesis_hash`]: Wallet::new_or_load_with_genesis_hash
272
#[derive(Debug)]
273
pub enum NewOrLoadError {
274
    /// There is a problem with the passed-in descriptor.
275
    Descriptor(crate::descriptor::DescriptorError),
276
    /// Either writing to or loading from the persistence backend failed.
277
    Persist(anyhow::Error),
278
    /// Wallet is not initialized, persistence backend is empty.
279
    NotInitialized,
280
    /// The loaded genesis hash does not match what was provided.
281
    LoadedGenesisDoesNotMatch {
282
        /// The expected genesis block hash.
283
        expected: BlockHash,
284
        /// The block hash loaded from persistence.
285
        got: Option<BlockHash>,
286
    },
287
    /// The loaded network type does not match what was provided.
288
    LoadedNetworkDoesNotMatch {
289
        /// The expected network type.
290
        expected: Network,
291
        /// The network type loaded from persistence.
292
        got: Option<Network>,
293
    },
294
    /// The loaded desccriptor does not match what was provided.
295
    LoadedDescriptorDoesNotMatch {
296
        /// The descriptor loaded from persistence.
297
        got: Option<ExtendedDescriptor>,
298
        /// The keychain of the descriptor not matching
299
        keychain: KeychainKind,
300
    },
301
}
302

303
impl fmt::Display for NewOrLoadError {
304
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
305
        match self {
×
306
            NewOrLoadError::Descriptor(e) => e.fmt(f),
×
307
            NewOrLoadError::Persist(e) => write!(
×
308
                f,
×
309
                "failed to either write to or load from persistence, {}",
×
310
                e
×
311
            ),
×
312
            NewOrLoadError::NotInitialized => {
313
                write!(f, "wallet is not initialized, persistence backend is empty")
×
314
            }
315
            NewOrLoadError::LoadedGenesisDoesNotMatch { expected, got } => {
×
316
                write!(f, "loaded genesis hash is not {}, got {:?}", expected, got)
×
317
            }
318
            NewOrLoadError::LoadedNetworkDoesNotMatch { expected, got } => {
×
319
                write!(f, "loaded network type is not {}, got {:?}", expected, got)
×
320
            }
321
            NewOrLoadError::LoadedDescriptorDoesNotMatch { got, keychain } => {
×
322
                write!(
×
323
                    f,
×
324
                    "loaded descriptor is different from what was provided, got {:?} for keychain {:?}",
×
325
                    got, keychain
×
326
                )
×
327
            }
328
        }
329
    }
×
330
}
331

332
#[cfg(feature = "std")]
333
impl std::error::Error for NewOrLoadError {}
334

335
/// An error that may occur when inserting a transaction into [`Wallet`].
336
#[derive(Debug)]
337
pub enum InsertTxError {
338
    /// The error variant that occurs when the caller attempts to insert a transaction with a
339
    /// confirmation height that is greater than the internal chain tip.
340
    ConfirmationHeightCannotBeGreaterThanTip {
341
        /// The internal chain's tip height.
342
        tip_height: u32,
343
        /// The introduced transaction's confirmation height.
344
        tx_height: u32,
345
    },
346
}
347

348
impl fmt::Display for InsertTxError {
349
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
350
        match self {
×
351
            InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
×
352
                tip_height,
×
353
                tx_height,
×
354
            } => {
×
355
                write!(f, "cannot insert tx with confirmation height ({}) higher than internal tip height ({})", tx_height, tip_height)
×
356
            }
×
357
        }
×
358
    }
×
359
}
360

361
#[cfg(feature = "std")]
362
impl std::error::Error for InsertTxError {}
363

364
/// An error that may occur when applying a block to [`Wallet`].
365
#[derive(Debug)]
366
pub enum ApplyBlockError {
367
    /// Occurs when the update chain cannot connect with original chain.
368
    CannotConnect(CannotConnectError),
369
    /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
370
    UnexpectedConnectedToHash {
371
        /// Block hash of `connected_to`.
372
        connected_to_hash: BlockHash,
373
        /// Expected block hash of `connected_to`, as derived from `block`.
374
        expected_hash: BlockHash,
375
    },
376
}
377

378
impl fmt::Display for ApplyBlockError {
379
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
380
        match self {
×
381
            ApplyBlockError::CannotConnect(err) => err.fmt(f),
×
382
            ApplyBlockError::UnexpectedConnectedToHash {
383
                expected_hash: block_hash,
×
384
                connected_to_hash: checkpoint_hash,
×
385
            } => write!(
×
386
                f,
×
387
                "`connected_to` hash {} differs from the expected hash {} (which is derived from `block`)",
×
388
                checkpoint_hash, block_hash
×
389
            ),
×
390
        }
391
    }
×
392
}
393

394
#[cfg(feature = "std")]
395
impl std::error::Error for ApplyBlockError {}
396

397
impl Wallet {
398
    /// Initialize an empty [`Wallet`].
399
    pub fn new<E: IntoWalletDescriptor>(
152✔
400
        descriptor: E,
152✔
401
        change_descriptor: Option<E>,
152✔
402
        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
152✔
403
        network: Network,
152✔
404
    ) -> Result<Self, NewError> {
152✔
405
        let genesis_hash = genesis_block(network).block_hash();
152✔
406
        Self::new_with_genesis_hash(descriptor, change_descriptor, db, network, genesis_hash)
152✔
407
    }
152✔
408

409
    /// Initialize an empty [`Wallet`] with a custom genesis hash.
410
    ///
411
    /// This is like [`Wallet::new`] with an additional `genesis_hash` parameter. This is useful
412
    /// for syncing from alternative networks.
413
    pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
154✔
414
        descriptor: E,
154✔
415
        change_descriptor: Option<E>,
154✔
416
        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
154✔
417
        network: Network,
154✔
418
        genesis_hash: BlockHash,
154✔
419
    ) -> Result<Self, NewError> {
154✔
420
        if let Ok(changeset) = db.load_from_persistence() {
154✔
421
            if changeset.is_some() {
154✔
422
                return Err(NewError::NonEmptyDatabase);
2✔
423
            }
152✔
424
        }
×
425
        let secp = Secp256k1::new();
152✔
426
        let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
152✔
427
        let mut index = KeychainTxOutIndex::<KeychainKind>::default();
152✔
428

429
        let (signers, change_signers) =
152✔
430
            create_signers(&mut index, &secp, descriptor, change_descriptor, network)
152✔
431
                .map_err(NewError::Descriptor)?;
152✔
432

433
        let indexed_graph = IndexedTxGraph::new(index);
152✔
434

152✔
435
        let mut persist = Persist::new(db);
152✔
436
        persist.stage(ChangeSet {
152✔
437
            chain: chain_changeset,
152✔
438
            indexed_tx_graph: indexed_graph.initial_changeset(),
152✔
439
            network: Some(network),
152✔
440
        });
152✔
441
        persist.commit().map_err(NewError::Persist)?;
152✔
442

443
        Ok(Wallet {
152✔
444
            signers,
152✔
445
            change_signers,
152✔
446
            network,
152✔
447
            chain,
152✔
448
            indexed_graph,
152✔
449
            persist,
152✔
450
            secp,
152✔
451
        })
152✔
452
    }
154✔
453

454
    /// Load [`Wallet`] from the given persistence backend.
455
    ///
456
    /// Note that the descriptor secret keys are not persisted to the db; this means that after
457
    /// calling this method the [`Wallet`] **won't** know the secret keys, and as such, won't be
458
    /// able to sign transactions.
459
    ///
460
    /// If you wish to use the wallet to sign transactions, you need to add the secret keys
461
    /// manually to the [`Wallet`]:
462
    ///
463
    /// ```rust,no_run
464
    /// # use bdk_wallet::Wallet;
465
    /// # use bdk_wallet::signer::{SignersContainer, SignerOrdering};
466
    /// # use bdk_wallet::descriptor::Descriptor;
467
    /// # use bitcoin::key::Secp256k1;
468
    /// # use bdk_wallet::KeychainKind;
469
    /// # use bdk_sqlite::{Store, rusqlite::Connection};
470
    /// #
471
    /// # fn main() -> Result<(), anyhow::Error> {
472
    /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
473
    /// # let file_path = temp_dir.path().join("store.db");
474
    /// # let conn = Connection::open(file_path).expect("must open connection");
475
    /// # let db = Store::new(conn).expect("must create db");
476
    /// let secp = Secp256k1::new();
477
    ///
478
    /// let (external_descriptor, external_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)").unwrap();
479
    /// let (internal_descriptor, internal_keymap) = Descriptor::parse_descriptor(&secp, "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)").unwrap();
480
    ///
481
    /// let external_signer_container = SignersContainer::build(external_keymap, &external_descriptor, &secp);
482
    /// let internal_signer_container = SignersContainer::build(internal_keymap, &internal_descriptor, &secp);
483
    ///
484
    /// let mut wallet = Wallet::load(db)?;
485
    ///
486
    /// external_signer_container.signers().into_iter()
487
    ///     .for_each(|s| wallet.add_signer(KeychainKind::External, SignerOrdering::default(), s.clone()));
488
    /// internal_signer_container.signers().into_iter()
489
    ///     .for_each(|s| wallet.add_signer(KeychainKind::Internal, SignerOrdering::default(), s.clone()));
490
    /// # Ok(())
491
    /// # }
492
    /// ```
493
    ///
494
    /// Alternatively, you can call [`Wallet::new_or_load`], which will add the private keys of the
495
    /// passed-in descriptors to the [`Wallet`].
496
    pub fn load(
2✔
497
        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
2✔
498
    ) -> Result<Self, LoadError> {
2✔
499
        let changeset = db
2✔
500
            .load_from_persistence()
2✔
501
            .map_err(LoadError::Persist)?
2✔
502
            .ok_or(LoadError::NotInitialized)?;
2✔
503
        Self::load_from_changeset(db, changeset)
2✔
504
    }
2✔
505

506
    fn load_from_changeset(
12✔
507
        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
12✔
508
        changeset: ChangeSet,
12✔
509
    ) -> Result<Self, LoadError> {
12✔
510
        let secp = Secp256k1::new();
12✔
511
        let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
12✔
512
        let chain =
12✔
513
            LocalChain::from_changeset(changeset.chain).map_err(|_| LoadError::MissingGenesis)?;
12✔
514
        let mut index = KeychainTxOutIndex::<KeychainKind>::default();
12✔
515
        let descriptor = changeset
12✔
516
            .indexed_tx_graph
12✔
517
            .indexer
12✔
518
            .keychains_added
12✔
519
            .get(&KeychainKind::External)
12✔
520
            .ok_or(LoadError::MissingDescriptor)?
12✔
521
            .clone();
12✔
522
        let change_descriptor = changeset
12✔
523
            .indexed_tx_graph
12✔
524
            .indexer
12✔
525
            .keychains_added
12✔
526
            .get(&KeychainKind::Internal)
12✔
527
            .cloned();
12✔
528

12✔
529
        let (signers, change_signers) =
12✔
530
            create_signers(&mut index, &secp, descriptor, change_descriptor, network)
12✔
531
                .expect("Can't fail: we passed in valid descriptors, recovered from the changeset");
12✔
532

12✔
533
        let mut indexed_graph = IndexedTxGraph::new(index);
12✔
534
        indexed_graph.apply_changeset(changeset.indexed_tx_graph);
12✔
535

12✔
536
        let persist = Persist::new(db);
12✔
537

12✔
538
        Ok(Wallet {
12✔
539
            signers,
12✔
540
            change_signers,
12✔
541
            chain,
12✔
542
            indexed_graph,
12✔
543
            persist,
12✔
544
            network,
12✔
545
            secp,
12✔
546
        })
12✔
547
    }
12✔
548

549
    /// Either loads [`Wallet`] from persistence, or initializes it if it does not exist.
550
    ///
551
    /// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
552
    pub fn new_or_load<E: IntoWalletDescriptor>(
10✔
553
        descriptor: E,
10✔
554
        change_descriptor: Option<E>,
10✔
555
        db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
10✔
556
        network: Network,
10✔
557
    ) -> Result<Self, NewOrLoadError> {
10✔
558
        let genesis_hash = genesis_block(network).block_hash();
10✔
559
        Self::new_or_load_with_genesis_hash(
10✔
560
            descriptor,
10✔
561
            change_descriptor,
10✔
562
            db,
10✔
563
            network,
10✔
564
            genesis_hash,
10✔
565
        )
10✔
566
    }
10✔
567

568
    /// Either loads [`Wallet`] from persistence, or initializes it if it does not exist, using the
569
    /// provided descriptor, change descriptor, network, and custom genesis hash.
570
    ///
571
    /// This method will fail if the loaded [`Wallet`] has different parameters to those provided.
572
    /// This is like [`Wallet::new_or_load`] with an additional `genesis_hash` parameter. This is
573
    /// useful for syncing from alternative networks.
574
    pub fn new_or_load_with_genesis_hash<E: IntoWalletDescriptor>(
12✔
575
        descriptor: E,
12✔
576
        change_descriptor: Option<E>,
12✔
577
        mut db: impl PersistBackend<ChangeSet> + Send + Sync + 'static,
12✔
578
        network: Network,
12✔
579
        genesis_hash: BlockHash,
12✔
580
    ) -> Result<Self, NewOrLoadError> {
12✔
581
        let changeset = db
12✔
582
            .load_from_persistence()
12✔
583
            .map_err(NewOrLoadError::Persist)?;
12✔
584
        match changeset {
12✔
585
            Some(changeset) => {
10✔
586
                let mut wallet = Self::load_from_changeset(db, changeset).map_err(|e| match e {
10✔
587
                    LoadError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
588
                    LoadError::Persist(e) => NewOrLoadError::Persist(e),
×
589
                    LoadError::NotInitialized => NewOrLoadError::NotInitialized,
×
590
                    LoadError::MissingNetwork => NewOrLoadError::LoadedNetworkDoesNotMatch {
×
591
                        expected: network,
×
592
                        got: None,
×
593
                    },
×
594
                    LoadError::MissingGenesis => NewOrLoadError::LoadedGenesisDoesNotMatch {
×
595
                        expected: genesis_hash,
×
596
                        got: None,
×
597
                    },
×
598
                    LoadError::MissingDescriptor => NewOrLoadError::LoadedDescriptorDoesNotMatch {
×
599
                        got: None,
×
600
                        keychain: KeychainKind::External,
×
601
                    },
×
602
                })?;
10✔
603
                if wallet.network != network {
10✔
604
                    return Err(NewOrLoadError::LoadedNetworkDoesNotMatch {
2✔
605
                        expected: network,
2✔
606
                        got: Some(wallet.network),
2✔
607
                    });
2✔
608
                }
8✔
609
                if wallet.chain.genesis_hash() != genesis_hash {
8✔
610
                    return Err(NewOrLoadError::LoadedGenesisDoesNotMatch {
2✔
611
                        expected: genesis_hash,
2✔
612
                        got: Some(wallet.chain.genesis_hash()),
2✔
613
                    });
2✔
614
                }
6✔
615

616
                let (expected_descriptor, expected_descriptor_keymap) = descriptor
6✔
617
                    .into_wallet_descriptor(&wallet.secp, network)
6✔
618
                    .map_err(NewOrLoadError::Descriptor)?;
6✔
619
                let wallet_descriptor = wallet.public_descriptor(KeychainKind::External).cloned();
6✔
620
                if wallet_descriptor != Some(expected_descriptor.clone()) {
6✔
621
                    return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
2✔
622
                        got: wallet_descriptor,
2✔
623
                        keychain: KeychainKind::External,
2✔
624
                    });
2✔
625
                }
4✔
626
                // if expected descriptor has private keys add them as new signers
4✔
627
                if !expected_descriptor_keymap.is_empty() {
4✔
628
                    let signer_container = SignersContainer::build(
4✔
629
                        expected_descriptor_keymap,
4✔
630
                        &expected_descriptor,
4✔
631
                        &wallet.secp,
4✔
632
                    );
4✔
633
                    signer_container.signers().into_iter().for_each(|signer| {
4✔
634
                        wallet.add_signer(
4✔
635
                            KeychainKind::External,
4✔
636
                            SignerOrdering::default(),
4✔
637
                            signer.clone(),
4✔
638
                        )
4✔
639
                    });
4✔
640
                }
4✔
641

642
                let expected_change_descriptor = if let Some(c) = change_descriptor {
4✔
643
                    Some(
644
                        c.into_wallet_descriptor(&wallet.secp, network)
2✔
645
                            .map_err(NewOrLoadError::Descriptor)?,
2✔
646
                    )
647
                } else {
648
                    None
2✔
649
                };
650
                let wallet_change_descriptor =
4✔
651
                    wallet.public_descriptor(KeychainKind::Internal).cloned();
4✔
652

4✔
653
                match (expected_change_descriptor, wallet_change_descriptor) {
4✔
654
                    (Some((expected_descriptor, expected_keymap)), Some(wallet_descriptor))
×
655
                        if wallet_descriptor == expected_descriptor =>
×
656
                    {
×
657
                        // if expected change descriptor has private keys add them as new signers
×
658
                        if !expected_keymap.is_empty() {
×
659
                            let signer_container = SignersContainer::build(
×
660
                                expected_keymap,
×
661
                                &expected_descriptor,
×
662
                                &wallet.secp,
×
663
                            );
×
664
                            signer_container.signers().into_iter().for_each(|signer| {
×
665
                                wallet.add_signer(
×
666
                                    KeychainKind::Internal,
×
667
                                    SignerOrdering::default(),
×
668
                                    signer.clone(),
×
669
                                )
×
670
                            });
×
671
                        }
×
672
                    }
673
                    (None, None) => (),
2✔
674
                    (_, wallet_descriptor) => {
2✔
675
                        return Err(NewOrLoadError::LoadedDescriptorDoesNotMatch {
2✔
676
                            got: wallet_descriptor,
2✔
677
                            keychain: KeychainKind::Internal,
2✔
678
                        });
2✔
679
                    }
680
                }
681

682
                Ok(wallet)
2✔
683
            }
684
            None => Self::new_with_genesis_hash(
2✔
685
                descriptor,
2✔
686
                change_descriptor,
2✔
687
                db,
2✔
688
                network,
2✔
689
                genesis_hash,
2✔
690
            )
2✔
691
            .map_err(|e| match e {
2✔
692
                NewError::NonEmptyDatabase => {
693
                    unreachable!("database is already checked to have no data")
×
694
                }
695
                NewError::Descriptor(e) => NewOrLoadError::Descriptor(e),
×
696
                NewError::Persist(e) => NewOrLoadError::Persist(e),
×
697
            }),
2✔
698
        }
699
    }
12✔
700

701
    /// Get the Bitcoin network the wallet is using.
702
    pub fn network(&self) -> Network {
48✔
703
        self.network
48✔
704
    }
48✔
705

706
    /// Iterator over all keychains in this wallet
707
    pub fn keychains(&self) -> impl Iterator<Item = (&KeychainKind, &ExtendedDescriptor)> {
64✔
708
        self.indexed_graph.index.keychains()
64✔
709
    }
64✔
710

711
    /// Peek an address of the given `keychain` at `index` without revealing it.
712
    ///
713
    /// For non-wildcard descriptors this returns the same address at every provided index.
714
    ///
715
    /// # Panics
716
    ///
717
    /// This panics when the caller requests for an address of derivation index greater than the
718
    /// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
719
    pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
1,256✔
720
        let keychain = self.map_keychain(keychain);
1,256✔
721
        let mut spk_iter = self
1,256✔
722
            .indexed_graph
1,256✔
723
            .index
1,256✔
724
            .unbounded_spk_iter(&keychain)
1,256✔
725
            .expect("Must exist (we called map_keychain)");
1,256✔
726
        if !spk_iter.descriptor().has_wildcard() {
1,256✔
727
            index = 0;
936✔
728
        }
936✔
729
        let (index, spk) = spk_iter
1,256✔
730
            .nth(index as usize)
1,256✔
731
            .expect("derivation index is out of bounds");
1,256✔
732

1,256✔
733
        AddressInfo {
1,256✔
734
            index,
1,256✔
735
            address: Address::from_script(&spk, self.network).expect("must have address form"),
1,256✔
736
            keychain,
1,256✔
737
        }
1,256✔
738
    }
1,256✔
739

740
    /// Attempt to reveal the next address of the given `keychain`.
741
    ///
742
    /// This will increment the internal derivation index. If the keychain's descriptor doesn't
743
    /// contain a wildcard or every address is already revealed up to the maximum derivation
744
    /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
745
    /// then returns the last revealed address.
746
    ///
747
    /// # Errors
748
    ///
749
    /// If writing to persistent storage fails.
750
    pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
120✔
751
        let keychain = self.map_keychain(keychain);
120✔
752
        let (next_spk, index_changeset) = self.indexed_graph.index.reveal_next_spk(&keychain);
120✔
753
        let (index, spk) = next_spk.expect("Must exist (we called map_keychain)");
120✔
754
        self.persist
120✔
755
            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
120✔
756

757
        Ok(AddressInfo {
120✔
758
            index,
120✔
759
            address: Address::from_script(&spk, self.network).expect("must have address form"),
120✔
760
            keychain,
120✔
761
        })
120✔
762
    }
120✔
763

764
    /// Reveal addresses up to and including the target `index` and return an iterator
765
    /// of newly revealed addresses.
766
    ///
767
    /// If the target `index` is unreachable, we make a best effort to reveal up to the last
768
    /// possible index. If all addresses up to the given `index` are already revealed, then
769
    /// no new addresses are returned.
770
    ///
771
    /// # Errors
772
    ///
773
    /// If writing to persistent storage fails.
774
    pub fn reveal_addresses_to(
16✔
775
        &mut self,
16✔
776
        keychain: KeychainKind,
16✔
777
        index: u32,
16✔
778
    ) -> anyhow::Result<impl Iterator<Item = AddressInfo> + '_> {
16✔
779
        let keychain = self.map_keychain(keychain);
16✔
780
        let (spk_iter, index_changeset) =
16✔
781
            self.indexed_graph.index.reveal_to_target(&keychain, index);
16✔
782
        let spk_iter = spk_iter.expect("Must exist (we called map_keychain)");
16✔
783

16✔
784
        self.persist
16✔
785
            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
16✔
786

787
        Ok(spk_iter.map(move |(index, spk)| AddressInfo {
24✔
788
            index,
10✔
789
            address: Address::from_script(&spk, self.network).expect("must have address form"),
10✔
790
            keychain,
10✔
791
        }))
24✔
792
    }
16✔
793

794
    /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
795
    /// derivation index that hasn't been used.
796
    ///
797
    /// This will attempt to derive and reveal a new address if no newly revealed addresses
798
    /// are available. See also [`reveal_next_address`](Self::reveal_next_address).
799
    ///
800
    /// # Errors
801
    ///
802
    /// If writing to persistent storage fails.
803
    pub fn next_unused_address(&mut self, keychain: KeychainKind) -> anyhow::Result<AddressInfo> {
896✔
804
        let keychain = self.map_keychain(keychain);
896✔
805
        let (next_spk, index_changeset) = self.indexed_graph.index.next_unused_spk(&keychain);
896✔
806
        let (index, spk) = next_spk.expect("Must exist (we called map_keychain)");
896✔
807

896✔
808
        self.persist
896✔
809
            .stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
896✔
810

811
        Ok(AddressInfo {
896✔
812
            index,
896✔
813
            address: Address::from_script(&spk, self.network).expect("must have address form"),
896✔
814
            keychain,
896✔
815
        })
896✔
816
    }
896✔
817

818
    /// Marks an address used of the given `keychain` at `index`.
819
    ///
820
    /// Returns whether the given index was present and then removed from the unused set.
821
    pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
8✔
822
        self.indexed_graph.index.mark_used(keychain, index)
8✔
823
    }
8✔
824

825
    /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted
826
    /// back into the unused set.
827
    ///
828
    /// Since this is only a superficial marker, it will have no effect if the address at the given
829
    /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the
830
    /// derived spk.
831
    ///
832
    /// [`mark_used`]: Self::mark_used
833
    pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
16✔
834
        self.indexed_graph.index.unmark_used(keychain, index)
16✔
835
    }
16✔
836

837
    /// List addresses that are revealed but unused.
838
    ///
839
    /// Note if the returned iterator is empty you can reveal more addresses
840
    /// by using [`reveal_next_address`](Self::reveal_next_address) or
841
    /// [`reveal_addresses_to`](Self::reveal_addresses_to).
842
    pub fn list_unused_addresses(
24✔
843
        &self,
24✔
844
        keychain: KeychainKind,
24✔
845
    ) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
24✔
846
        let keychain = self.map_keychain(keychain);
24✔
847
        self.indexed_graph
24✔
848
            .index
24✔
849
            .unused_keychain_spks(&keychain)
24✔
850
            .map(move |(index, spk)| AddressInfo {
32✔
851
                index,
11✔
852
                address: Address::from_script(&spk, self.network).expect("must have address form"),
11✔
853
                keychain,
11✔
854
            })
32✔
855
    }
24✔
856

857
    /// Return whether or not a `script` is part of this wallet (either internal or external)
858
    pub fn is_mine(&self, script: &Script) -> bool {
1,768✔
859
        self.indexed_graph.index.index_of_spk(script).is_some()
1,768✔
860
    }
1,768✔
861

862
    /// Finds how the wallet derived the script pubkey `spk`.
863
    ///
864
    /// Will only return `Some(_)` if the wallet has given out the spk.
865
    pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
56✔
866
        self.indexed_graph.index.index_of_spk(spk)
56✔
867
    }
56✔
868

869
    /// Return the list of unspent outputs of this wallet
870
    pub fn list_unspent(&self) -> impl Iterator<Item = LocalOutput> + '_ {
1,184✔
871
        self.indexed_graph
1,184✔
872
            .graph()
1,184✔
873
            .filter_chain_unspents(
1,184✔
874
                &self.chain,
1,184✔
875
                self.chain.tip().block_id(),
1,184✔
876
                self.indexed_graph.index.outpoints(),
1,184✔
877
            )
1,184✔
878
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
1,296✔
879
    }
1,184✔
880

881
    /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
882
    ///
883
    /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
884
    pub fn list_output(&self) -> impl Iterator<Item = LocalOutput> + '_ {
8✔
885
        self.indexed_graph
8✔
886
            .graph()
8✔
887
            .filter_chain_txouts(
8✔
888
                &self.chain,
8✔
889
                self.chain.tip().block_id(),
8✔
890
                self.indexed_graph.index.outpoints(),
8✔
891
            )
8✔
892
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
9✔
893
    }
8✔
894

895
    /// Get all the checkpoints the wallet is currently storing indexed by height.
896
    pub fn checkpoints(&self) -> CheckPointIter {
×
897
        self.chain.iter_checkpoints()
×
898
    }
×
899

900
    /// Returns the latest checkpoint.
901
    pub fn latest_checkpoint(&self) -> CheckPoint {
72✔
902
        self.chain.tip()
72✔
903
    }
72✔
904

905
    /// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
906
    ///
907
    /// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
908
    /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
909
    /// electrum server) which will go through each address until it reaches a *stop gap*.
910
    ///
911
    /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
912
    /// script pubkeys the wallet is storing internally).
913
    pub fn all_unbounded_spk_iters(
×
914
        &self,
×
915
    ) -> BTreeMap<KeychainKind, impl Iterator<Item = (u32, ScriptBuf)> + Clone> {
×
916
        self.indexed_graph.index.all_unbounded_spk_iters()
×
917
    }
×
918

919
    /// Get an unbounded script pubkey iterator for the given `keychain`.
920
    ///
921
    /// See [`all_unbounded_spk_iters`] for more documentation
922
    ///
923
    /// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
924
    pub fn unbounded_spk_iter(
×
925
        &self,
×
926
        keychain: KeychainKind,
×
927
    ) -> impl Iterator<Item = (u32, ScriptBuf)> + Clone {
×
928
        let keychain = self.map_keychain(keychain);
×
929
        self.indexed_graph
×
930
            .index
×
931
            .unbounded_spk_iter(&keychain)
×
932
            .expect("Must exist (we called map_keychain)")
×
933
    }
×
934

935
    /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
936
    /// wallet's database.
937
    pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
72✔
938
        let (keychain, index, _) = self.indexed_graph.index.txout(op)?;
72✔
939
        self.indexed_graph
72✔
940
            .graph()
72✔
941
            .filter_chain_unspents(
72✔
942
                &self.chain,
72✔
943
                self.chain.tip().block_id(),
72✔
944
                core::iter::once(((), op)),
72✔
945
            )
72✔
946
            .map(|(_, full_txo)| new_local_utxo(keychain, index, full_txo))
72✔
947
            .next()
72✔
948
    }
72✔
949

950
    /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
951
    ///
952
    /// This is used for providing a previous output's value so that we can use [`calculate_fee`]
953
    /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
954
    /// not be returned in [`list_unspent`] or [`list_output`].
955
    ///
956
    /// Any inserted `TxOut`s are not persisted until [`commit`] is called.
957
    ///
958
    /// **WARNING:** This should only be used to add `TxOut`s that the wallet does not own. Only
959
    /// insert `TxOut`s that you trust the values for!
960
    ///
961
    /// [`calculate_fee`]: Self::calculate_fee
962
    /// [`calculate_fee_rate`]: Self::calculate_fee_rate
963
    /// [`list_unspent`]: Self::list_unspent
964
    /// [`list_output`]: Self::list_output
965
    /// [`commit`]: Self::commit
966
    pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
16✔
967
        let additions = self.indexed_graph.insert_txout(outpoint, txout);
16✔
968
        self.persist.stage(additions.into());
16✔
969
    }
16✔
970

971
    /// Calculates the fee of a given transaction. Returns 0 if `tx` is a coinbase transaction.
972
    ///
973
    /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
974
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
975
    ///
976
    /// Note `tx` does not have to be in the graph for this to work.
977
    ///
978
    /// # Examples
979
    ///
980
    /// ```rust, no_run
981
    /// # use bitcoin::Txid;
982
    /// # use bdk_wallet::Wallet;
983
    /// # let mut wallet: Wallet = todo!();
984
    /// # let txid:Txid = todo!();
985
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
986
    /// let fee = wallet.calculate_fee(&tx).expect("fee");
987
    /// ```
988
    ///
989
    /// ```rust, no_run
990
    /// # use bitcoin::Psbt;
991
    /// # use bdk_wallet::Wallet;
992
    /// # let mut wallet: Wallet = todo!();
993
    /// # let mut psbt: Psbt = todo!();
994
    /// let tx = &psbt.clone().extract_tx().expect("tx");
995
    /// let fee = wallet.calculate_fee(tx).expect("fee");
996
    /// ```
997
    /// [`insert_txout`]: Self::insert_txout
998
    pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
536✔
999
        self.indexed_graph.graph().calculate_fee(tx)
536✔
1000
    }
536✔
1001

1002
    /// Calculate the [`FeeRate`] for a given transaction.
1003
    ///
1004
    /// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you must
1005
    /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
1006
    ///
1007
    /// Note `tx` does not have to be in the graph for this to work.
1008
    ///
1009
    /// # Examples
1010
    ///
1011
    /// ```rust, no_run
1012
    /// # use bitcoin::Txid;
1013
    /// # use bdk_wallet::Wallet;
1014
    /// # let mut wallet: Wallet = todo!();
1015
    /// # let txid:Txid = todo!();
1016
    /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
1017
    /// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
1018
    /// ```
1019
    ///
1020
    /// ```rust, no_run
1021
    /// # use bitcoin::Psbt;
1022
    /// # use bdk_wallet::Wallet;
1023
    /// # let mut wallet: Wallet = todo!();
1024
    /// # let mut psbt: Psbt = todo!();
1025
    /// let tx = &psbt.clone().extract_tx().expect("tx");
1026
    /// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
1027
    /// ```
1028
    /// [`insert_txout`]: Self::insert_txout
1029
    pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
144✔
1030
        self.calculate_fee(tx)
144✔
1031
            .map(|fee| Amount::from_sat(fee) / tx.weight())
144✔
1032
    }
144✔
1033

1034
    /// Compute the `tx`'s sent and received [`Amount`]s.
1035
    ///
1036
    /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
1037
    /// that spend from previous txouts tracked by this wallet. Received is the summation
1038
    /// of this tx's outputs that send to script pubkeys tracked by this wallet.
1039
    ///
1040
    /// # Examples
1041
    ///
1042
    /// ```rust, no_run
1043
    /// # use bitcoin::Txid;
1044
    /// # use bdk_wallet::Wallet;
1045
    /// # let mut wallet: Wallet = todo!();
1046
    /// # let txid:Txid = todo!();
1047
    /// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
1048
    /// let (sent, received) = wallet.sent_and_received(&tx);
1049
    /// ```
1050
    ///
1051
    /// ```rust, no_run
1052
    /// # use bitcoin::Psbt;
1053
    /// # use bdk_wallet::Wallet;
1054
    /// # let mut wallet: Wallet = todo!();
1055
    /// # let mut psbt: Psbt = todo!();
1056
    /// let tx = &psbt.clone().extract_tx().expect("tx");
1057
    /// let (sent, received) = wallet.sent_and_received(tx);
1058
    /// ```
1059
    pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) {
224✔
1060
        self.indexed_graph.index.sent_and_received(tx, ..)
224✔
1061
    }
224✔
1062

1063
    /// Get a single transaction from the wallet as a [`CanonicalTx`] (if the transaction exists).
1064
    ///
1065
    /// `CanonicalTx` contains the full transaction alongside meta-data such as:
1066
    /// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
1067
    ///   in the best chain.
1068
    /// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
1069
    ///   confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
1070
    ///   confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
1071
    ///   the transaction was last seen in the mempool is provided.
1072
    ///
1073
    /// ```rust, no_run
1074
    /// use bdk_chain::Anchor;
1075
    /// use bdk_wallet::{chain::ChainPosition, Wallet};
1076
    /// # let wallet: Wallet = todo!();
1077
    /// # let my_txid: bitcoin::Txid = todo!();
1078
    ///
1079
    /// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
1080
    ///
1081
    /// // get reference to full transaction
1082
    /// println!("my tx: {:#?}", canonical_tx.tx_node.tx);
1083
    ///
1084
    /// // list all transaction anchors
1085
    /// for anchor in canonical_tx.tx_node.anchors {
1086
    ///     println!(
1087
    ///         "tx is anchored by block of hash {}",
1088
    ///         anchor.anchor_block().hash
1089
    ///     );
1090
    /// }
1091
    ///
1092
    /// // get confirmation status of transaction
1093
    /// match canonical_tx.chain_position {
1094
    ///     ChainPosition::Confirmed(anchor) => println!(
1095
    ///         "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
1096
    ///         anchor.confirmation_height, anchor.anchor_block.height, anchor.anchor_block.hash,
1097
    ///     ),
1098
    ///     ChainPosition::Unconfirmed(last_seen) => println!(
1099
    ///         "tx is last seen at {}, it is unconfirmed as it is not anchored in the best chain",
1100
    ///         last_seen,
1101
    ///     ),
1102
    /// }
1103
    /// ```
1104
    ///
1105
    /// [`Anchor`]: bdk_chain::Anchor
1106
    pub fn get_tx(
56✔
1107
        &self,
56✔
1108
        txid: Txid,
56✔
1109
    ) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> {
56✔
1110
        let graph = self.indexed_graph.graph();
56✔
1111

56✔
1112
        Some(CanonicalTx {
56✔
1113
            chain_position: graph.get_chain_position(
56✔
1114
                &self.chain,
56✔
1115
                self.chain.tip().block_id(),
56✔
1116
                txid,
56✔
1117
            )?,
56✔
1118
            tx_node: graph.get_tx_node(txid)?,
56✔
1119
        })
1120
    }
56✔
1121

1122
    /// Add a new checkpoint to the wallet's internal view of the chain.
1123
    /// This stages but does not [`commit`] the change.
1124
    ///
1125
    /// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
1126
    /// there).
1127
    ///
1128
    /// [`commit`]: Self::commit
1129
    pub fn insert_checkpoint(
2,158✔
1130
        &mut self,
2,158✔
1131
        block_id: BlockId,
2,158✔
1132
    ) -> Result<bool, local_chain::AlterCheckPointError> {
2,158✔
1133
        let changeset = self.chain.insert_block(block_id)?;
2,158✔
1134
        let changed = !changeset.is_empty();
2,158✔
1135
        self.persist.stage(changeset.into());
2,158✔
1136
        Ok(changed)
2,158✔
1137
    }
2,158✔
1138

1139
    /// Add a transaction to the wallet's internal view of the chain. This stages but does not
1140
    /// [`commit`] the change.
1141
    ///
1142
    /// Returns whether anything changed with the transaction insertion (e.g. `false` if the
1143
    /// transaction was already inserted at the same position).
1144
    ///
1145
    /// A `tx` can be rejected if `position` has a height greater than the [`latest_checkpoint`].
1146
    /// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually
1147
    /// inserting new transactions.
1148
    ///
1149
    /// **WARNING:** If `position` is confirmed, we anchor the `tx` to a the lowest checkpoint that
1150
    /// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our
1151
    /// local view of the best chain's history.
1152
    ///
1153
    /// [`commit`]: Self::commit
1154
    /// [`latest_checkpoint`]: Self::latest_checkpoint
1155
    /// [`insert_checkpoint`]: Self::insert_checkpoint
1156
    pub fn insert_tx(
2,414✔
1157
        &mut self,
2,414✔
1158
        tx: Transaction,
2,414✔
1159
        position: ConfirmationTime,
2,414✔
1160
    ) -> Result<bool, InsertTxError> {
2,414✔
1161
        let (anchor, last_seen) = match position {
2,414✔
1162
            ConfirmationTime::Confirmed { height, time } => {
2,230✔
1163
                // anchor tx to checkpoint with lowest height that is >= position's height
1164
                let anchor = self
2,230✔
1165
                    .chain
2,230✔
1166
                    .range(height..)
2,230✔
1167
                    .last()
2,230✔
1168
                    .ok_or(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
2,230✔
1169
                        tip_height: self.chain.tip().height(),
2,230✔
1170
                        tx_height: height,
2,230✔
1171
                    })
2,230✔
1172
                    .map(|anchor_cp| ConfirmationTimeHeightAnchor {
2,230✔
1173
                        anchor_block: anchor_cp.block_id(),
2,230✔
1174
                        confirmation_height: height,
2,230✔
1175
                        confirmation_time: time,
2,230✔
1176
                    })?;
2,230✔
1177

1178
                (Some(anchor), None)
2,230✔
1179
            }
1180
            ConfirmationTime::Unconfirmed { last_seen } => (None, Some(last_seen)),
184✔
1181
        };
1182

1183
        let mut changeset = ChangeSet::default();
2,414✔
1184
        let txid = tx.txid();
2,414✔
1185
        changeset.append(self.indexed_graph.insert_tx(tx).into());
2,414✔
1186
        if let Some(anchor) = anchor {
2,414✔
1187
            changeset.append(self.indexed_graph.insert_anchor(txid, anchor).into());
2,230✔
1188
        }
2,230✔
1189
        if let Some(last_seen) = last_seen {
2,414✔
1190
            changeset.append(self.indexed_graph.insert_seen_at(txid, last_seen).into());
184✔
1191
        }
2,230✔
1192

1193
        let changed = !changeset.is_empty();
2,414✔
1194
        self.persist.stage(changeset);
2,414✔
1195
        Ok(changed)
2,414✔
1196
    }
2,414✔
1197

1198
    /// Iterate over the transactions in the wallet.
1199
    pub fn transactions(
38✔
1200
        &self,
38✔
1201
    ) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> + '_
38✔
1202
    {
38✔
1203
        self.indexed_graph
38✔
1204
            .graph()
38✔
1205
            .list_chain_txs(&self.chain, self.chain.tip().block_id())
38✔
1206
    }
38✔
1207

1208
    /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
1209
    /// values.
1210
    pub fn get_balance(&self) -> Balance {
32✔
1211
        self.indexed_graph.graph().balance(
32✔
1212
            &self.chain,
32✔
1213
            self.chain.tip().block_id(),
32✔
1214
            self.indexed_graph.index.outpoints(),
32✔
1215
            |&(k, _), _| k == KeychainKind::Internal,
32✔
1216
        )
32✔
1217
    }
32✔
1218

1219
    /// Add an external signer
1220
    ///
1221
    /// See [the `signer` module](signer) for an example.
1222
    pub fn add_signer(
48✔
1223
        &mut self,
48✔
1224
        keychain: KeychainKind,
48✔
1225
        ordering: SignerOrdering,
48✔
1226
        signer: Arc<dyn TransactionSigner>,
48✔
1227
    ) {
48✔
1228
        let signers = match keychain {
48✔
1229
            KeychainKind::External => Arc::make_mut(&mut self.signers),
48✔
1230
            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
×
1231
        };
1232

1233
        signers.add_external(signer.id(&self.secp), ordering, signer);
48✔
1234
    }
48✔
1235

1236
    /// Get the signers
1237
    ///
1238
    /// ## Example
1239
    ///
1240
    /// ```
1241
    /// # use bdk_wallet::{Wallet, KeychainKind};
1242
    /// # use bdk_wallet::bitcoin::Network;
1243
    /// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
1244
    /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
1245
    ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
1246
    ///     println!("secret_key: {}", secret_key);
1247
    /// }
1248
    ///
1249
    /// Ok::<(), Box<dyn std::error::Error>>(())
1250
    /// ```
1251
    pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
35✔
1252
        match keychain {
35✔
1253
            KeychainKind::External => Arc::clone(&self.signers),
22✔
1254
            KeychainKind::Internal => Arc::clone(&self.change_signers),
13✔
1255
        }
1256
    }
35✔
1257

1258
    /// Start building a transaction.
1259
    ///
1260
    /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
1261
    ///
1262
    /// ## Example
1263
    ///
1264
    /// ```
1265
    /// # use std::str::FromStr;
1266
    /// # use bitcoin::*;
1267
    /// # use bdk_wallet::*;
1268
    /// # use bdk_wallet::wallet::ChangeSet;
1269
    /// # use bdk_wallet::wallet::error::CreateTxError;
1270
    /// # use bdk_persist::PersistBackend;
1271
    /// # use anyhow::Error;
1272
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1273
    /// # let mut wallet = doctest_wallet!();
1274
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1275
    /// let psbt = {
1276
    ///    let mut builder =  wallet.build_tx();
1277
    ///    builder
1278
    ///        .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1279
    ///    builder.finish()?
1280
    /// };
1281
    ///
1282
    /// // sign and broadcast ...
1283
    /// # Ok::<(), anyhow::Error>(())
1284
    /// ```
1285
    ///
1286
    /// [`TxBuilder`]: crate::TxBuilder
1287
    pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm> {
1,088✔
1288
        TxBuilder {
1,088✔
1289
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
1,088✔
1290
            params: TxParams::default(),
1,088✔
1291
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
1,088✔
1292
        }
1,088✔
1293
    }
1,088✔
1294

1295
    pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
145✔
1296
        &mut self,
145✔
1297
        coin_selection: Cs,
145✔
1298
        params: TxParams,
145✔
1299
    ) -> Result<Psbt, CreateTxError> {
145✔
1300
        let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
145✔
1301
        let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
145✔
1302
        let internal_descriptor = keychains.get(&KeychainKind::Internal);
145✔
1303

1304
        let external_policy = external_descriptor
145✔
1305
            .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
145✔
1306
            .unwrap();
145✔
1307
        let internal_policy = internal_descriptor
145✔
1308
            .as_ref()
145✔
1309
            .map(|desc| {
145✔
1310
                Ok::<_, CreateTxError>(
5✔
1311
                    desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
5✔
1312
                        .unwrap(),
5✔
1313
                )
1314
            })
145✔
1315
            .transpose()?;
145✔
1316

1317
        // The policy allows spending external outputs, but it requires a policy path that hasn't been
1318
        // provided
1319
        if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
145✔
1320
            && external_policy.requires_path()
145✔
1321
            && params.external_policy_path.is_none()
4✔
1322
        {
1323
            return Err(CreateTxError::SpendingPolicyRequired(
1✔
1324
                KeychainKind::External,
1✔
1325
            ));
1✔
1326
        };
144✔
1327
        // Same for the internal_policy path, if present
1328
        if let Some(internal_policy) = &internal_policy {
144✔
1329
            if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
5✔
1330
                && internal_policy.requires_path()
5✔
1331
                && params.internal_policy_path.is_none()
×
1332
            {
1333
                return Err(CreateTxError::SpendingPolicyRequired(
×
1334
                    KeychainKind::Internal,
×
1335
                ));
×
1336
            };
5✔
1337
        }
139✔
1338

1339
        let external_requirements = external_policy.get_condition(
144✔
1340
            params
144✔
1341
                .external_policy_path
144✔
1342
                .as_ref()
144✔
1343
                .unwrap_or(&BTreeMap::new()),
144✔
1344
        )?;
144✔
1345
        let internal_requirements = internal_policy
144✔
1346
            .map(|policy| {
144✔
1347
                Ok::<_, CreateTxError>(
5✔
1348
                    policy.get_condition(
5✔
1349
                        params
5✔
1350
                            .internal_policy_path
5✔
1351
                            .as_ref()
5✔
1352
                            .unwrap_or(&BTreeMap::new()),
5✔
1353
                    )?,
5✔
1354
                )
1355
            })
144✔
1356
            .transpose()?;
144✔
1357

1358
        let requirements =
144✔
1359
            external_requirements.merge(&internal_requirements.unwrap_or_default())?;
144✔
1360

1361
        let version = match params.version {
142✔
1362
            Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
1✔
1363
            Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
18✔
1364
                return Err(CreateTxError::Version1Csv)
1✔
1365
            }
1366
            Some(tx_builder::Version(x)) => x,
18✔
1367
            None if requirements.csv.is_some() => 2,
124✔
1368
            None => 1,
120✔
1369
        };
1370

1371
        // We use a match here instead of a unwrap_or_else as it's way more readable :)
1372
        let current_height = match params.current_height {
142✔
1373
            // If they didn't tell us the current height, we assume it's the latest sync height.
1374
            None => {
1375
                let tip_height = self.chain.tip().height();
138✔
1376
                absolute::LockTime::from_height(tip_height).expect("invalid height")
138✔
1377
            }
1378
            Some(h) => h,
4✔
1379
        };
1380

1381
        let lock_time = match params.locktime {
141✔
1382
            // When no nLockTime is specified, we try to prevent fee sniping, if possible
1383
            None => {
1384
                // Fee sniping can be partially prevented by setting the timelock
1385
                // to current_height. If we don't know the current_height,
1386
                // we default to 0.
1387
                let fee_sniping_height = current_height;
139✔
1388

1389
                // We choose the biggest between the required nlocktime and the fee sniping
1390
                // height
1391
                match requirements.timelock {
4✔
1392
                    // No requirement, just use the fee_sniping_height
1393
                    None => fee_sniping_height,
135✔
1394
                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
1395
                    Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => {
4✔
1396
                        fee_sniping_height
×
1397
                    }
1398
                    // There's a time-based requirement or a block-based requirement greater
1399
                    // than the fee_sniping_height use that value
1400
                    Some(value) => value,
4✔
1401
                }
1402
            }
1403
            // Specific nLockTime required and we have no constraints, so just set to that value
1404
            Some(x) if requirements.timelock.is_none() => x,
3✔
1405
            // Specific nLockTime required and it's compatible with the constraints
1406
            Some(x)
1✔
1407
                if requirements.timelock.unwrap().is_same_unit(x)
2✔
1408
                    && x >= requirements.timelock.unwrap() =>
2✔
1409
            {
1✔
1410
                x
1✔
1411
            }
1412
            // Invalid nLockTime required
1413
            Some(x) => {
1✔
1414
                return Err(CreateTxError::LockTime {
1✔
1415
                    requested: x,
1✔
1416
                    required: requirements.timelock.unwrap(),
1✔
1417
                })
1✔
1418
            }
1419
        };
1420

1421
        // The nSequence to be by default for inputs unless an explicit sequence is specified.
1422
        let n_sequence = match (params.rbf, requirements.csv) {
141✔
1423
            // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
1424
            (None, None) if lock_time != absolute::LockTime::ZERO => {
116✔
1425
                Sequence::ENABLE_LOCKTIME_NO_RBF
115✔
1426
            }
1427
            // No RBF, CSV or nLockTime, make the transaction final
1428
            (None, None) => Sequence::MAX,
1✔
1429

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

1435
            // RBF with a specific value but that value is too high
1436
            (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
3✔
1437
                return Err(CreateTxError::RbfSequence)
1✔
1438
            }
1439
            // RBF with a specific value requested, but the value is incompatible with CSV
1440
            (Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
1✔
1441
                if !check_nsequence_rbf(rbf, csv) =>
1✔
1442
            {
1✔
1443
                return Err(CreateTxError::RbfSequenceCsv { rbf, csv })
1✔
1444
            }
1445

1446
            // RBF enabled with the default value with CSV also enabled. CSV takes precedence
1447
            (Some(tx_builder::RbfValue::Default), Some(csv)) => csv,
1✔
1448
            // Valid RBF, either default or with a specific value. We ignore the `CSV` value
1449
            // because we've already checked it before
1450
            (Some(rbf), _) => rbf.get_value(),
20✔
1451
        };
1452

1453
        let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
139✔
1454
            //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
1455
            FeePolicy::FeeAmount(fee) => {
9✔
1456
                if let Some(previous_fee) = params.bumping_fee {
9✔
1457
                    if fee < previous_fee.absolute {
6✔
1458
                        return Err(CreateTxError::FeeTooLow {
2✔
1459
                            required: previous_fee.absolute,
2✔
1460
                        });
2✔
1461
                    }
4✔
1462
                }
3✔
1463
                (FeeRate::ZERO, fee)
7✔
1464
            }
1465
            FeePolicy::FeeRate(rate) => {
130✔
1466
                if let Some(previous_fee) = params.bumping_fee {
130✔
1467
                    let required_feerate = FeeRate::from_sat_per_kwu(
11✔
1468
                        previous_fee.rate.to_sat_per_kwu()
11✔
1469
                            + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
11✔
1470
                    );
11✔
1471
                    if rate < required_feerate {
11✔
1472
                        return Err(CreateTxError::FeeRateTooLow {
1✔
1473
                            required: required_feerate,
1✔
1474
                        });
1✔
1475
                    }
10✔
1476
                }
119✔
1477
                (rate, 0)
129✔
1478
            }
1479
        };
1480

1481
        let mut tx = Transaction {
136✔
1482
            version: transaction::Version::non_standard(version),
136✔
1483
            lock_time,
136✔
1484
            input: vec![],
136✔
1485
            output: vec![],
136✔
1486
        };
136✔
1487

136✔
1488
        if params.manually_selected_only && params.utxos.is_empty() {
136✔
1489
            return Err(CreateTxError::NoUtxosSelected);
1✔
1490
        }
135✔
1491

135✔
1492
        // we keep it as a float while we accumulate it, and only round it at the end
135✔
1493
        let mut outgoing: u64 = 0;
135✔
1494
        let mut received: u64 = 0;
135✔
1495

135✔
1496
        let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
135✔
1497

1498
        for (index, (script_pubkey, value)) in recipients.enumerate() {
135✔
1499
            if !params.allow_dust
86✔
1500
                && value.is_dust(script_pubkey)
85✔
1501
                && !script_pubkey.is_provably_unspendable()
1✔
1502
            {
1503
                return Err(CreateTxError::OutputBelowDustLimit(index));
1✔
1504
            }
85✔
1505

85✔
1506
            if self.is_mine(script_pubkey) {
85✔
1507
                received += value;
43✔
1508
            }
46✔
1509

1510
            let new_out = TxOut {
85✔
1511
                script_pubkey: script_pubkey.clone(),
85✔
1512
                value: Amount::from_sat(value),
85✔
1513
            };
85✔
1514

85✔
1515
            tx.output.push(new_out);
85✔
1516

85✔
1517
            outgoing += value;
85✔
1518
        }
1519

1520
        fee_amount += (fee_rate * tx.weight()).to_sat();
134✔
1521

134✔
1522
        if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
134✔
1523
            && internal_descriptor.is_none()
1✔
1524
        {
1525
            return Err(CreateTxError::ChangePolicyDescriptor);
1✔
1526
        }
133✔
1527

133✔
1528
        let (required_utxos, optional_utxos) =
133✔
1529
            self.preselect_utxos(&params, Some(current_height.to_consensus_u32()));
133✔
1530

1531
        // get drain script
1532
        let drain_script = match params.drain_to {
133✔
1533
            Some(ref drain_recipient) => drain_recipient.clone(),
51✔
1534
            None => {
1535
                let change_keychain = self.map_keychain(KeychainKind::Internal);
82✔
1536
                let (next_spk, index_changeset) =
82✔
1537
                    self.indexed_graph.index.next_unused_spk(&change_keychain);
82✔
1538
                let (index, spk) = next_spk.expect("Keychain exists (we called map_keychain)");
82✔
1539
                self.indexed_graph.index.mark_used(change_keychain, index);
82✔
1540
                self.persist.stage(index_changeset.into());
82✔
1541
                self.persist.commit().map_err(CreateTxError::Persist)?;
82✔
1542
                spk
82✔
1543
            }
1544
        };
1545

1546
        let (required_utxos, optional_utxos) =
133✔
1547
            coin_selection::filter_duplicates(required_utxos, optional_utxos);
133✔
1548

1549
        let coin_selection = coin_selection.coin_select(
133✔
1550
            required_utxos,
133✔
1551
            optional_utxos,
133✔
1552
            fee_rate,
133✔
1553
            outgoing + fee_amount,
133✔
1554
            &drain_script,
133✔
1555
        )?;
133✔
1556
        fee_amount += coin_selection.fee_amount;
127✔
1557
        let excess = &coin_selection.excess;
127✔
1558

127✔
1559
        tx.input = coin_selection
127✔
1560
            .selected
127✔
1561
            .iter()
127✔
1562
            .map(|u| bitcoin::TxIn {
143✔
1563
                previous_output: u.outpoint(),
143✔
1564
                script_sig: ScriptBuf::default(),
143✔
1565
                sequence: u.sequence().unwrap_or(n_sequence),
143✔
1566
                witness: Witness::new(),
143✔
1567
            })
143✔
1568
            .collect();
127✔
1569

127✔
1570
        if tx.output.is_empty() {
127✔
1571
            // Uh oh, our transaction has no outputs.
1572
            // We allow this when:
1573
            // - We have a drain_to address and the utxos we must spend (this happens,
1574
            // for example, when we RBF)
1575
            // - We have a drain_to address and drain_wallet set
1576
            // Otherwise, we don't know who we should send the funds to, and how much
1577
            // we should send!
1578
            if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
49✔
1579
                if let NoChange {
1580
                    dust_threshold,
1✔
1581
                    remaining_amount,
1✔
1582
                    change_fee,
1✔
1583
                } = excess
47✔
1584
                {
1585
                    return Err(CreateTxError::InsufficientFunds {
1✔
1586
                        needed: *dust_threshold,
1✔
1587
                        available: remaining_amount.saturating_sub(*change_fee),
1✔
1588
                    });
1✔
1589
                }
46✔
1590
            } else {
1591
                return Err(CreateTxError::NoRecipients);
2✔
1592
            }
1593
        }
78✔
1594

1595
        match excess {
124✔
1596
            NoChange {
1597
                remaining_amount, ..
3✔
1598
            } => fee_amount += remaining_amount,
3✔
1599
            Change { amount, fee } => {
121✔
1600
                if self.is_mine(&drain_script) {
121✔
1601
                    received += amount;
111✔
1602
                }
111✔
1603
                fee_amount += fee;
121✔
1604

121✔
1605
                // create drain output
121✔
1606
                let drain_output = TxOut {
121✔
1607
                    value: Amount::from_sat(*amount),
121✔
1608
                    script_pubkey: drain_script,
121✔
1609
                };
121✔
1610

121✔
1611
                // TODO: We should pay attention when adding a new output: this might increase
121✔
1612
                // the length of the "number of vouts" parameter by 2 bytes, potentially making
121✔
1613
                // our feerate too low
121✔
1614
                tx.output.push(drain_output);
121✔
1615
            }
1616
        };
1617

1618
        // sort input/outputs according to the chosen algorithm
1619
        params.ordering.sort_tx(&mut tx);
124✔
1620

1621
        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
124✔
1622
        Ok(psbt)
122✔
1623
    }
145✔
1624

1625
    /// Bump the fee of a transaction previously created with this wallet.
1626
    ///
1627
    /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
1628
    /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
1629
    /// pre-populated with the inputs and outputs of the original transaction.
1630
    ///
1631
    /// ## Example
1632
    ///
1633
    /// ```no_run
1634
    /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
1635
    /// # use std::str::FromStr;
1636
    /// # use bitcoin::*;
1637
    /// # use bdk_wallet::*;
1638
    /// # use bdk_wallet::wallet::ChangeSet;
1639
    /// # use bdk_wallet::wallet::error::CreateTxError;
1640
    /// # use bdk_persist::PersistBackend;
1641
    /// # use anyhow::Error;
1642
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1643
    /// # let mut wallet = doctest_wallet!();
1644
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1645
    /// let mut psbt = {
1646
    ///     let mut builder = wallet.build_tx();
1647
    ///     builder
1648
    ///         .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
1649
    ///         .enable_rbf();
1650
    ///     builder.finish()?
1651
    /// };
1652
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1653
    /// let tx = psbt.clone().extract_tx().expect("tx");
1654
    /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
1655
    /// let mut psbt =  {
1656
    ///     let mut builder = wallet.build_fee_bump(tx.txid())?;
1657
    ///     builder
1658
    ///         .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
1659
    ///     builder.finish()?
1660
    /// };
1661
    ///
1662
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1663
    /// let fee_bumped_tx = psbt.extract_tx();
1664
    /// // broadcast fee_bumped_tx to replace original
1665
    /// # Ok::<(), anyhow::Error>(())
1666
    /// ```
1667
    // TODO: support for merging multiple transactions while bumping the fees
1668
    pub fn build_fee_bump(
152✔
1669
        &mut self,
152✔
1670
        txid: Txid,
152✔
1671
    ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
152✔
1672
        let graph = self.indexed_graph.graph();
152✔
1673
        let txout_index = &self.indexed_graph.index;
152✔
1674
        let chain_tip = self.chain.tip().block_id();
152✔
1675

1676
        let mut tx = graph
152✔
1677
            .get_tx(txid)
152✔
1678
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
152✔
1679
            .as_ref()
152✔
1680
            .clone();
152✔
1681

1682
        let pos = graph
152✔
1683
            .get_chain_position(&self.chain, chain_tip, txid)
152✔
1684
            .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?;
152✔
1685
        if let ChainPosition::Confirmed(_) = pos {
152✔
1686
            return Err(BuildFeeBumpError::TransactionConfirmed(txid));
8✔
1687
        }
144✔
1688

144✔
1689
        if !tx
144✔
1690
            .input
144✔
1691
            .iter()
144✔
1692
            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
144✔
1693
        {
1694
            return Err(BuildFeeBumpError::IrreplaceableTransaction(tx.txid()));
8✔
1695
        }
136✔
1696

1697
        let fee = self
136✔
1698
            .calculate_fee(&tx)
136✔
1699
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1700
        let fee_rate = self
136✔
1701
            .calculate_fee_rate(&tx)
136✔
1702
            .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
136✔
1703

1704
        // remove the inputs from the tx and process them
1705
        let original_txin = tx.input.drain(..).collect::<Vec<_>>();
136✔
1706
        let original_utxos = original_txin
136✔
1707
            .iter()
136✔
1708
            .map(|txin| -> Result<_, BuildFeeBumpError> {
144✔
1709
                let prev_tx = graph
144✔
1710
                    .get_tx(txin.previous_output.txid)
144✔
1711
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
144✔
1712
                let txout = &prev_tx.output[txin.previous_output.vout as usize];
144✔
1713

1714
                let confirmation_time: ConfirmationTime = graph
144✔
1715
                    .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
144✔
1716
                    .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?
144✔
1717
                    .cloned()
144✔
1718
                    .into();
144✔
1719

1720
                let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
144✔
1721
                    Some((keychain, derivation_index)) => {
144✔
1722
                        let satisfaction_weight = self
144✔
1723
                            .get_descriptor_for_keychain(keychain)
144✔
1724
                            .max_weight_to_satisfy()
144✔
1725
                            .unwrap();
144✔
1726
                        WeightedUtxo {
144✔
1727
                            utxo: Utxo::Local(LocalOutput {
144✔
1728
                                outpoint: txin.previous_output,
144✔
1729
                                txout: txout.clone(),
144✔
1730
                                keychain,
144✔
1731
                                is_spent: true,
144✔
1732
                                derivation_index,
144✔
1733
                                confirmation_time,
144✔
1734
                            }),
144✔
1735
                            satisfaction_weight,
144✔
1736
                        }
144✔
1737
                    }
1738
                    None => {
1739
                        let satisfaction_weight =
×
1740
                            serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len();
×
1741
                        WeightedUtxo {
×
1742
                            utxo: Utxo::Foreign {
×
1743
                                outpoint: txin.previous_output,
×
1744
                                sequence: Some(txin.sequence),
×
1745
                                psbt_input: Box::new(psbt::Input {
×
1746
                                    witness_utxo: Some(txout.clone()),
×
1747
                                    non_witness_utxo: Some(prev_tx.as_ref().clone()),
×
1748
                                    ..Default::default()
×
1749
                                }),
×
1750
                            },
×
1751
                            satisfaction_weight,
×
1752
                        }
×
1753
                    }
1754
                };
1755

1756
                Ok(weighted_utxo)
144✔
1757
            })
144✔
1758
            .collect::<Result<Vec<_>, _>>()?;
136✔
1759

1760
        if tx.output.len() > 1 {
136✔
1761
            let mut change_index = None;
80✔
1762
            for (index, txout) in tx.output.iter().enumerate() {
160✔
1763
                let change_type = self.map_keychain(KeychainKind::Internal);
160✔
1764
                match txout_index.index_of_spk(&txout.script_pubkey) {
160✔
1765
                    Some((keychain, _)) if keychain == change_type => change_index = Some(index),
104✔
1766
                    _ => {}
56✔
1767
                }
1768
            }
1769

1770
            if let Some(change_index) = change_index {
80✔
1771
                tx.output.remove(change_index);
80✔
1772
            }
80✔
1773
        }
56✔
1774

1775
        let params = TxParams {
136✔
1776
            // TODO: figure out what rbf option should be?
136✔
1777
            version: Some(tx_builder::Version(tx.version.0)),
136✔
1778
            recipients: tx
136✔
1779
                .output
136✔
1780
                .into_iter()
136✔
1781
                .map(|txout| (txout.script_pubkey, txout.value.to_sat()))
136✔
1782
                .collect(),
136✔
1783
            utxos: original_utxos,
136✔
1784
            bumping_fee: Some(tx_builder::PreviousFee {
136✔
1785
                absolute: fee,
136✔
1786
                rate: fee_rate,
136✔
1787
            }),
136✔
1788
            ..Default::default()
136✔
1789
        };
136✔
1790

136✔
1791
        Ok(TxBuilder {
136✔
1792
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
136✔
1793
            params,
136✔
1794
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
136✔
1795
        })
136✔
1796
    }
152✔
1797

1798
    /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
1799
    /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
1800
    ///
1801
    /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
1802
    /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
1803
    /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
1804
    /// in this library will.
1805
    ///
1806
    /// ## Example
1807
    ///
1808
    /// ```
1809
    /// # use std::str::FromStr;
1810
    /// # use bitcoin::*;
1811
    /// # use bdk_wallet::*;
1812
    /// # use bdk_wallet::wallet::ChangeSet;
1813
    /// # use bdk_wallet::wallet::error::CreateTxError;
1814
    /// # use bdk_persist::PersistBackend;
1815
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1816
    /// # let mut wallet = doctest_wallet!();
1817
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1818
    /// let mut psbt = {
1819
    ///     let mut builder = wallet.build_tx();
1820
    ///     builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1821
    ///     builder.finish()?
1822
    /// };
1823
    /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
1824
    /// assert!(finalized, "we should have signed all the inputs");
1825
    /// # Ok::<(),anyhow::Error>(())
1826
    pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, SignerError> {
344✔
1827
        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
344✔
1828
        // to derive our keys
344✔
1829
        self.update_psbt_with_descriptor(psbt)
344✔
1830
            .map_err(SignerError::MiniscriptPsbt)?;
344✔
1831

1832
        // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
1833
        // has the `non_witness_utxo`
1834
        if !sign_options.trust_witness_utxo
344✔
1835
            && psbt
296✔
1836
                .inputs
296✔
1837
                .iter()
296✔
1838
                .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
304✔
1839
                .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
296✔
1840
                .any(|i| i.non_witness_utxo.is_none())
296✔
1841
        {
1842
            return Err(SignerError::MissingNonWitnessUtxo);
×
1843
        }
344✔
1844

344✔
1845
        // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
344✔
1846
        // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
344✔
1847
        if !sign_options.allow_all_sighashes
344✔
1848
            && !psbt.inputs.iter().all(|i| {
352✔
1849
                i.sighash_type.is_none()
352✔
1850
                    || i.sighash_type == Some(EcdsaSighashType::All.into())
24✔
1851
                    || i.sighash_type == Some(TapSighashType::All.into())
16✔
1852
                    || i.sighash_type == Some(TapSighashType::Default.into())
16✔
1853
            })
352✔
1854
        {
1855
            return Err(SignerError::NonStandardSighash);
16✔
1856
        }
328✔
1857

1858
        for signer in self
392✔
1859
            .signers
328✔
1860
            .signers()
328✔
1861
            .iter()
328✔
1862
            .chain(self.change_signers.signers().iter())
328✔
1863
        {
1864
            signer.sign_transaction(psbt, &sign_options, &self.secp)?;
392✔
1865
        }
1866

1867
        // attempt to finalize
1868
        if sign_options.try_finalize {
296✔
1869
            self.finalize_psbt(psbt, sign_options)
272✔
1870
        } else {
1871
            Ok(false)
24✔
1872
        }
1873
    }
344✔
1874

1875
    /// Return the spending policies for the wallet's descriptor
1876
    pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, DescriptorError> {
24✔
1877
        let signers = match keychain {
24✔
1878
            KeychainKind::External => &self.signers,
24✔
1879
            KeychainKind::Internal => &self.change_signers,
×
1880
        };
1881

1882
        match self.public_descriptor(keychain) {
24✔
1883
            Some(desc) => Ok(desc.extract_policy(signers, BuildSatisfaction::None, &self.secp)?),
24✔
1884
            None => Ok(None),
×
1885
        }
1886
    }
24✔
1887

1888
    /// Return the "public" version of the wallet's descriptor, meaning a new descriptor that has
1889
    /// the same structure but with every secret key removed
1890
    ///
1891
    /// This can be used to build a watch-only version of a wallet
1892
    pub fn public_descriptor(&self, keychain: KeychainKind) -> Option<&ExtendedDescriptor> {
7,262✔
1893
        self.indexed_graph
7,262✔
1894
            .index
7,262✔
1895
            .keychains()
7,262✔
1896
            .find(|(k, _)| *k == &keychain)
7,589✔
1897
            .map(|(_, d)| d)
7,262✔
1898
    }
7,262✔
1899

1900
    /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
1901
    /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
1902
    /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer)
1903
    /// for further information.
1904
    ///
1905
    /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
1906
    ///
1907
    /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
1908
    pub fn finalize_psbt(
272✔
1909
        &self,
272✔
1910
        psbt: &mut Psbt,
272✔
1911
        sign_options: SignOptions,
272✔
1912
    ) -> Result<bool, SignerError> {
272✔
1913
        let chain_tip = self.chain.tip().block_id();
272✔
1914

272✔
1915
        let tx = &psbt.unsigned_tx;
272✔
1916
        let mut finished = true;
272✔
1917

1918
        for (n, input) in tx.input.iter().enumerate() {
312✔
1919
            let psbt_input = &psbt
312✔
1920
                .inputs
312✔
1921
                .get(n)
312✔
1922
                .ok_or(SignerError::InputIndexOutOfRange)?;
312✔
1923
            if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
304✔
1924
                continue;
16✔
1925
            }
288✔
1926
            let confirmation_height = self
288✔
1927
                .indexed_graph
288✔
1928
                .graph()
288✔
1929
                .get_chain_position(&self.chain, chain_tip, input.previous_output.txid)
288✔
1930
                .map(|chain_position| match chain_position {
288✔
1931
                    ChainPosition::Confirmed(a) => a.confirmation_height,
264✔
1932
                    ChainPosition::Unconfirmed(_) => u32::MAX,
×
1933
                });
288✔
1934
            let current_height = sign_options
288✔
1935
                .assume_height
288✔
1936
                .unwrap_or_else(|| self.chain.tip().height());
288✔
1937

288✔
1938
            // - Try to derive the descriptor by looking at the txout. If it's in our database, we
288✔
1939
            //   know exactly which `keychain` to use, and which derivation index it is
288✔
1940
            // - If that fails, try to derive it by looking at the psbt input: the complete logic
288✔
1941
            //   is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
288✔
1942
            //   `redeem_script` and `witness_script` to determine the right derivation
288✔
1943
            // - If that also fails, it will try it on the internal descriptor, if present
288✔
1944
            let desc = psbt
288✔
1945
                .get_utxo_for(n)
288✔
1946
                .and_then(|txout| self.get_descriptor_for_txout(&txout))
288✔
1947
                .or_else(|| {
288✔
1948
                    self.indexed_graph.index.keychains().find_map(|(_, desc)| {
16✔
1949
                        desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
16✔
1950
                    })
16✔
1951
                });
288✔
1952

288✔
1953
            match desc {
288✔
1954
                Some(desc) => {
272✔
1955
                    let mut tmp_input = bitcoin::TxIn::default();
272✔
1956
                    match desc.satisfy(
272✔
1957
                        &mut tmp_input,
272✔
1958
                        (
272✔
1959
                            PsbtInputSatisfier::new(psbt, n),
272✔
1960
                            After::new(Some(current_height), false),
272✔
1961
                            Older::new(Some(current_height), confirmation_height, false),
272✔
1962
                        ),
272✔
1963
                    ) {
272✔
1964
                        Ok(_) => {
1965
                            let psbt_input = &mut psbt.inputs[n];
264✔
1966
                            psbt_input.final_script_sig = Some(tmp_input.script_sig);
264✔
1967
                            psbt_input.final_script_witness = Some(tmp_input.witness);
264✔
1968
                            if sign_options.remove_partial_sigs {
264✔
1969
                                psbt_input.partial_sigs.clear();
240✔
1970
                            }
240✔
1971
                            if sign_options.remove_taproot_extras {
264✔
1972
                                // We just constructed the final witness, clear these fields.
264✔
1973
                                psbt_input.tap_key_sig = None;
264✔
1974
                                psbt_input.tap_script_sigs.clear();
264✔
1975
                                psbt_input.tap_scripts.clear();
264✔
1976
                                psbt_input.tap_key_origins.clear();
264✔
1977
                                psbt_input.tap_internal_key = None;
264✔
1978
                                psbt_input.tap_merkle_root = None;
264✔
1979
                            }
264✔
1980
                        }
1981
                        Err(_) => finished = false,
8✔
1982
                    }
1983
                }
1984
                None => finished = false,
16✔
1985
            }
1986
        }
1987

1988
        if finished && sign_options.remove_taproot_extras {
264✔
1989
            for output in &mut psbt.outputs {
568✔
1990
                output.tap_key_origins.clear();
328✔
1991
            }
328✔
1992
        }
24✔
1993

1994
        Ok(finished)
264✔
1995
    }
272✔
1996

1997
    /// Return the secp256k1 context used for all signing operations
1998
    pub fn secp_ctx(&self) -> &SecpCtx {
27✔
1999
        &self.secp
27✔
2000
    }
27✔
2001

2002
    /// Returns the descriptor used to create addresses for a particular `keychain`.
2003
    pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
6,067✔
2004
        self.public_descriptor(self.map_keychain(keychain))
6,067✔
2005
            .expect("we mapped it to external if it doesn't exist")
6,067✔
2006
    }
6,067✔
2007

2008
    /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
2009
    /// Otherwise, it will return the index of the highest address it has derived.
2010
    pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
40✔
2011
        self.indexed_graph.index.last_revealed_index(&keychain)
40✔
2012
    }
40✔
2013

2014
    /// The index of the next address that you would get if you were to ask the wallet for a new address
2015
    pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
×
2016
        let keychain = self.map_keychain(keychain);
×
2017
        self.indexed_graph
×
2018
            .index
×
2019
            .next_index(&keychain)
×
2020
            .expect("Keychain must exist (we called map_keychain)")
×
2021
            .0
×
2022
    }
×
2023

2024
    /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
2025
    ///
2026
    /// This frees up the change address used when creating the tx for use in future transactions.
2027
    // TODO: Make this free up reserved utxos when that's implemented
2028
    pub fn cancel_tx(&mut self, tx: &Transaction) {
16✔
2029
        let txout_index = &mut self.indexed_graph.index;
16✔
2030
        for txout in &tx.output {
48✔
2031
            if let Some((keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
32✔
2032
                // NOTE: unmark_used will **not** make something unused if it has actually been used
16✔
2033
                // by a tx in the tracker. It only removes the superficial marking.
16✔
2034
                txout_index.unmark_used(keychain, index);
16✔
2035
            }
16✔
2036
        }
2037
    }
16✔
2038

2039
    fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
9,243✔
2040
        if keychain == KeychainKind::Internal
9,243✔
2041
            && self.public_descriptor(KeychainKind::Internal).is_none()
1,013✔
2042
        {
2043
            KeychainKind::External
808✔
2044
        } else {
2045
            keychain
8,435✔
2046
        }
2047
    }
9,243✔
2048

2049
    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
288✔
2050
        let (keychain, child) = self
288✔
2051
            .indexed_graph
288✔
2052
            .index
288✔
2053
            .index_of_spk(&txout.script_pubkey)?;
288✔
2054
        let descriptor = self.get_descriptor_for_keychain(keychain);
272✔
2055
        descriptor.at_derivation_index(child).ok()
272✔
2056
    }
288✔
2057

2058
    fn get_available_utxos(&self) -> Vec<(LocalOutput, usize)> {
1,120✔
2059
        self.list_unspent()
1,120✔
2060
            .map(|utxo| {
1,232✔
2061
                let keychain = utxo.keychain;
1,232✔
2062
                (utxo, {
1,232✔
2063
                    self.get_descriptor_for_keychain(keychain)
1,232✔
2064
                        .max_weight_to_satisfy()
1,232✔
2065
                        .unwrap()
1,232✔
2066
                })
1,232✔
2067
            })
1,232✔
2068
            .collect()
1,120✔
2069
    }
1,120✔
2070

2071
    /// Given the options returns the list of utxos that must be used to form the
2072
    /// transaction and any further that may be used if needed.
2073
    fn preselect_utxos(
1,120✔
2074
        &self,
1,120✔
2075
        params: &TxParams,
1,120✔
2076
        current_height: Option<u32>,
1,120✔
2077
    ) -> (Vec<WeightedUtxo>, Vec<WeightedUtxo>) {
1,120✔
2078
        let TxParams {
1,120✔
2079
            change_policy,
1,120✔
2080
            unspendable,
1,120✔
2081
            utxos,
1,120✔
2082
            drain_wallet,
1,120✔
2083
            manually_selected_only,
1,120✔
2084
            bumping_fee,
1,120✔
2085
            ..
1,120✔
2086
        } = params;
1,120✔
2087

1,120✔
2088
        let manually_selected = utxos.clone();
1,120✔
2089
        // we mandate confirmed transactions if we're bumping the fee
1,120✔
2090
        let must_only_use_confirmed_tx = bumping_fee.is_some();
1,120✔
2091
        let must_use_all_available = *drain_wallet;
1,120✔
2092

1,120✔
2093
        let chain_tip = self.chain.tip().block_id();
1,120✔
2094
        //    must_spend <- manually selected utxos
1,120✔
2095
        //    may_spend  <- all other available utxos
1,120✔
2096
        let mut may_spend = self.get_available_utxos();
1,120✔
2097

1,120✔
2098
        may_spend.retain(|may_spend| {
1,232✔
2099
            !manually_selected
1,232✔
2100
                .iter()
1,232✔
2101
                .any(|manually_selected| manually_selected.utxo.outpoint() == may_spend.0.outpoint)
1,232✔
2102
        });
1,232✔
2103
        let mut must_spend = manually_selected;
1,120✔
2104

1,120✔
2105
        // NOTE: we are intentionally ignoring `unspendable` here. i.e manual
1,120✔
2106
        // selection overrides unspendable.
1,120✔
2107
        if *manually_selected_only {
1,120✔
2108
            return (must_spend, vec![]);
40✔
2109
        }
1,080✔
2110

1,080✔
2111
        let satisfies_confirmed = may_spend
1,080✔
2112
            .iter()
1,080✔
2113
            .map(|u| -> bool {
1,120✔
2114
                let txid = u.0.outpoint.txid;
1,120✔
2115
                let tx = match self.indexed_graph.graph().get_tx(txid) {
1,120✔
2116
                    Some(tx) => tx,
1,120✔
2117
                    None => return false,
×
2118
                };
2119
                let confirmation_time: ConfirmationTime = match self
1,120✔
2120
                    .indexed_graph
1,120✔
2121
                    .graph()
1,120✔
2122
                    .get_chain_position(&self.chain, chain_tip, txid)
1,120✔
2123
                {
2124
                    Some(chain_position) => chain_position.cloned().into(),
1,120✔
2125
                    None => return false,
×
2126
                };
2127

2128
                // Whether the UTXO is mature and, if needed, confirmed
2129
                let mut spendable = true;
1,120✔
2130
                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
1,120✔
2131
                    return false;
64✔
2132
                }
1,056✔
2133
                if tx.is_coinbase() {
1,056✔
2134
                    debug_assert!(
24✔
2135
                        confirmation_time.is_confirmed(),
24✔
2136
                        "coinbase must always be confirmed"
×
2137
                    );
2138
                    if let Some(current_height) = current_height {
24✔
2139
                        match confirmation_time {
24✔
2140
                            ConfirmationTime::Confirmed { height, .. } => {
24✔
2141
                                // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
24✔
2142
                                spendable &=
24✔
2143
                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
24✔
2144
                            }
24✔
2145
                            ConfirmationTime::Unconfirmed { .. } => spendable = false,
×
2146
                        }
2147
                    }
×
2148
                }
1,032✔
2149
                spendable
1,056✔
2150
            })
1,120✔
2151
            .collect::<Vec<_>>();
1,080✔
2152

1,080✔
2153
        let mut i = 0;
1,080✔
2154
        may_spend.retain(|u| {
1,120✔
2155
            let retain = change_policy.is_satisfied_by(&u.0)
1,120✔
2156
                && !unspendable.contains(&u.0.outpoint)
1,120✔
2157
                && satisfies_confirmed[i];
1,120✔
2158
            i += 1;
1,120✔
2159
            retain
1,120✔
2160
        });
1,120✔
2161

1,080✔
2162
        let mut may_spend = may_spend
1,080✔
2163
            .into_iter()
1,080✔
2164
            .map(|(local_utxo, satisfaction_weight)| WeightedUtxo {
1,080✔
2165
                satisfaction_weight,
1,040✔
2166
                utxo: Utxo::Local(local_utxo),
1,040✔
2167
            })
1,080✔
2168
            .collect();
1,080✔
2169

1,080✔
2170
        if must_use_all_available {
1,080✔
2171
            must_spend.append(&mut may_spend);
384✔
2172
        }
696✔
2173

2174
        (must_spend, may_spend)
1,080✔
2175
    }
1,120✔
2176

2177
    fn complete_transaction(
1,048✔
2178
        &self,
1,048✔
2179
        tx: Transaction,
1,048✔
2180
        selected: Vec<Utxo>,
1,048✔
2181
        params: TxParams,
1,048✔
2182
    ) -> Result<Psbt, CreateTxError> {
1,048✔
2183
        let mut psbt = Psbt::from_unsigned_tx(tx)?;
1,048✔
2184

2185
        if params.add_global_xpubs {
1,048✔
2186
            let all_xpubs = self
24✔
2187
                .keychains()
24✔
2188
                .flat_map(|(_, desc)| desc.get_extended_keys())
24✔
2189
                .collect::<Vec<_>>();
24✔
2190

2191
            for xpub in all_xpubs {
40✔
2192
                let origin = match xpub.origin {
16✔
2193
                    Some(origin) => origin,
8✔
2194
                    None if xpub.xkey.depth == 0 => {
16✔
2195
                        (xpub.root_fingerprint(&self.secp), vec![].into())
8✔
2196
                    }
2197
                    _ => return Err(CreateTxError::MissingKeyOrigin(xpub.xkey.to_string())),
8✔
2198
                };
2199

2200
                psbt.xpub.insert(xpub.xkey, origin);
16✔
2201
            }
2202
        }
1,024✔
2203

2204
        let mut lookup_output = selected
1,040✔
2205
            .into_iter()
1,040✔
2206
            .map(|utxo| (utxo.outpoint(), utxo))
1,168✔
2207
            .collect::<HashMap<_, _>>();
1,040✔
2208

2209
        // add metadata for the inputs
2210
        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
1,168✔
2211
            let utxo = match lookup_output.remove(&input.previous_output) {
1,168✔
2212
                Some(utxo) => utxo,
1,168✔
2213
                None => continue,
×
2214
            };
2215

2216
            match utxo {
1,168✔
2217
                Utxo::Local(utxo) => {
1,120✔
2218
                    *psbt_input =
1,120✔
2219
                        match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
1,120✔
2220
                            Ok(psbt_input) => psbt_input,
1,120✔
2221
                            Err(e) => match e {
×
2222
                                CreateTxError::UnknownUtxo => psbt::Input {
×
2223
                                    sighash_type: params.sighash,
×
2224
                                    ..psbt::Input::default()
×
2225
                                },
×
2226
                                _ => return Err(e),
×
2227
                            },
2228
                        }
2229
                }
2230
                Utxo::Foreign {
2231
                    outpoint,
48✔
2232
                    psbt_input: foreign_psbt_input,
48✔
2233
                    ..
48✔
2234
                } => {
48✔
2235
                    let is_taproot = foreign_psbt_input
48✔
2236
                        .witness_utxo
48✔
2237
                        .as_ref()
48✔
2238
                        .map(|txout| txout.script_pubkey.is_p2tr())
48✔
2239
                        .unwrap_or(false);
48✔
2240
                    if !is_taproot
48✔
2241
                        && !params.only_witness_utxo
40✔
2242
                        && foreign_psbt_input.non_witness_utxo.is_none()
16✔
2243
                    {
2244
                        return Err(CreateTxError::MissingNonWitnessUtxo(outpoint));
8✔
2245
                    }
40✔
2246
                    *psbt_input = *foreign_psbt_input;
40✔
2247
                }
2248
            }
2249
        }
2250

2251
        self.update_psbt_with_descriptor(&mut psbt)?;
1,032✔
2252

2253
        Ok(psbt)
1,032✔
2254
    }
1,048✔
2255

2256
    /// get the corresponding PSBT Input for a LocalUtxo
2257
    pub fn get_psbt_input(
1,136✔
2258
        &self,
1,136✔
2259
        utxo: LocalOutput,
1,136✔
2260
        sighash_type: Option<psbt::PsbtSighashType>,
1,136✔
2261
        only_witness_utxo: bool,
1,136✔
2262
    ) -> Result<psbt::Input, CreateTxError> {
1,136✔
2263
        // Try to find the prev_script in our db to figure out if this is internal or external,
2264
        // and the derivation index
2265
        let (keychain, child) = self
1,136✔
2266
            .indexed_graph
1,136✔
2267
            .index
1,136✔
2268
            .index_of_spk(&utxo.txout.script_pubkey)
1,136✔
2269
            .ok_or(CreateTxError::UnknownUtxo)?;
1,136✔
2270

2271
        let mut psbt_input = psbt::Input {
1,136✔
2272
            sighash_type,
1,136✔
2273
            ..psbt::Input::default()
1,136✔
2274
        };
1,136✔
2275

1,136✔
2276
        let desc = self.get_descriptor_for_keychain(keychain);
1,136✔
2277
        let derived_descriptor = desc
1,136✔
2278
            .at_derivation_index(child)
1,136✔
2279
            .expect("child can't be hardened");
1,136✔
2280

1,136✔
2281
        psbt_input
1,136✔
2282
            .update_with_descriptor_unchecked(&derived_descriptor)
1,136✔
2283
            .map_err(MiniscriptPsbtError::Conversion)?;
1,136✔
2284

2285
        let prev_output = utxo.outpoint;
1,136✔
2286
        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
1,136✔
2287
            if desc.is_witness() || desc.is_taproot() {
1,136✔
2288
                psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
1,104✔
2289
            }
1,104✔
2290
            if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
1,136✔
2291
                psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
904✔
2292
            }
904✔
2293
        }
×
2294
        Ok(psbt_input)
1,136✔
2295
    }
1,136✔
2296

2297
    fn update_psbt_with_descriptor(&self, psbt: &mut Psbt) -> Result<(), MiniscriptPsbtError> {
1,376✔
2298
        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
1,376✔
2299
        // the input utxos and outputs
1,376✔
2300
        let utxos = (0..psbt.inputs.len())
1,376✔
2301
            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
1,544✔
2302
            .chain(
1,376✔
2303
                psbt.unsigned_tx
1,376✔
2304
                    .output
1,376✔
2305
                    .iter()
1,376✔
2306
                    .enumerate()
1,376✔
2307
                    .map(|(i, out)| (false, i, out.clone())),
2,184✔
2308
            )
1,376✔
2309
            .collect::<Vec<_>>();
1,376✔
2310

2311
        // Try to figure out the keychain and derivation for every input and output
2312
        for (is_input, index, out) in utxos.into_iter() {
3,688✔
2313
            if let Some((keychain, child)) =
3,112✔
2314
                self.indexed_graph.index.index_of_spk(&out.script_pubkey)
3,688✔
2315
            {
2316
                let desc = self.get_descriptor_for_keychain(keychain);
3,112✔
2317
                let desc = desc
3,112✔
2318
                    .at_derivation_index(child)
3,112✔
2319
                    .expect("child can't be hardened");
3,112✔
2320

3,112✔
2321
                if is_input {
3,112✔
2322
                    psbt.update_input_with_descriptor(index, &desc)
1,432✔
2323
                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
1,432✔
2324
                } else {
2325
                    psbt.update_output_with_descriptor(index, &desc)
1,680✔
2326
                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
1,680✔
2327
                }
2328
            }
576✔
2329
        }
2330

2331
        Ok(())
1,376✔
2332
    }
1,376✔
2333

2334
    /// Return the checksum of the public descriptor associated to `keychain`
2335
    ///
2336
    /// Internally calls [`Self::get_descriptor_for_keychain`] to fetch the right descriptor
2337
    pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
8✔
2338
        self.get_descriptor_for_keychain(keychain)
8✔
2339
            .to_string()
8✔
2340
            .split_once('#')
8✔
2341
            .unwrap()
8✔
2342
            .1
8✔
2343
            .to_string()
8✔
2344
    }
8✔
2345

2346
    /// Applies an update to the wallet and stages the changes (but does not [`commit`] them).
2347
    ///
2348
    /// Usually you create an `update` by interacting with some blockchain data source and inserting
2349
    /// transactions related to your wallet into it.
2350
    ///
2351
    /// [`commit`]: Self::commit
2352
    pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
×
2353
        let update = update.into();
×
NEW
2354
        let mut changeset = ChangeSet::default();
×
2355

NEW
2356
        if let Some(chain_update) = update.chain {
×
NEW
2357
            changeset.append(self.chain.apply_update(chain_update)?.into());
×
NEW
2358
        }
×
NEW
2359
        changeset.append({
×
NEW
2360
            let (_, index_changeset) = self
×
NEW
2361
                .indexed_graph
×
NEW
2362
                .index
×
NEW
2363
                .reveal_to_target_multi(&update.last_active_indices);
×
NEW
2364
            index_changeset.into()
×
NEW
2365
        });
×
NEW
2366
        changeset.append(self.indexed_graph.apply_update(update.graph).into());
×
2367
        self.persist.stage(changeset);
×
2368
        Ok(())
×
2369
    }
×
2370

2371
    /// Commits all currently [`staged`] changed to the persistence backend returning and error when
2372
    /// this fails.
2373
    ///
2374
    /// This returns whether the `update` resulted in any changes.
2375
    ///
2376
    /// [`staged`]: Self::staged
2377
    pub fn commit(&mut self) -> anyhow::Result<bool> {
×
2378
        self.persist.commit().map(|c| c.is_some())
×
2379
    }
×
2380

2381
    /// Returns the changes that will be committed with the next call to [`commit`].
2382
    ///
2383
    /// [`commit`]: Self::commit
2384
    pub fn staged(&self) -> &ChangeSet {
×
2385
        self.persist.staged()
×
2386
    }
×
2387

2388
    /// Get a reference to the inner [`TxGraph`].
2389
    pub fn tx_graph(&self) -> &TxGraph<ConfirmationTimeHeightAnchor> {
×
2390
        self.indexed_graph.graph()
×
2391
    }
×
2392

2393
    /// Get a reference to the inner [`KeychainTxOutIndex`].
2394
    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
48✔
2395
        &self.indexed_graph.index
48✔
2396
    }
48✔
2397

2398
    /// Get a reference to the inner [`LocalChain`].
2399
    pub fn local_chain(&self) -> &LocalChain {
×
2400
        &self.chain
×
2401
    }
×
2402

2403
    /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
2404
    /// `prev_blockhash` of the block's header.
2405
    ///
2406
    /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
2407
    /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
2408
    ///
2409
    /// [`apply_block_connected_to`]: Self::apply_block_connected_to
2410
    pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
×
2411
        let connected_to = match height.checked_sub(1) {
×
2412
            Some(prev_height) => BlockId {
×
2413
                height: prev_height,
×
2414
                hash: block.header.prev_blockhash,
×
2415
            },
×
2416
            None => BlockId {
×
2417
                height,
×
2418
                hash: block.block_hash(),
×
2419
            },
×
2420
        };
2421
        self.apply_block_connected_to(block, height, connected_to)
×
2422
            .map_err(|err| match err {
×
2423
                ApplyHeaderError::InconsistentBlocks => {
2424
                    unreachable!("connected_to is derived from the block so must be consistent")
×
2425
                }
2426
                ApplyHeaderError::CannotConnect(err) => err,
×
2427
            })
×
2428
    }
×
2429

2430
    /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
2431
    /// block to the internal chain.
2432
    ///
2433
    /// The `connected_to` parameter informs the wallet how this block connects to the internal
2434
    /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
2435
    /// internal [`TxGraph`].
2436
    pub fn apply_block_connected_to(
×
2437
        &mut self,
×
2438
        block: &Block,
×
2439
        height: u32,
×
2440
        connected_to: BlockId,
×
2441
    ) -> Result<(), ApplyHeaderError> {
×
2442
        let mut changeset = ChangeSet::default();
×
2443
        changeset.append(
×
2444
            self.chain
×
2445
                .apply_header_connected_to(&block.header, height, connected_to)?
×
2446
                .into(),
×
2447
        );
×
2448
        changeset.append(
×
2449
            self.indexed_graph
×
2450
                .apply_block_relevant(block, height)
×
2451
                .into(),
×
2452
        );
×
2453
        self.persist.stage(changeset);
×
2454
        Ok(())
×
2455
    }
×
2456

2457
    /// Apply relevant unconfirmed transactions to the wallet.
2458
    ///
2459
    /// Transactions that are not relevant are filtered out.
2460
    ///
2461
    /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
2462
    /// when the transaction was last seen in the mempool. This is used for conflict resolution
2463
    /// when there is conflicting unconfirmed transactions. The transaction with the later
2464
    /// `last_seen` is prioritized.
2465
    pub fn apply_unconfirmed_txs<'t>(
×
2466
        &mut self,
×
2467
        unconfirmed_txs: impl IntoIterator<Item = (&'t Transaction, u64)>,
×
2468
    ) {
×
2469
        let indexed_graph_changeset = self
×
2470
            .indexed_graph
×
2471
            .batch_insert_relevant_unconfirmed(unconfirmed_txs);
×
NEW
2472
        self.persist.stage(indexed_graph_changeset.into());
×
2473
    }
×
2474
}
2475

2476
/// Methods to construct sync/full-scan requests for spk-based chain sources.
2477
impl Wallet {
2478
    /// Create a partial [`SyncRequest`] for this wallet for all revealed spks.
2479
    ///
2480
    /// This is the first step when performing a spk-based wallet partial sync, the returned
2481
    /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
2482
    /// start a blockchain sync with a spk based blockchain client.
2483
    pub fn start_sync_with_revealed_spks(&self) -> SyncRequest {
×
2484
        SyncRequest::from_chain_tip(self.chain.tip())
×
2485
            .cache_graph_txs(self.tx_graph())
×
2486
            .populate_with_revealed_spks(&self.indexed_graph.index, ..)
×
2487
    }
×
2488

2489
    /// Create a [`FullScanRequest] for this wallet.
2490
    ///
2491
    /// This is the first step when performing a spk-based wallet full scan, the returned
2492
    /// [`FullScanRequest] collects iterators for the wallet's keychain script pub keys needed to
2493
    /// start a blockchain full scan with a spk based blockchain client.
2494
    ///
2495
    /// This operation is generally only used when importing or restoring a previously used wallet
2496
    /// in which the list of used scripts is not known.
2497
    pub fn start_full_scan(&self) -> FullScanRequest<KeychainKind> {
×
2498
        FullScanRequest::from_keychain_txout_index(self.chain.tip(), &self.indexed_graph.index)
×
2499
            .cache_graph_txs(self.tx_graph())
×
2500
    }
×
2501
}
2502

2503
impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor>> for Wallet {
2504
    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeHeightAnchor> {
×
2505
        self.indexed_graph.graph()
×
2506
    }
×
2507
}
2508

2509
/// Deterministically generate a unique name given the descriptors defining the wallet
2510
///
2511
/// Compatible with [`wallet_name_from_descriptor`]
2512
pub fn wallet_name_from_descriptor<T>(
×
2513
    descriptor: T,
×
2514
    change_descriptor: Option<T>,
×
2515
    network: Network,
×
2516
    secp: &SecpCtx,
×
2517
) -> Result<String, DescriptorError>
×
2518
where
×
2519
    T: IntoWalletDescriptor,
×
2520
{
×
2521
    //TODO check descriptors contains only public keys
2522
    let descriptor = descriptor
×
2523
        .into_wallet_descriptor(secp, network)?
×
2524
        .0
2525
        .to_string();
×
2526
    let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
×
2527
    if let Some(change_descriptor) = change_descriptor {
×
2528
        let change_descriptor = change_descriptor
×
2529
            .into_wallet_descriptor(secp, network)?
×
2530
            .0
2531
            .to_string();
×
2532
        wallet_name.push_str(
×
2533
            calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
×
2534
        );
2535
    }
×
2536

2537
    Ok(wallet_name)
×
2538
}
×
2539

2540
fn new_local_utxo(
1,384✔
2541
    keychain: KeychainKind,
1,384✔
2542
    derivation_index: u32,
1,384✔
2543
    full_txo: FullTxOut<ConfirmationTimeHeightAnchor>,
1,384✔
2544
) -> LocalOutput {
1,384✔
2545
    LocalOutput {
1,384✔
2546
        outpoint: full_txo.outpoint,
1,384✔
2547
        txout: full_txo.txout,
1,384✔
2548
        is_spent: full_txo.spent_by.is_some(),
1,384✔
2549
        confirmation_time: full_txo.chain_position.into(),
1,384✔
2550
        keychain,
1,384✔
2551
        derivation_index,
1,384✔
2552
    }
1,384✔
2553
}
1,384✔
2554

2555
fn create_signers<E: IntoWalletDescriptor>(
164✔
2556
    index: &mut KeychainTxOutIndex<KeychainKind>,
164✔
2557
    secp: &Secp256k1<All>,
164✔
2558
    descriptor: E,
164✔
2559
    change_descriptor: Option<E>,
164✔
2560
    network: Network,
164✔
2561
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), crate::descriptor::error::Error> {
164✔
2562
    let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
164✔
2563
    let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
164✔
2564
    let _ = index.insert_descriptor(KeychainKind::External, descriptor);
164✔
2565

2566
    let change_signers = match change_descriptor {
164✔
2567
        Some(descriptor) => {
7✔
2568
            let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, secp, network)?;
7✔
2569
            let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
7✔
2570
            let _ = index.insert_descriptor(KeychainKind::Internal, descriptor);
7✔
2571
            signers
7✔
2572
        }
2573
        None => Arc::new(SignersContainer::new()),
157✔
2574
    };
2575

2576
    Ok((signers, change_signers))
164✔
2577
}
164✔
2578

2579
/// Transforms a [`FeeRate`] to `f64` with unit as sat/vb.
2580
#[macro_export]
2581
#[doc(hidden)]
2582
macro_rules! floating_rate {
2583
    ($rate:expr) => {{
2584
        use $crate::bitcoin::blockdata::constants::WITNESS_SCALE_FACTOR;
2585
        // sat_kwu / 250.0 -> sat_vb
2586
        $rate.to_sat_per_kwu() as f64 / ((1000 / WITNESS_SCALE_FACTOR) as f64)
2587
    }};
2588
}
2589

2590
#[macro_export]
2591
#[doc(hidden)]
2592
/// Macro for getting a wallet for use in a doctest
2593
macro_rules! doctest_wallet {
2594
    () => {{
2595
        use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
2596
        use $crate::chain::{ConfirmationTime, BlockId};
2597
        use $crate::{KeychainKind, wallet::Wallet};
2598
        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
2599
        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
2600

2601
        let mut wallet = Wallet::new_no_persist(
2602
            descriptor,
2603
            Some(change_descriptor),
2604
            Network::Regtest,
2605
        )
2606
        .unwrap();
2607
        let address = wallet.peek_address(KeychainKind::External, 0).address;
2608
        let tx = Transaction {
2609
            version: transaction::Version::ONE,
2610
            lock_time: absolute::LockTime::ZERO,
2611
            input: vec![],
2612
            output: vec![TxOut {
2613
                value: Amount::from_sat(500_000),
2614
                script_pubkey: address.script_pubkey(),
2615
            }],
2616
        };
2617
        let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() });
2618
        let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed {
2619
            height: 500,
2620
            time: 50_000
2621
        });
2622

2623
        wallet
2624
    }}
2625
}
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