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

bitcoindevkit / bdk / 5119600689

pending completion
5119600689

Pull #976

github

web-flow
Merge 250458ae0 into 9bc7fe855
Pull Request #976: Reimplement `Wallet`, `ElectrumExt` and `Esplora{Async}Ext` with redesigned structures.

909 of 909 new or added lines in 13 files covered. (100.0%)

7569 of 9556 relevant lines covered (79.21%)

5314.36 hits per line

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

87.44
/crates/bdk/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`] structure.
15
use crate::collections::{BTreeMap, HashMap, HashSet};
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::IndexedAdditions,
25
    keychain::{KeychainTxOutIndex, LocalChangeSet, LocalUpdate},
26
    local_chain::{self, LocalChain, UpdateNotConnectedError},
27
    tx_graph::{CanonicalTx, TxGraph},
28
    Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
29
    IndexedTxGraph, Persist, PersistBackend,
30
};
31
use bitcoin::consensus::encode::serialize;
32
use bitcoin::secp256k1::Secp256k1;
33
use bitcoin::util::psbt;
34
use bitcoin::{
35
    Address, BlockHash, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script,
36
    Sequence, Transaction, TxOut, Txid, Witness,
37
};
38
use core::fmt;
39
use core::ops::Deref;
40
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
41

42
#[allow(unused_imports)]
43
use log::{debug, error, info, trace};
44

45
pub mod coin_selection;
46
pub mod export;
47
pub mod signer;
48
pub mod tx_builder;
49
pub(crate) mod utils;
50

51
#[cfg(feature = "hardware-signer")]
52
#[cfg_attr(docsrs, doc(cfg(feature = "hardware-signer")))]
53
pub mod hardwaresigner;
54

55
pub use utils::IsDust;
56

57
#[allow(deprecated)]
58
use coin_selection::DefaultCoinSelectionAlgorithm;
59
use signer::{SignOptions, SignerOrdering, SignersContainer, TransactionSigner};
60
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
61
use utils::{check_nsequence_rbf, After, Older, SecpCtx};
62

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

74
const COINBASE_MATURITY: u32 = 100;
75

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

96
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
97
pub type Update = LocalUpdate<KeychainKind, ConfirmationTimeAnchor>;
98

99
/// The changeset produced internally by [`Wallet`] when mutated.
100
pub type ChangeSet = LocalChangeSet<KeychainKind, ConfirmationTimeAnchor>;
101

102
/// The address index selection strategy to use to derived an address from the wallet's external
103
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
104
#[derive(Debug)]
×
105
pub enum AddressIndex {
106
    /// Return a new address after incrementing the current descriptor index.
107
    New,
108
    /// Return the address for the current descriptor index if it has not been used in a received
109
    /// transaction. Otherwise return a new address as with [`AddressIndex::New`].
110
    ///
111
    /// Use with caution, if the wallet has not yet detected an address has been used it could
112
    /// return an already used address. This function is primarily meant for situations where the
113
    /// caller is untrusted; for example when deriving donation addresses on-demand for a public
114
    /// web page.
115
    LastUnused,
116
    /// Return the address for a specific descriptor index. Does not change the current descriptor
117
    /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
118
    ///
119
    /// Use with caution, if an index is given that is less than the current descriptor index
120
    /// then the returned address may have already been used.
121
    Peek(u32),
122
}
123

124
/// A derived address and the index it was found at.
125
/// For convenience this automatically derefs to `Address`
126
#[derive(Debug, PartialEq, Eq)]
7✔
127
pub struct AddressInfo {
128
    /// Child index of this address
129
    pub index: u32,
130
    /// Address
131
    pub address: Address,
132
    /// Type of keychain
133
    pub keychain: KeychainKind,
134
}
135

136
impl Deref for AddressInfo {
137
    type Target = Address;
138

139
    fn deref(&self) -> &Self::Target {
824✔
140
        &self.address
824✔
141
    }
824✔
142
}
143

144
impl fmt::Display for AddressInfo {
145
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192✔
146
        write!(f, "{}", self.address)
192✔
147
    }
192✔
148
}
149

150
impl Wallet {
151
    /// Creates a wallet that does not persist data.
152
    pub fn new_no_persist<E: IntoWalletDescriptor>(
138✔
153
        descriptor: E,
138✔
154
        change_descriptor: Option<E>,
138✔
155
        network: Network,
138✔
156
    ) -> Result<Self, crate::descriptor::DescriptorError> {
138✔
157
        Self::new(descriptor, change_descriptor, (), network).map_err(|e| match e {
138✔
158
            NewError::Descriptor(e) => e,
×
159
            NewError::Persist(_) => unreachable!("no persistence so it can't fail"),
×
160
        })
138✔
161
    }
138✔
162
}
163

164
#[derive(Debug)]
×
165
/// Error returned from [`Wallet::new`]
166
pub enum NewError<P> {
167
    /// There was problem with the descriptors passed in
168
    Descriptor(crate::descriptor::DescriptorError),
169
    /// We were unable to load the wallet's data from the persistance backend
170
    Persist(P),
171
}
172

173
impl<P> core::fmt::Display for NewError<P>
174
where
175
    P: core::fmt::Display,
176
{
177
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
×
178
        match self {
×
179
            NewError::Descriptor(e) => e.fmt(f),
×
180
            NewError::Persist(e) => {
×
181
                write!(f, "failed to load wallet from persistance backend: {}", e)
×
182
            }
183
        }
184
    }
×
185
}
186

187
/// An error that may occur when inserting a transaction into [`Wallet`].
188
#[derive(Debug)]
×
189
pub enum InsertTxError {
190
    /// The error variant that occurs when the caller attempts to insert a transaction with a
191
    /// confirmation height that is greater than the internal chain tip.
192
    ConfirmationHeightCannotBeGreaterThanTip {
193
        /// The internal chain's tip height.
194
        tip_height: Option<u32>,
195
        /// The introduced transaction's confirmation height.
196
        tx_height: u32,
197
    },
198
}
199

200
#[cfg(feature = "std")]
201
impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for NewError<P> {}
202

203
impl<D> Wallet<D> {
204
    /// Create a wallet from a `descriptor` (and an optional `change_descriptor`) and load related
205
    /// transaction data from `db`.
206
    pub fn new<E: IntoWalletDescriptor>(
138✔
207
        descriptor: E,
138✔
208
        change_descriptor: Option<E>,
138✔
209
        mut db: D,
138✔
210
        network: Network,
138✔
211
    ) -> Result<Self, NewError<D::LoadError>>
138✔
212
    where
138✔
213
        D: PersistBackend<ChangeSet>,
138✔
214
    {
138✔
215
        let secp = Secp256k1::new();
138✔
216
        let mut chain = LocalChain::default();
138✔
217
        let mut indexed_graph =
138✔
218
            IndexedTxGraph::<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>::default();
138✔
219

220
        let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)
138✔
221
            .map_err(NewError::Descriptor)?;
138✔
222
        indexed_graph
138✔
223
            .index
138✔
224
            .add_keychain(KeychainKind::External, descriptor.clone());
138✔
225
        let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp));
138✔
226
        let change_signers = match change_descriptor {
138✔
227
            Some(desc) => {
6✔
228
                let (change_descriptor, change_keymap) =
6✔
229
                    into_wallet_descriptor_checked(desc, &secp, network)
6✔
230
                        .map_err(NewError::Descriptor)?;
6✔
231

232
                let change_signers = Arc::new(SignersContainer::build(
6✔
233
                    change_keymap,
6✔
234
                    &change_descriptor,
6✔
235
                    &secp,
6✔
236
                ));
6✔
237

6✔
238
                indexed_graph
6✔
239
                    .index
6✔
240
                    .add_keychain(KeychainKind::Internal, change_descriptor);
6✔
241

6✔
242
                change_signers
6✔
243
            }
244
            None => Arc::new(SignersContainer::new()),
132✔
245
        };
246

247
        let changeset = db.load_from_persistence().map_err(NewError::Persist)?;
138✔
248
        chain.apply_changeset(changeset.chain_changeset);
138✔
249
        indexed_graph.apply_additions(changeset.indexed_additions);
138✔
250

138✔
251
        let persist = Persist::new(db);
138✔
252

138✔
253
        Ok(Wallet {
138✔
254
            signers,
138✔
255
            change_signers,
138✔
256
            network,
138✔
257
            chain,
138✔
258
            indexed_graph,
138✔
259
            persist,
138✔
260
            secp,
138✔
261
        })
138✔
262
    }
138✔
263

264
    /// Get the Bitcoin network the wallet is using.
265
    pub fn network(&self) -> Network {
×
266
        self.network
×
267
    }
×
268

269
    /// Iterator over all keychains in this wallet
270
    pub fn keychains(&self) -> &BTreeMap<KeychainKind, ExtendedDescriptor> {
4✔
271
        self.indexed_graph.index.keychains()
4✔
272
    }
4✔
273

274
    /// Return a derived address using the external descriptor, see [`AddressIndex`] for
275
    /// available address index selection strategies. If none of the keys in the descriptor are derivable
276
    /// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
277
    pub fn get_address(&mut self, address_index: AddressIndex) -> AddressInfo
247✔
278
    where
247✔
279
        D: PersistBackend<ChangeSet>,
247✔
280
    {
247✔
281
        self._get_address(KeychainKind::External, address_index)
247✔
282
            .expect("persistence backend must not fail")
247✔
283
    }
247✔
284

285
    /// Return a derived address using the internal (change) descriptor.
286
    ///
287
    /// If the wallet doesn't have an internal descriptor it will use the external descriptor.
288
    ///
289
    /// see [`AddressIndex`] for available address index selection strategies. If none of the keys
290
    /// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
291
    /// be returned for any [`AddressIndex`].
292
    pub fn get_internal_address(&mut self, address_index: AddressIndex) -> AddressInfo
5✔
293
    where
5✔
294
        D: PersistBackend<ChangeSet>,
5✔
295
    {
5✔
296
        self._get_address(KeychainKind::Internal, address_index)
5✔
297
            .expect("persistence backend must not fail")
5✔
298
    }
5✔
299

300
    /// Return a derived address using the specified `keychain` (external/internal).
301
    ///
302
    /// If `keychain` is [`KeychainKind::External`], external addresses will be derived (used for
303
    /// receiving funds).
304
    ///
305
    /// If `keychain` is [`KeychainKind::Internal`], internal addresses will be derived (used for
306
    /// creating change outputs). If the wallet does not have an internal keychain, it will use the
307
    /// external keychain to derive change outputs.
308
    ///
309
    /// See [`AddressIndex`] for available address index selection strategies. If none of the keys
310
    /// in the descriptor are derivable (i.e. does not end with /*) then the same address will
311
    /// always be returned for any [`AddressIndex`].
312
    fn _get_address(
252✔
313
        &mut self,
252✔
314
        keychain: KeychainKind,
252✔
315
        address_index: AddressIndex,
252✔
316
    ) -> Result<AddressInfo, D::WriteError>
252✔
317
    where
252✔
318
        D: PersistBackend<ChangeSet>,
252✔
319
    {
252✔
320
        let keychain = self.map_keychain(keychain);
252✔
321
        let txout_index = &mut self.indexed_graph.index;
252✔
322
        let (index, spk, additions) = match address_index {
252✔
323
            AddressIndex::New => {
324
                let ((index, spk), index_additions) = txout_index.reveal_next_spk(&keychain);
230✔
325
                (index, spk.clone(), Some(index_additions))
230✔
326
            }
327
            AddressIndex::LastUnused => {
328
                let ((index, spk), index_additions) = txout_index.next_unused_spk(&keychain);
14✔
329
                (index, spk.clone(), Some(index_additions))
14✔
330
            }
331
            AddressIndex::Peek(index) => {
8✔
332
                let (index, spk) = txout_index
8✔
333
                    .spks_of_keychain(&keychain)
8✔
334
                    .take(index as usize + 1)
8✔
335
                    .last()
8✔
336
                    .unwrap();
8✔
337
                (index, spk, None)
8✔
338
            }
339
        };
340

341
        if let Some(additions) = additions {
252✔
342
            self.persist
244✔
343
                .stage(ChangeSet::from(IndexedAdditions::from(additions)));
244✔
344
            self.persist.commit()?;
244✔
345
        }
8✔
346

347
        Ok(AddressInfo {
252✔
348
            index,
252✔
349
            address: Address::from_script(&spk, self.network)
252✔
350
                .expect("descriptor must have address form"),
252✔
351
            keychain,
252✔
352
        })
252✔
353
    }
252✔
354

355
    /// Return whether or not a `script` is part of this wallet (either internal or external)
356
    pub fn is_mine(&self, script: &Script) -> bool {
201✔
357
        self.indexed_graph.index.index_of_spk(script).is_some()
201✔
358
    }
201✔
359

360
    /// Finds how the wallet derived the script pubkey `spk`.
361
    ///
362
    /// Will only return `Some(_)` if the wallet has given out the spk.
363
    pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
8✔
364
        self.indexed_graph.index.index_of_spk(spk).copied()
8✔
365
    }
8✔
366

367
    /// Return the list of unspent outputs of this wallet
368
    pub fn list_unspent(&self) -> impl Iterator<Item = LocalUtxo> + '_ {
137✔
369
        self.indexed_graph
137✔
370
            .graph()
137✔
371
            .filter_chain_unspents(
137✔
372
                &self.chain,
137✔
373
                self.chain.tip().unwrap_or_default(),
137✔
374
                self.indexed_graph.index.outpoints().iter().cloned(),
137✔
375
            )
137✔
376
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
151✔
377
    }
137✔
378

379
    /// Get all the checkpoints the wallet is currently storing indexed by height.
380
    pub fn checkpoints(&self) -> &BTreeMap<u32, BlockHash> {
×
381
        self.chain.blocks()
×
382
    }
×
383

384
    /// Returns the latest checkpoint.
385
    pub fn latest_checkpoint(&self) -> Option<BlockId> {
9✔
386
        self.chain.tip()
9✔
387
    }
9✔
388

389
    /// Returns a iterators of all the script pubkeys for the `Internal` and External` variants in `KeychainKind`.
390
    ///
391
    /// This is inteded to be used when doing a full scan of your addresses (e.g. after restoring
392
    /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
393
    /// electrum server) which will go through each address until it reaches a *stop grap*.
394
    ///
395
    /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
396
    /// script pubkeys the wallet is storing internally).
397
    pub fn spks_of_all_keychains(
×
398
        &self,
×
399
    ) -> BTreeMap<KeychainKind, impl Iterator<Item = (u32, Script)> + Clone> {
×
400
        self.indexed_graph.index.spks_of_all_keychains()
×
401
    }
×
402

403
    /// Gets an iterator over all the script pubkeys in a single keychain.
404
    ///
405
    /// See [`spks_of_all_keychains`] for more documentation
406
    ///
407
    /// [`spks_of_all_keychains`]: Self::spks_of_all_keychains
408
    pub fn spks_of_keychain(
×
409
        &self,
×
410
        keychain: KeychainKind,
×
411
    ) -> impl Iterator<Item = (u32, Script)> + Clone {
×
412
        self.indexed_graph.index.spks_of_keychain(&keychain)
×
413
    }
×
414

415
    /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
416
    /// wallet's database.
417
    pub fn get_utxo(&self, op: OutPoint) -> Option<LocalUtxo> {
9✔
418
        let (&spk_i, _) = self.indexed_graph.index.txout(op)?;
9✔
419
        self.indexed_graph
9✔
420
            .graph()
9✔
421
            .filter_chain_unspents(
9✔
422
                &self.chain,
9✔
423
                self.chain.tip().unwrap_or_default(),
9✔
424
                core::iter::once((spk_i, op)),
9✔
425
            )
9✔
426
            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
9✔
427
            .next()
9✔
428
    }
9✔
429

430
    /// Return a single transactions made and received by the wallet
431
    ///
432
    /// Optionally fill the [`TransactionDetails::transaction`] field with the raw transaction if
433
    /// `include_raw` is `true`.
434
    pub fn get_tx(&self, txid: Txid, include_raw: bool) -> Option<TransactionDetails> {
4✔
435
        let graph = self.indexed_graph.graph();
4✔
436

437
        let canonical_tx = CanonicalTx {
4✔
438
            observed_as: graph.get_chain_position(
4✔
439
                &self.chain,
4✔
440
                self.chain.tip().unwrap_or_default(),
4✔
441
                txid,
4✔
442
            )?,
4✔
443
            node: graph.get_tx_node(txid)?,
4✔
444
        };
445

446
        Some(new_tx_details(
4✔
447
            &self.indexed_graph,
4✔
448
            canonical_tx,
4✔
449
            include_raw,
4✔
450
        ))
4✔
451
    }
4✔
452

453
    /// Add a new checkpoint to the wallet's internal view of the chain.
454
    /// This stages but does not [`commit`] the change.
455
    ///
456
    /// Returns whether anything changed with the insertion (e.g. `false` if checkpoint was already
457
    /// there).
458
    ///
459
    /// [`commit`]: Self::commit
460
    pub fn insert_checkpoint(
129✔
461
        &mut self,
129✔
462
        block_id: BlockId,
129✔
463
    ) -> Result<bool, local_chain::InsertBlockNotMatchingError>
129✔
464
    where
129✔
465
        D: PersistBackend<ChangeSet>,
129✔
466
    {
129✔
467
        let changeset = self.chain.insert_block(block_id)?;
129✔
468
        let changed = !changeset.is_empty();
129✔
469
        self.persist.stage(changeset.into());
129✔
470
        Ok(changed)
129✔
471
    }
129✔
472

473
    /// Add a transaction to the wallet's internal view of the chain. This stages but does not
474
    /// [`commit`] the change.
475
    ///
476
    /// Returns whether anything changed with the transaction insertion (e.g. `false` if the
477
    /// transaction was already inserted at the same position).
478
    ///
479
    /// A `tx` can be rejected if `position` has a height greater than the [`latest_checkpoint`].
480
    /// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually
481
    /// inserting new transactions.
482
    ///
483
    /// **WARNING:** If `position` is confirmed, we anchor the `tx` to a the lowest checkpoint that
484
    /// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our
485
    /// local view of the best chain's history.
486
    ///
487
    /// [`commit`]: Self::commit
488
    /// [`latest_checkpoint`]: Self::latest_checkpoint
489
    /// [`insert_checkpoint`]: Self::insert_checkpoint
490
    pub fn insert_tx(
161✔
491
        &mut self,
161✔
492
        tx: Transaction,
161✔
493
        position: ConfirmationTime,
161✔
494
    ) -> Result<bool, InsertTxError>
161✔
495
    where
161✔
496
        D: PersistBackend<ChangeSet>,
161✔
497
    {
161✔
498
        let (anchor, last_seen) = match position {
161✔
499
            ConfirmationTime::Confirmed { height, time } => {
138✔
500
                // anchor tx to checkpoint with lowest height that is >= position's height
501
                let anchor = self
138✔
502
                    .chain
138✔
503
                    .blocks()
138✔
504
                    .range(height..)
138✔
505
                    .next()
138✔
506
                    .ok_or(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
138✔
507
                        tip_height: self.chain.tip().map(|b| b.height),
138✔
508
                        tx_height: height,
138✔
509
                    })
138✔
510
                    .map(|(&anchor_height, &anchor_hash)| ConfirmationTimeAnchor {
138✔
511
                        anchor_block: BlockId {
138✔
512
                            height: anchor_height,
138✔
513
                            hash: anchor_hash,
138✔
514
                        },
138✔
515
                        confirmation_height: height,
138✔
516
                        confirmation_time: time,
138✔
517
                    })?;
138✔
518

519
                (Some(anchor), None)
138✔
520
            }
521
            ConfirmationTime::Unconfirmed { last_seen } => (None, Some(last_seen)),
23✔
522
        };
523

524
        let changeset: ChangeSet = self.indexed_graph.insert_tx(&tx, anchor, last_seen).into();
161✔
525
        let changed = !changeset.is_empty();
161✔
526
        self.persist.stage(changeset);
161✔
527
        Ok(changed)
161✔
528
    }
161✔
529

530
    /// Iterate over the transactions in the wallet.
531
    pub fn transactions(
7✔
532
        &self,
7✔
533
    ) -> impl Iterator<Item = CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>> + '_ {
7✔
534
        self.indexed_graph
7✔
535
            .graph()
7✔
536
            .list_chain_txs(&self.chain, self.chain.tip().unwrap_or_default())
7✔
537
    }
7✔
538

539
    /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
540
    /// values.
541
    pub fn get_balance(&self) -> Balance {
3✔
542
        self.indexed_graph.graph().balance(
3✔
543
            &self.chain,
3✔
544
            self.chain.tip().unwrap_or_default(),
3✔
545
            self.indexed_graph.index.outpoints().iter().cloned(),
3✔
546
            |&(k, _), _| k == KeychainKind::Internal,
3✔
547
        )
3✔
548
    }
3✔
549

550
    /// Add an external signer
551
    ///
552
    /// See [the `signer` module](signer) for an example.
553
    pub fn add_signer(
×
554
        &mut self,
×
555
        keychain: KeychainKind,
×
556
        ordering: SignerOrdering,
×
557
        signer: Arc<dyn TransactionSigner>,
×
558
    ) {
×
559
        let signers = match keychain {
×
560
            KeychainKind::External => Arc::make_mut(&mut self.signers),
×
561
            KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
×
562
        };
563

564
        signers.add_external(signer.id(&self.secp), ordering, signer);
×
565
    }
×
566

567
    /// Get the signers
568
    ///
569
    /// ## Example
570
    ///
571
    /// ```
572
    /// # use bdk::{Wallet, KeychainKind};
573
    /// # use bdk::bitcoin::Network;
574
    /// let wallet = Wallet::new_no_persist("wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*)", None, Network::Testnet)?;
575
    /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
576
    ///     // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
577
    ///     println!("secret_key: {}", secret_key);
578
    /// }
579
    ///
580
    /// Ok::<(), Box<dyn std::error::Error>>(())
581
    /// ```
582
    pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
9✔
583
        match keychain {
9✔
584
            KeychainKind::External => Arc::clone(&self.signers),
5✔
585
            KeychainKind::Internal => Arc::clone(&self.change_signers),
4✔
586
        }
587
    }
9✔
588

589
    /// Start building a transaction.
590
    ///
591
    /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the transaction.
592
    ///
593
    /// ## Example
594
    ///
595
    /// ```
596
    /// # use std::str::FromStr;
597
    /// # use bitcoin::*;
598
    /// # use bdk::*;
599
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
600
    /// # let mut wallet = doctest_wallet!();
601
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
602
    /// let (psbt, details) = {
603
    ///    let mut builder =  wallet.build_tx();
604
    ///    builder
605
    ///        .add_recipient(to_address.script_pubkey(), 50_000);
606
    ///    builder.finish()?
607
    /// };
608
    ///
609
    /// // sign and broadcast ...
610
    /// # Ok::<(), bdk::Error>(())
611
    /// ```
612
    ///
613
    /// [`TxBuilder`]: crate::TxBuilder
614
    pub fn build_tx(&mut self) -> TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, CreateTx> {
125✔
615
        TxBuilder {
125✔
616
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
125✔
617
            params: TxParams::default(),
125✔
618
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
125✔
619
            phantom: core::marker::PhantomData,
125✔
620
        }
125✔
621
    }
125✔
622

623
    pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
142✔
624
        &mut self,
142✔
625
        coin_selection: Cs,
142✔
626
        params: TxParams,
142✔
627
    ) -> Result<(psbt::PartiallySignedTransaction, TransactionDetails), Error>
142✔
628
    where
142✔
629
        D: PersistBackend<ChangeSet>,
142✔
630
    {
142✔
631
        let external_descriptor = self
142✔
632
            .indexed_graph
142✔
633
            .index
142✔
634
            .keychains()
142✔
635
            .get(&KeychainKind::External)
142✔
636
            .expect("must exist");
142✔
637
        let internal_descriptor = self
142✔
638
            .indexed_graph
142✔
639
            .index
142✔
640
            .keychains()
142✔
641
            .get(&KeychainKind::Internal);
142✔
642

643
        let external_policy = external_descriptor
142✔
644
            .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
142✔
645
            .unwrap();
142✔
646
        let internal_policy = internal_descriptor
142✔
647
            .as_ref()
142✔
648
            .map(|desc| {
142✔
649
                Ok::<_, Error>(
5✔
650
                    desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
5✔
651
                        .unwrap(),
5✔
652
                )
653
            })
142✔
654
            .transpose()?;
142✔
655

656
        // The policy allows spending external outputs, but it requires a policy path that hasn't been
657
        // provided
658
        if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
142✔
659
            && external_policy.requires_path()
142✔
660
            && params.external_policy_path.is_none()
4✔
661
        {
662
            return Err(Error::SpendingPolicyRequired(KeychainKind::External));
1✔
663
        };
141✔
664
        // Same for the internal_policy path, if present
665
        if let Some(internal_policy) = &internal_policy {
141✔
666
            if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
5✔
667
                && internal_policy.requires_path()
5✔
668
                && params.internal_policy_path.is_none()
×
669
            {
670
                return Err(Error::SpendingPolicyRequired(KeychainKind::Internal));
×
671
            };
5✔
672
        }
136✔
673

674
        let external_requirements = external_policy.get_condition(
141✔
675
            params
141✔
676
                .external_policy_path
141✔
677
                .as_ref()
141✔
678
                .unwrap_or(&BTreeMap::new()),
141✔
679
        )?;
141✔
680
        let internal_requirements = internal_policy
141✔
681
            .map(|policy| {
141✔
682
                Ok::<_, Error>(
5✔
683
                    policy.get_condition(
5✔
684
                        params
5✔
685
                            .internal_policy_path
5✔
686
                            .as_ref()
5✔
687
                            .unwrap_or(&BTreeMap::new()),
5✔
688
                    )?,
5✔
689
                )
690
            })
141✔
691
            .transpose()?;
141✔
692

693
        let requirements =
141✔
694
            external_requirements.merge(&internal_requirements.unwrap_or_default())?;
141✔
695
        debug!("Policy requirements: {:?}", requirements);
141✔
696

697
        let version = match params.version {
139✔
698
            Some(tx_builder::Version(0)) => {
699
                return Err(Error::Generic("Invalid version `0`".into()))
1✔
700
            }
701
            Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
1✔
702
                return Err(Error::Generic(
1✔
703
                    "TxBuilder requested version `1`, but at least `2` is needed to use OP_CSV"
1✔
704
                        .into(),
1✔
705
                ))
1✔
706
            }
707
            Some(tx_builder::Version(x)) => x,
18✔
708
            None if requirements.csv.is_some() => 2,
4✔
709
            _ => 1,
117✔
710
        };
711

712
        // We use a match here instead of a map_or_else as it's way more readable :)
713
        let current_height = match params.current_height {
139✔
714
            // If they didn't tell us the current height, we assume it's the latest sync height.
715
            None => self
135✔
716
                .chain
135✔
717
                .tip()
135✔
718
                .and_then(|cp| cp.height.into())
135✔
719
                .map(|height| LockTime::from_height(height).expect("Invalid height")),
135✔
720
            h => h,
4✔
721
        };
722

723
        let lock_time = match params.locktime {
138✔
724
            // When no nLockTime is specified, we try to prevent fee sniping, if possible
725
            None => {
726
                // Fee sniping can be partially prevented by setting the timelock
727
                // to current_height. If we don't know the current_height,
728
                // we default to 0.
729
                let fee_sniping_height = current_height.unwrap_or(LockTime::ZERO);
136✔
730

731
                // We choose the biggest between the required nlocktime and the fee sniping
732
                // height
733
                match requirements.timelock {
4✔
734
                    // No requirement, just use the fee_sniping_height
735
                    None => fee_sniping_height,
132✔
736
                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
737
                    Some(value @ LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height,
4✔
738
                    // There's a time-based requirement or a block-based requirement greater
739
                    // than the fee_sniping_height use that value
740
                    Some(value) => value,
4✔
741
                }
742
            }
743
            // Specific nLockTime required and we have no constraints, so just set to that value
744
            Some(x) if requirements.timelock.is_none() => x,
1✔
745
            // Specific nLockTime required and it's compatible with the constraints
746
            Some(x) if requirements.timelock.unwrap().is_same_unit(x) && x >= requirements.timelock.unwrap() => x,
2✔
747
            // Invalid nLockTime required
748
            Some(x) => return Err(Error::Generic(format!("TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script", x, requirements.timelock.unwrap())))
1✔
749
        };
750

751
        let n_sequence = match (params.rbf, requirements.csv) {
138✔
752
            // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
753
            (None, None) if lock_time != LockTime::ZERO => Sequence::ENABLE_LOCKTIME_NO_RBF,
113✔
754
            // No RBF, CSV or nLockTime, make the transaction final
755
            (None, None) => Sequence::MAX,
1✔
756

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

762
            // RBF with a specific value but that value is too high
763
            (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
1✔
764
                return Err(Error::Generic(
1✔
765
                    "Cannot enable RBF with a nSequence >= 0xFFFFFFFE".into(),
1✔
766
                ))
1✔
767
            }
768
            // RBF with a specific value requested, but the value is incompatible with CSV
769
            (Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
1✔
770
                if !check_nsequence_rbf(rbf, csv) =>
1✔
771
            {
1✔
772
                return Err(Error::Generic(format!(
1✔
773
                    "Cannot enable RBF with nSequence `{:?}` given a required OP_CSV of `{:?}`",
1✔
774
                    rbf, csv
1✔
775
                )))
1✔
776
            }
777

778
            // RBF enabled with the default value with CSV also enabled. CSV takes precedence
779
            (Some(tx_builder::RbfValue::Default), Some(csv)) => csv,
1✔
780
            // Valid RBF, either default or with a specific value. We ignore the `CSV` value
781
            // because we've already checked it before
782
            (Some(rbf), _) => rbf.get_value(),
20✔
783
        };
784

785
        let (fee_rate, mut fee_amount) = match params
136✔
786
            .fee_policy
136✔
787
            .as_ref()
136✔
788
            .unwrap_or(&FeePolicy::FeeRate(FeeRate::default()))
136✔
789
        {
790
            //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
791
            FeePolicy::FeeAmount(fee) => {
9✔
792
                if let Some(previous_fee) = params.bumping_fee {
9✔
793
                    if *fee < previous_fee.absolute {
6✔
794
                        return Err(Error::FeeTooLow {
2✔
795
                            required: previous_fee.absolute,
2✔
796
                        });
2✔
797
                    }
4✔
798
                }
3✔
799
                (FeeRate::from_sat_per_vb(0.0), *fee)
7✔
800
            }
801
            FeePolicy::FeeRate(rate) => {
127✔
802
                if let Some(previous_fee) = params.bumping_fee {
127✔
803
                    let required_feerate = FeeRate::from_sat_per_vb(previous_fee.rate + 1.0);
11✔
804
                    if *rate < required_feerate {
11✔
805
                        return Err(Error::FeeRateTooLow {
1✔
806
                            required: required_feerate,
1✔
807
                        });
1✔
808
                    }
10✔
809
                }
116✔
810
                (*rate, 0)
126✔
811
            }
812
        };
813

814
        let mut tx = Transaction {
133✔
815
            version,
133✔
816
            lock_time: lock_time.into(),
133✔
817
            input: vec![],
133✔
818
            output: vec![],
133✔
819
        };
133✔
820

133✔
821
        if params.manually_selected_only && params.utxos.is_empty() {
133✔
822
            return Err(Error::NoUtxosSelected);
1✔
823
        }
132✔
824

132✔
825
        // we keep it as a float while we accumulate it, and only round it at the end
132✔
826
        let mut outgoing: u64 = 0;
132✔
827
        let mut received: u64 = 0;
132✔
828

132✔
829
        let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
132✔
830

831
        for (index, (script_pubkey, value)) in recipients.enumerate() {
132✔
832
            if !params.allow_dust
84✔
833
                && value.is_dust(script_pubkey)
83✔
834
                && !script_pubkey.is_provably_unspendable()
1✔
835
            {
836
                return Err(Error::OutputBelowDustLimit(index));
1✔
837
            }
83✔
838

83✔
839
            if self.is_mine(script_pubkey) {
83✔
840
                received += value;
43✔
841
            }
44✔
842

843
            let new_out = TxOut {
83✔
844
                script_pubkey: script_pubkey.clone(),
83✔
845
                value,
83✔
846
            };
83✔
847

83✔
848
            tx.output.push(new_out);
83✔
849

83✔
850
            outgoing += value;
83✔
851
        }
852

853
        fee_amount += fee_rate.fee_wu(tx.weight());
131✔
854

131✔
855
        // Segwit transactions' header is 2WU larger than legacy txs' header,
131✔
856
        // as they contain a witness marker (1WU) and a witness flag (1WU) (see BIP144).
131✔
857
        // At this point we really don't know if the resulting transaction will be segwit
131✔
858
        // or legacy, so we just add this 2WU to the fee_amount - overshooting the fee amount
131✔
859
        // is better than undershooting it.
131✔
860
        // If we pass a fee_amount that is slightly higher than the final fee_amount, we
131✔
861
        // end up with a transaction with a slightly higher fee rate than the requested one.
131✔
862
        // If, instead, we undershoot, we may end up with a feerate lower than the requested one
131✔
863
        // - we might come up with non broadcastable txs!
131✔
864
        fee_amount += fee_rate.fee_wu(2);
131✔
865

131✔
866
        if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
131✔
867
            && internal_descriptor.is_none()
1✔
868
        {
869
            return Err(Error::Generic(
1✔
870
                "The `change_policy` can be set only if the wallet has a change_descriptor".into(),
1✔
871
            ));
1✔
872
        }
130✔
873

130✔
874
        let (required_utxos, optional_utxos) = self.preselect_utxos(
130✔
875
            params.change_policy,
130✔
876
            &params.unspendable,
130✔
877
            params.utxos.clone(),
130✔
878
            params.drain_wallet,
130✔
879
            params.manually_selected_only,
130✔
880
            params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee
130✔
881
            current_height.map(LockTime::to_consensus_u32),
130✔
882
        );
130✔
883

884
        // get drain script
885
        let drain_script = match params.drain_to {
130✔
886
            Some(ref drain_recipient) => drain_recipient.clone(),
50✔
887
            None => {
888
                let change_keychain = self.map_keychain(KeychainKind::Internal);
80✔
889
                let ((index, spk), index_additions) =
80✔
890
                    self.indexed_graph.index.next_unused_spk(&change_keychain);
80✔
891
                let spk = spk.clone();
80✔
892
                self.indexed_graph.index.mark_used(&change_keychain, index);
80✔
893
                self.persist
80✔
894
                    .stage(ChangeSet::from(IndexedAdditions::from(index_additions)));
80✔
895
                self.persist.commit().expect("TODO");
80✔
896
                spk
80✔
897
            }
898
        };
899

900
        let coin_selection = coin_selection.coin_select(
130✔
901
            required_utxos,
130✔
902
            optional_utxos,
130✔
903
            fee_rate,
130✔
904
            outgoing + fee_amount,
130✔
905
            &drain_script,
130✔
906
        )?;
130✔
907
        fee_amount += coin_selection.fee_amount;
124✔
908
        let excess = &coin_selection.excess;
124✔
909

124✔
910
        tx.input = coin_selection
124✔
911
            .selected
124✔
912
            .iter()
124✔
913
            .map(|u| bitcoin::TxIn {
139✔
914
                previous_output: u.outpoint(),
139✔
915
                script_sig: Script::default(),
139✔
916
                sequence: n_sequence,
139✔
917
                witness: Witness::new(),
139✔
918
            })
139✔
919
            .collect();
124✔
920

124✔
921
        if tx.output.is_empty() {
124✔
922
            // Uh oh, our transaction has no outputs.
923
            // We allow this when:
924
            // - We have a drain_to address and the utxos we must spend (this happens,
925
            // for example, when we RBF)
926
            // - We have a drain_to address and drain_wallet set
927
            // Otherwise, we don't know who we should send the funds to, and how much
928
            // we should send!
929
            if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
48✔
930
                if let NoChange {
931
                    dust_threshold,
1✔
932
                    remaining_amount,
1✔
933
                    change_fee,
1✔
934
                } = excess
46✔
935
                {
936
                    return Err(Error::InsufficientFunds {
1✔
937
                        needed: *dust_threshold,
1✔
938
                        available: remaining_amount.saturating_sub(*change_fee),
1✔
939
                    });
1✔
940
                }
45✔
941
            } else {
942
                return Err(Error::NoRecipients);
2✔
943
            }
944
        }
76✔
945

946
        match excess {
121✔
947
            NoChange {
948
                remaining_amount, ..
3✔
949
            } => fee_amount += remaining_amount,
3✔
950
            Change { amount, fee } => {
118✔
951
                if self.is_mine(&drain_script) {
118✔
952
                    received += amount;
107✔
953
                }
107✔
954
                fee_amount += fee;
118✔
955

118✔
956
                // create drain output
118✔
957
                let drain_output = TxOut {
118✔
958
                    value: *amount,
118✔
959
                    script_pubkey: drain_script,
118✔
960
                };
118✔
961

118✔
962
                // TODO: We should pay attention when adding a new output: this might increase
118✔
963
                // the lenght of the "number of vouts" parameter by 2 bytes, potentially making
118✔
964
                // our feerate too low
118✔
965
                tx.output.push(drain_output);
118✔
966
            }
967
        };
968

969
        // sort input/outputs according to the chosen algorithm
970
        params.ordering.sort_tx(&mut tx);
121✔
971

121✔
972
        let txid = tx.txid();
121✔
973
        let sent = coin_selection.local_selected_amount();
121✔
974
        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
121✔
975

976
        let transaction_details = TransactionDetails {
119✔
977
            transaction: None,
119✔
978
            txid,
119✔
979
            confirmation_time: ConfirmationTime::Unconfirmed { last_seen: 0 },
119✔
980
            received,
119✔
981
            sent,
119✔
982
            fee: Some(fee_amount),
119✔
983
        };
119✔
984

119✔
985
        Ok((psbt, transaction_details))
119✔
986
    }
142✔
987

988
    /// Bump the fee of a transaction previously created with this wallet.
989
    ///
990
    /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
991
    /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
992
    /// pre-populated with the inputs and outputs of the original transaction.
993
    ///
994
    /// ## Example
995
    ///
996
    /// ```no_run
997
    /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
998
    /// # use std::str::FromStr;
999
    /// # use bitcoin::*;
1000
    /// # use bdk::*;
1001
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1002
    /// # let mut wallet = doctest_wallet!();
1003
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
1004
    /// let (mut psbt, _) = {
1005
    ///     let mut builder = wallet.build_tx();
1006
    ///     builder
1007
    ///         .add_recipient(to_address.script_pubkey(), 50_000)
1008
    ///         .enable_rbf();
1009
    ///     builder.finish()?
1010
    /// };
1011
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1012
    /// let tx = psbt.extract_tx();
1013
    /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
1014
    /// let (mut psbt, _) =  {
1015
    ///     let mut builder = wallet.build_fee_bump(tx.txid())?;
1016
    ///     builder
1017
    ///         .fee_rate(FeeRate::from_sat_per_vb(5.0));
1018
    ///     builder.finish()?
1019
    /// };
1020
    ///
1021
    /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1022
    /// let fee_bumped_tx = psbt.extract_tx();
1023
    /// // broadcast fee_bumped_tx to replace original
1024
    /// # Ok::<(), bdk::Error>(())
1025
    /// ```
1026
    // TODO: support for merging multiple transactions while bumping the fees
1027
    pub fn build_fee_bump(
19✔
1028
        &mut self,
19✔
1029
        txid: Txid,
19✔
1030
    ) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
19✔
1031
        let graph = self.indexed_graph.graph();
19✔
1032
        let txout_index = &self.indexed_graph.index;
19✔
1033
        let chain_tip = self.chain.tip().unwrap_or_default();
19✔
1034

1035
        let mut tx = graph
19✔
1036
            .get_tx(txid)
19✔
1037
            .ok_or(Error::TransactionNotFound)?
19✔
1038
            .clone();
19✔
1039

1040
        let pos = graph
19✔
1041
            .get_chain_position(&self.chain, chain_tip, txid)
19✔
1042
            .ok_or(Error::TransactionNotFound)?;
19✔
1043
        if let ChainPosition::Confirmed(_) = pos {
19✔
1044
            return Err(Error::TransactionConfirmed);
1✔
1045
        }
18✔
1046

18✔
1047
        if !tx
18✔
1048
            .input
18✔
1049
            .iter()
18✔
1050
            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
18✔
1051
        {
1052
            return Err(Error::IrreplaceableTransaction);
1✔
1053
        }
17✔
1054

1055
        let fee = graph.calculate_fee(&tx).ok_or(Error::FeeRateUnavailable)?;
17✔
1056
        if fee < 0 {
17✔
1057
            // It's available but it's wrong so let's say it's unavailable
1058
            return Err(Error::FeeRateUnavailable)?;
×
1059
        }
17✔
1060
        let fee = fee as u64;
17✔
1061
        let feerate = FeeRate::from_wu(fee, tx.weight());
17✔
1062

17✔
1063
        // remove the inputs from the tx and process them
17✔
1064
        let original_txin = tx.input.drain(..).collect::<Vec<_>>();
17✔
1065
        let original_utxos = original_txin
17✔
1066
            .iter()
17✔
1067
            .map(|txin| -> Result<_, Error> {
18✔
1068
                let prev_tx = graph
18✔
1069
                    .get_tx(txin.previous_output.txid)
18✔
1070
                    .ok_or(Error::UnknownUtxo)?;
18✔
1071
                let txout = &prev_tx.output[txin.previous_output.vout as usize];
18✔
1072

1073
                let confirmation_time: ConfirmationTime = graph
18✔
1074
                    .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
18✔
1075
                    .ok_or(Error::UnknownUtxo)?
18✔
1076
                    .cloned()
18✔
1077
                    .into();
18✔
1078

1079
                let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
18✔
1080
                    Some(&(keychain, derivation_index)) => {
18✔
1081
                        let satisfaction_weight = self
18✔
1082
                            .get_descriptor_for_keychain(keychain)
18✔
1083
                            .max_satisfaction_weight()
18✔
1084
                            .unwrap();
18✔
1085
                        WeightedUtxo {
18✔
1086
                            utxo: Utxo::Local(LocalUtxo {
18✔
1087
                                outpoint: txin.previous_output,
18✔
1088
                                txout: txout.clone(),
18✔
1089
                                keychain,
18✔
1090
                                is_spent: true,
18✔
1091
                                derivation_index,
18✔
1092
                                confirmation_time,
18✔
1093
                            }),
18✔
1094
                            satisfaction_weight,
18✔
1095
                        }
18✔
1096
                    }
1097
                    None => {
1098
                        let satisfaction_weight =
×
1099
                            serialize(&txin.script_sig).len() * 4 + serialize(&txin.witness).len();
×
1100
                        WeightedUtxo {
×
1101
                            satisfaction_weight,
×
1102
                            utxo: Utxo::Foreign {
×
1103
                                outpoint: txin.previous_output,
×
1104
                                psbt_input: Box::new(psbt::Input {
×
1105
                                    witness_utxo: Some(txout.clone()),
×
1106
                                    non_witness_utxo: Some(prev_tx.clone()),
×
1107
                                    ..Default::default()
×
1108
                                }),
×
1109
                            },
×
1110
                        }
×
1111
                    }
1112
                };
1113

1114
                Ok(weighted_utxo)
18✔
1115
            })
18✔
1116
            .collect::<Result<Vec<_>, _>>()?;
17✔
1117

1118
        if tx.output.len() > 1 {
17✔
1119
            let mut change_index = None;
10✔
1120
            for (index, txout) in tx.output.iter().enumerate() {
20✔
1121
                let change_type = self.map_keychain(KeychainKind::Internal);
20✔
1122
                match txout_index.index_of_spk(&txout.script_pubkey) {
20✔
1123
                    Some(&(keychain, _)) if keychain == change_type => change_index = Some(index),
13✔
1124
                    _ => {}
7✔
1125
                }
1126
            }
1127

1128
            if let Some(change_index) = change_index {
10✔
1129
                tx.output.remove(change_index);
10✔
1130
            }
10✔
1131
        }
7✔
1132

1133
        let params = TxParams {
17✔
1134
            // TODO: figure out what rbf option should be?
17✔
1135
            version: Some(tx_builder::Version(tx.version)),
17✔
1136
            recipients: tx
17✔
1137
                .output
17✔
1138
                .into_iter()
17✔
1139
                .map(|txout| (txout.script_pubkey, txout.value))
17✔
1140
                .collect(),
17✔
1141
            utxos: original_utxos,
17✔
1142
            bumping_fee: Some(tx_builder::PreviousFee {
17✔
1143
                absolute: fee,
17✔
1144
                rate: feerate.as_sat_per_vb(),
17✔
1145
            }),
17✔
1146
            ..Default::default()
17✔
1147
        };
17✔
1148

17✔
1149
        Ok(TxBuilder {
17✔
1150
            wallet: alloc::rc::Rc::new(core::cell::RefCell::new(self)),
17✔
1151
            params,
17✔
1152
            coin_selection: DefaultCoinSelectionAlgorithm::default(),
17✔
1153
            phantom: core::marker::PhantomData,
17✔
1154
        })
17✔
1155
    }
19✔
1156

1157
    /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
1158
    /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that has the value true if the PSBT was finalized, or false otherwise.
1159
    ///
1160
    /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
1161
    /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
1162
    /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
1163
    /// in this library will.
1164
    ///
1165
    /// ## Example
1166
    ///
1167
    /// ```
1168
    /// # use std::str::FromStr;
1169
    /// # use bitcoin::*;
1170
    /// # use bdk::*;
1171
    /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1172
    /// # let mut wallet = doctest_wallet!();
1173
    /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap();
1174
    /// let (mut psbt, _) = {
1175
    ///     let mut builder = wallet.build_tx();
1176
    ///     builder.add_recipient(to_address.script_pubkey(), 50_000);
1177
    ///     builder.finish()?
1178
    /// };
1179
    /// let  finalized = wallet.sign(&mut psbt, SignOptions::default())?;
1180
    /// assert!(finalized, "we should have signed all the inputs");
1181
    /// # Ok::<(), bdk::Error>(())
1182
    pub fn sign(
1183
        &self,
1184
        psbt: &mut psbt::PartiallySignedTransaction,
1185
        sign_options: SignOptions,
1186
    ) -> Result<bool, Error> {
1187
        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
1188
        // to derive our keys
1189
        self.update_psbt_with_descriptor(psbt)?;
39✔
1190

1191
        // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
1192
        // has the `non_witness_utxo`
1193
        if !sign_options.trust_witness_utxo
39✔
1194
            && psbt
33✔
1195
                .inputs
33✔
1196
                .iter()
33✔
1197
                .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
34✔
1198
                .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
33✔
1199
                .any(|i| i.non_witness_utxo.is_none())
33✔
1200
        {
1201
            return Err(Error::Signer(signer::SignerError::MissingNonWitnessUtxo));
×
1202
        }
39✔
1203

39✔
1204
        // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
39✔
1205
        // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
39✔
1206
        if !sign_options.allow_all_sighashes
39✔
1207
            && !psbt.inputs.iter().all(|i| {
40✔
1208
                i.sighash_type.is_none()
40✔
1209
                    || i.sighash_type == Some(EcdsaSighashType::All.into())
3✔
1210
                    || i.sighash_type == Some(SchnorrSighashType::All.into())
2✔
1211
                    || i.sighash_type == Some(SchnorrSighashType::Default.into())
2✔
1212
            })
40✔
1213
        {
1214
            return Err(Error::Signer(signer::SignerError::NonStandardSighash));
2✔
1215
        }
37✔
1216

1217
        for signer in self
43✔
1218
            .signers
37✔
1219
            .signers()
37✔
1220
            .iter()
37✔
1221
            .chain(self.change_signers.signers().iter())
37✔
1222
        {
1223
            signer.sign_transaction(psbt, &sign_options, &self.secp)?;
43✔
1224
        }
1225

1226
        // attempt to finalize
1227
        if sign_options.try_finalize {
33✔
1228
            self.finalize_psbt(psbt, sign_options)
31✔
1229
        } else {
1230
            Ok(false)
2✔
1231
        }
1232
    }
39✔
1233

1234
    /// Return the spending policies for the wallet's descriptor
1235
    pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, Error> {
3✔
1236
        let signers = match keychain {
3✔
1237
            KeychainKind::External => &self.signers,
3✔
1238
            KeychainKind::Internal => &self.change_signers,
×
1239
        };
1240

1241
        match self.public_descriptor(keychain) {
3✔
1242
            Some(desc) => Ok(desc.extract_policy(signers, BuildSatisfaction::None, &self.secp)?),
3✔
1243
            None => Ok(None),
×
1244
        }
1245
    }
3✔
1246

1247
    /// Return the "public" version of the wallet's descriptor, meaning a new descriptor that has
1248
    /// the same structure but with every secret key removed
1249
    ///
1250
    /// This can be used to build a watch-only version of a wallet
1251
    pub fn public_descriptor(&self, keychain: KeychainKind) -> Option<&ExtendedDescriptor> {
828✔
1252
        self.indexed_graph.index.keychains().get(&keychain)
828✔
1253
    }
828✔
1254

1255
    /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
1256
    /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
1257
    /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer)
1258
    /// for further information.
1259
    ///
1260
    /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
1261
    ///
1262
    /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
1263
    pub fn finalize_psbt(
31✔
1264
        &self,
31✔
1265
        psbt: &mut psbt::PartiallySignedTransaction,
31✔
1266
        sign_options: SignOptions,
31✔
1267
    ) -> Result<bool, Error> {
31✔
1268
        let chain_tip = self.chain.tip().unwrap_or_default();
31✔
1269

31✔
1270
        let tx = &psbt.unsigned_tx;
31✔
1271
        let mut finished = true;
31✔
1272

1273
        for (n, input) in tx.input.iter().enumerate() {
36✔
1274
            let psbt_input = &psbt
36✔
1275
                .inputs
36✔
1276
                .get(n)
36✔
1277
                .ok_or(Error::Signer(SignerError::InputIndexOutOfRange))?;
36✔
1278
            if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
35✔
1279
                continue;
2✔
1280
            }
33✔
1281
            let confirmation_height = self
33✔
1282
                .indexed_graph
33✔
1283
                .graph()
33✔
1284
                .get_chain_position(&self.chain, chain_tip, input.previous_output.txid)
33✔
1285
                .map(|observed_as| match observed_as {
33✔
1286
                    ChainPosition::Confirmed(a) => a.confirmation_height,
30✔
1287
                    ChainPosition::Unconfirmed(_) => u32::MAX,
×
1288
                });
33✔
1289
            let current_height = sign_options
33✔
1290
                .assume_height
33✔
1291
                .or(self.chain.tip().map(|b| b.height));
33✔
1292

33✔
1293
            debug!(
33✔
1294
                "Input #{} - {}, using `confirmation_height` = {:?}, `current_height` = {:?}",
×
1295
                n, input.previous_output, confirmation_height, current_height
1296
            );
1297

1298
            // - Try to derive the descriptor by looking at the txout. If it's in our database, we
1299
            //   know exactly which `keychain` to use, and which derivation index it is
1300
            // - If that fails, try to derive it by looking at the psbt input: the complete logic
1301
            //   is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
1302
            //   `redeem_script` and `witness_script` to determine the right derivation
1303
            // - If that also fails, it will try it on the internal descriptor, if present
1304
            let desc = psbt
33✔
1305
                .get_utxo_for(n)
33✔
1306
                .and_then(|txout| self.get_descriptor_for_txout(&txout))
33✔
1307
                .or_else(|| {
33✔
1308
                    self.indexed_graph
3✔
1309
                        .index
3✔
1310
                        .keychains()
3✔
1311
                        .iter()
3✔
1312
                        .find_map(|(_, desc)| {
3✔
1313
                            desc.derive_from_psbt_input(
3✔
1314
                                psbt_input,
3✔
1315
                                psbt.get_utxo_for(n),
3✔
1316
                                &self.secp,
3✔
1317
                            )
3✔
1318
                        })
3✔
1319
                });
33✔
1320

33✔
1321
            match desc {
33✔
1322
                Some(desc) => {
31✔
1323
                    let mut tmp_input = bitcoin::TxIn::default();
31✔
1324
                    match desc.satisfy(
31✔
1325
                        &mut tmp_input,
31✔
1326
                        (
31✔
1327
                            PsbtInputSatisfier::new(psbt, n),
31✔
1328
                            After::new(current_height, false),
31✔
1329
                            Older::new(current_height, confirmation_height, false),
31✔
1330
                        ),
31✔
1331
                    ) {
31✔
1332
                        Ok(_) => {
1333
                            let psbt_input = &mut psbt.inputs[n];
30✔
1334
                            psbt_input.final_script_sig = Some(tmp_input.script_sig);
30✔
1335
                            psbt_input.final_script_witness = Some(tmp_input.witness);
30✔
1336
                            if sign_options.remove_partial_sigs {
30✔
1337
                                psbt_input.partial_sigs.clear();
27✔
1338
                            }
27✔
1339
                        }
1340
                        Err(e) => {
1✔
1341
                            debug!("satisfy error {:?} for input {}", e, n);
1✔
1342
                            finished = false
1✔
1343
                        }
1344
                    }
1345
                }
1346
                None => finished = false,
2✔
1347
            }
1348
        }
1349

1350
        Ok(finished)
30✔
1351
    }
31✔
1352

1353
    /// Return the secp256k1 context used for all signing operations
1354
    pub fn secp_ctx(&self) -> &SecpCtx {
9✔
1355
        &self.secp
9✔
1356
    }
9✔
1357

1358
    /// Returns the descriptor used to create addresses for a particular `keychain`.
1359
    pub fn get_descriptor_for_keychain(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
706✔
1360
        self.public_descriptor(self.map_keychain(keychain))
706✔
1361
            .expect("we mapped it to external if it doesn't exist")
706✔
1362
    }
706✔
1363

1364
    /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
1365
    /// Otherwise, it will return the index of the highest address it has derived.
1366
    pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
4✔
1367
        self.indexed_graph.index.last_revealed_index(&keychain)
4✔
1368
    }
4✔
1369

1370
    /// The index of the next address that you would get if you were to ask the wallet for a new address
1371
    pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
×
1372
        self.indexed_graph.index.next_index(&keychain).0
×
1373
    }
×
1374

1375
    /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
1376
    ///
1377
    /// This frees up the change address used when creating the tx for use in future transactions.
1378
    // TODO: Make this free up reserved utxos when that's implemented
1379
    pub fn cancel_tx(&mut self, tx: &Transaction) {
2✔
1380
        let txout_index = &mut self.indexed_graph.index;
2✔
1381
        for txout in &tx.output {
6✔
1382
            if let Some(&(keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
4✔
1383
                // NOTE: unmark_used will **not** make something unused if it has actually been used
2✔
1384
                // by a tx in the tracker. It only removes the superficial marking.
2✔
1385
                txout_index.unmark_used(&keychain, index);
2✔
1386
            }
2✔
1387
        }
1388
    }
2✔
1389

1390
    fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
1,058✔
1391
        if keychain == KeychainKind::Internal
1,058✔
1392
            && self.public_descriptor(KeychainKind::Internal).is_none()
114✔
1393
        {
1394
            KeychainKind::External
99✔
1395
        } else {
1396
            keychain
959✔
1397
        }
1398
    }
1,058✔
1399

1400
    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
33✔
1401
        let &(keychain, child) = self
33✔
1402
            .indexed_graph
33✔
1403
            .index
33✔
1404
            .index_of_spk(&txout.script_pubkey)?;
33✔
1405
        let descriptor = self.get_descriptor_for_keychain(keychain);
30✔
1406
        Some(descriptor.at_derivation_index(child))
30✔
1407
    }
33✔
1408

1409
    fn get_available_utxos(&self) -> Vec<(LocalUtxo, usize)> {
130✔
1410
        self.list_unspent()
130✔
1411
            .map(|utxo| {
144✔
1412
                let keychain = utxo.keychain;
144✔
1413
                (
144✔
1414
                    utxo,
144✔
1415
                    self.get_descriptor_for_keychain(keychain)
144✔
1416
                        .max_satisfaction_weight()
144✔
1417
                        .unwrap(),
144✔
1418
                )
144✔
1419
            })
144✔
1420
            .collect()
130✔
1421
    }
130✔
1422

1423
    /// Given the options returns the list of utxos that must be used to form the
1424
    /// transaction and any further that may be used if needed.
1425
    #[allow(clippy::too_many_arguments)]
1426
    fn preselect_utxos(
130✔
1427
        &self,
130✔
1428
        change_policy: tx_builder::ChangeSpendPolicy,
130✔
1429
        unspendable: &HashSet<OutPoint>,
130✔
1430
        manually_selected: Vec<WeightedUtxo>,
130✔
1431
        must_use_all_available: bool,
130✔
1432
        manual_only: bool,
130✔
1433
        must_only_use_confirmed_tx: bool,
130✔
1434
        current_height: Option<u32>,
130✔
1435
    ) -> (Vec<WeightedUtxo>, Vec<WeightedUtxo>) {
130✔
1436
        let chain_tip = self.chain.tip().unwrap_or_default();
130✔
1437
        //    must_spend <- manually selected utxos
130✔
1438
        //    may_spend  <- all other available utxos
130✔
1439
        let mut may_spend = self.get_available_utxos();
130✔
1440

130✔
1441
        may_spend.retain(|may_spend| {
144✔
1442
            !manually_selected
144✔
1443
                .iter()
144✔
1444
                .any(|manually_selected| manually_selected.utxo.outpoint() == may_spend.0.outpoint)
144✔
1445
        });
144✔
1446
        let mut must_spend = manually_selected;
130✔
1447

130✔
1448
        // NOTE: we are intentionally ignoring `unspendable` here. i.e manual
130✔
1449
        // selection overrides unspendable.
130✔
1450
        if manual_only {
130✔
1451
            return (must_spend, vec![]);
5✔
1452
        }
125✔
1453

125✔
1454
        let satisfies_confirmed = may_spend
125✔
1455
            .iter()
125✔
1456
            .map(|u| -> bool {
130✔
1457
                let txid = u.0.outpoint.txid;
130✔
1458
                let tx = match self.indexed_graph.graph().get_tx(txid) {
130✔
1459
                    Some(tx) => tx,
130✔
1460
                    None => return false,
×
1461
                };
1462
                let confirmation_time: ConfirmationTime = match self
130✔
1463
                    .indexed_graph
130✔
1464
                    .graph()
130✔
1465
                    .get_chain_position(&self.chain, chain_tip, txid)
130✔
1466
                {
1467
                    Some(observed_as) => observed_as.cloned().into(),
130✔
1468
                    None => return false,
×
1469
                };
1470

1471
                // Whether the UTXO is mature and, if needed, confirmed
1472
                let mut spendable = true;
130✔
1473
                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
130✔
1474
                    return false;
8✔
1475
                }
122✔
1476
                if tx.is_coin_base() {
122✔
1477
                    debug_assert!(
1478
                        confirmation_time.is_confirmed(),
3✔
1479
                        "coinbase must always be confirmed"
×
1480
                    );
1481
                    if let Some(current_height) = current_height {
3✔
1482
                        match confirmation_time {
3✔
1483
                            ConfirmationTime::Confirmed { height, .. } => {
3✔
1484
                                // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
3✔
1485
                                spendable &=
3✔
1486
                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
3✔
1487
                            }
3✔
1488
                            ConfirmationTime::Unconfirmed { .. } => spendable = false,
×
1489
                        }
1490
                    }
×
1491
                }
119✔
1492
                spendable
122✔
1493
            })
130✔
1494
            .collect::<Vec<_>>();
125✔
1495

125✔
1496
        let mut i = 0;
125✔
1497
        may_spend.retain(|u| {
130✔
1498
            let retain = change_policy.is_satisfied_by(&u.0)
130✔
1499
                && !unspendable.contains(&u.0.outpoint)
130✔
1500
                && satisfies_confirmed[i];
130✔
1501
            i += 1;
130✔
1502
            retain
130✔
1503
        });
130✔
1504

125✔
1505
        let mut may_spend = may_spend
125✔
1506
            .into_iter()
125✔
1507
            .map(|(local_utxo, satisfaction_weight)| WeightedUtxo {
125✔
1508
                satisfaction_weight,
120✔
1509
                utxo: Utxo::Local(local_utxo),
120✔
1510
            })
125✔
1511
            .collect();
125✔
1512

125✔
1513
        if must_use_all_available {
125✔
1514
            must_spend.append(&mut may_spend);
42✔
1515
        }
83✔
1516

1517
        (must_spend, may_spend)
125✔
1518
    }
130✔
1519

1520
    fn complete_transaction(
121✔
1521
        &self,
121✔
1522
        tx: Transaction,
121✔
1523
        selected: Vec<Utxo>,
121✔
1524
        params: TxParams,
121✔
1525
    ) -> Result<psbt::PartiallySignedTransaction, Error> {
121✔
1526
        let mut psbt = psbt::PartiallySignedTransaction::from_unsigned_tx(tx)?;
121✔
1527

1528
        if params.add_global_xpubs {
121✔
1529
            let all_xpubs = self
3✔
1530
                .keychains()
3✔
1531
                .iter()
3✔
1532
                .flat_map(|(_, desc)| desc.get_extended_keys())
3✔
1533
                .collect::<Vec<_>>();
3✔
1534

1535
            for xpub in all_xpubs {
5✔
1536
                let origin = match xpub.origin {
2✔
1537
                    Some(origin) => origin,
1✔
1538
                    None if xpub.xkey.depth == 0 => {
2✔
1539
                        (xpub.root_fingerprint(&self.secp), vec![].into())
1✔
1540
                    }
1541
                    _ => return Err(Error::MissingKeyOrigin(xpub.xkey.to_string())),
1✔
1542
                };
1543

1544
                psbt.xpub.insert(xpub.xkey, origin);
2✔
1545
            }
1546
        }
118✔
1547

1548
        let mut lookup_output = selected
120✔
1549
            .into_iter()
120✔
1550
            .map(|utxo| (utxo.outpoint(), utxo))
135✔
1551
            .collect::<HashMap<_, _>>();
120✔
1552

1553
        // add metadata for the inputs
1554
        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
135✔
1555
            let utxo = match lookup_output.remove(&input.previous_output) {
135✔
1556
                Some(utxo) => utxo,
135✔
1557
                None => continue,
×
1558
            };
1559

1560
            match utxo {
135✔
1561
                Utxo::Local(utxo) => {
130✔
1562
                    *psbt_input =
130✔
1563
                        match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
130✔
1564
                            Ok(psbt_input) => psbt_input,
130✔
1565
                            Err(e) => match e {
×
1566
                                Error::UnknownUtxo => psbt::Input {
×
1567
                                    sighash_type: params.sighash,
×
1568
                                    ..psbt::Input::default()
×
1569
                                },
×
1570
                                _ => return Err(e),
×
1571
                            },
1572
                        }
1573
                }
1574
                Utxo::Foreign {
1575
                    psbt_input: foreign_psbt_input,
5✔
1576
                    outpoint,
5✔
1577
                } => {
5✔
1578
                    let is_taproot = foreign_psbt_input
5✔
1579
                        .witness_utxo
5✔
1580
                        .as_ref()
5✔
1581
                        .map(|txout| txout.script_pubkey.is_v1_p2tr())
5✔
1582
                        .unwrap_or(false);
5✔
1583
                    if !is_taproot
5✔
1584
                        && !params.only_witness_utxo
4✔
1585
                        && foreign_psbt_input.non_witness_utxo.is_none()
2✔
1586
                    {
1587
                        return Err(Error::Generic(format!(
1✔
1588
                            "Missing non_witness_utxo on foreign utxo {}",
1✔
1589
                            outpoint
1✔
1590
                        )));
1✔
1591
                    }
4✔
1592
                    *psbt_input = *foreign_psbt_input;
4✔
1593
                }
1594
            }
1595
        }
1596

1597
        self.update_psbt_with_descriptor(&mut psbt)?;
119✔
1598

1599
        Ok(psbt)
119✔
1600
    }
121✔
1601

1602
    /// get the corresponding PSBT Input for a LocalUtxo
1603
    pub fn get_psbt_input(
132✔
1604
        &self,
132✔
1605
        utxo: LocalUtxo,
132✔
1606
        sighash_type: Option<psbt::PsbtSighashType>,
132✔
1607
        only_witness_utxo: bool,
132✔
1608
    ) -> Result<psbt::Input, Error> {
132✔
1609
        // Try to find the prev_script in our db to figure out if this is internal or external,
1610
        // and the derivation index
1611
        let &(keychain, child) = self
132✔
1612
            .indexed_graph
132✔
1613
            .index
132✔
1614
            .index_of_spk(&utxo.txout.script_pubkey)
132✔
1615
            .ok_or(Error::UnknownUtxo)?;
132✔
1616

1617
        let mut psbt_input = psbt::Input {
132✔
1618
            sighash_type,
132✔
1619
            ..psbt::Input::default()
132✔
1620
        };
132✔
1621

132✔
1622
        let desc = self.get_descriptor_for_keychain(keychain);
132✔
1623
        let derived_descriptor = desc.at_derivation_index(child);
132✔
1624

132✔
1625
        psbt_input
132✔
1626
            .update_with_descriptor_unchecked(&derived_descriptor)
132✔
1627
            .map_err(MiniscriptPsbtError::Conversion)?;
132✔
1628

1629
        let prev_output = utxo.outpoint;
132✔
1630
        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
132✔
1631
            if desc.is_witness() || desc.is_taproot() {
132✔
1632
                psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
128✔
1633
            }
128✔
1634
            if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
132✔
1635
                psbt_input.non_witness_utxo = Some(prev_tx.clone());
113✔
1636
            }
113✔
1637
        }
×
1638
        Ok(psbt_input)
132✔
1639
    }
132✔
1640

1641
    fn update_psbt_with_descriptor(
158✔
1642
        &self,
158✔
1643
        psbt: &mut psbt::PartiallySignedTransaction,
158✔
1644
    ) -> Result<(), Error> {
158✔
1645
        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
158✔
1646
        // the input utxos and outputs
158✔
1647
        //
158✔
1648
        // Clippy complains that the collect is not required, but that's wrong
158✔
1649
        #[allow(clippy::needless_collect)]
158✔
1650
        let utxos = (0..psbt.inputs.len())
158✔
1651
            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
178✔
1652
            .chain(
158✔
1653
                psbt.unsigned_tx
158✔
1654
                    .output
158✔
1655
                    .iter()
158✔
1656
                    .enumerate()
158✔
1657
                    .map(|(i, out)| (false, i, out.clone())),
247✔
1658
            )
158✔
1659
            .collect::<Vec<_>>();
158✔
1660

1661
        // Try to figure out the keychain and derivation for every input and output
1662
        for (is_input, index, out) in utxos.into_iter() {
419✔
1663
            if let Some(&(keychain, child)) =
358✔
1664
                self.indexed_graph.index.index_of_spk(&out.script_pubkey)
419✔
1665
            {
1666
                debug!(
358✔
1667
                    "Found descriptor for input #{} {:?}/{}",
×
1668
                    index, keychain, child
1669
                );
1670

1671
                let desc = self.get_descriptor_for_keychain(keychain);
358✔
1672
                let desc = desc.at_derivation_index(child);
358✔
1673

358✔
1674
                if is_input {
358✔
1675
                    psbt.update_input_with_descriptor(index, &desc)
164✔
1676
                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
164✔
1677
                } else {
1678
                    psbt.update_output_with_descriptor(index, &desc)
194✔
1679
                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
194✔
1680
                }
1681
            }
61✔
1682
        }
1683

1684
        Ok(())
158✔
1685
    }
158✔
1686

1687
    /// Return the checksum of the public descriptor associated to `keychain`
1688
    ///
1689
    /// Internally calls [`Self::get_descriptor_for_keychain`] to fetch the right descriptor
1690
    pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
1✔
1691
        self.get_descriptor_for_keychain(keychain)
1✔
1692
            .to_string()
1✔
1693
            .split_once('#')
1✔
1694
            .unwrap()
1✔
1695
            .1
1✔
1696
            .to_string()
1✔
1697
    }
1✔
1698

1699
    /// Applies an update to the wallet and stages the changes (but does not [`commit`] them).
1700
    ///
1701
    /// This returns whether the `update` resulted in any changes.
1702
    ///
1703
    /// Usually you create an `update` by interacting with some blockchain data source and inserting
1704
    /// transactions related to your wallet into it.
1705
    ///
1706
    /// [`commit`]: Self::commit
1707
    pub fn apply_update(&mut self, update: Update) -> Result<bool, UpdateNotConnectedError>
×
1708
    where
×
1709
        D: PersistBackend<ChangeSet>,
×
1710
    {
×
1711
        let mut changeset: ChangeSet = self.chain.apply_update(update.chain)?.into();
×
1712
        let (_, index_additions) = self
×
1713
            .indexed_graph
×
1714
            .index
×
1715
            .reveal_to_target_multi(&update.keychain);
×
1716
        changeset.append(ChangeSet::from(IndexedAdditions::from(index_additions)));
×
1717
        changeset.append(self.indexed_graph.apply_update(update.graph).into());
×
1718

×
1719
        let changed = !changeset.is_empty();
×
1720
        self.persist.stage(changeset);
×
1721
        Ok(changed)
×
1722
    }
×
1723

1724
    /// Commits all curently [`staged`] changed to the persistence backend returning and error when
1725
    /// this fails.
1726
    ///
1727
    /// This returns whether the `update` resulted in any changes.
1728
    ///
1729
    /// [`staged`]: Self::staged
1730
    pub fn commit(&mut self) -> Result<bool, D::WriteError>
×
1731
    where
×
1732
        D: PersistBackend<ChangeSet>,
×
1733
    {
×
1734
        self.persist.commit().map(|c| c.is_some())
×
1735
    }
×
1736

1737
    /// Returns the changes that will be staged with the next call to [`commit`].
1738
    ///
1739
    /// [`commit`]: Self::commit
1740
    pub fn staged(&self) -> &ChangeSet
×
1741
    where
×
1742
        D: PersistBackend<ChangeSet>,
×
1743
    {
×
1744
        self.persist.staged()
×
1745
    }
×
1746

1747
    /// Get a reference to the inner [`TxGraph`].
1748
    pub fn tx_graph(&self) -> &TxGraph<ConfirmationTimeAnchor> {
×
1749
        self.indexed_graph.graph()
×
1750
    }
×
1751

1752
    /// Get a reference to the inner [`KeychainTxOutIndex`].
1753
    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
×
1754
        &self.indexed_graph.index
×
1755
    }
×
1756

1757
    /// Get a reference to the inner [`LocalChain`].
1758
    pub fn local_chain(&self) -> &LocalChain {
×
1759
        &self.chain
×
1760
    }
×
1761
}
1762

1763
impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor>> for Wallet<D> {
1764
    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor> {
×
1765
        self.indexed_graph.graph()
×
1766
    }
×
1767
}
1768

1769
/// Deterministically generate a unique name given the descriptors defining the wallet
1770
///
1771
/// Compatible with [`wallet_name_from_descriptor`]
1772
pub fn wallet_name_from_descriptor<T>(
×
1773
    descriptor: T,
×
1774
    change_descriptor: Option<T>,
×
1775
    network: Network,
×
1776
    secp: &SecpCtx,
×
1777
) -> Result<String, Error>
×
1778
where
×
1779
    T: IntoWalletDescriptor,
×
1780
{
×
1781
    //TODO check descriptors contains only public keys
1782
    let descriptor = descriptor
×
1783
        .into_wallet_descriptor(secp, network)?
×
1784
        .0
1785
        .to_string();
×
1786
    let mut wallet_name = calc_checksum(&descriptor[..descriptor.find('#').unwrap()])?;
×
1787
    if let Some(change_descriptor) = change_descriptor {
×
1788
        let change_descriptor = change_descriptor
×
1789
            .into_wallet_descriptor(secp, network)?
×
1790
            .0
1791
            .to_string();
×
1792
        wallet_name.push_str(
×
1793
            calc_checksum(&change_descriptor[..change_descriptor.find('#').unwrap()])?.as_str(),
×
1794
        );
1795
    }
×
1796

1797
    Ok(wallet_name)
×
1798
}
×
1799

1800
fn new_local_utxo(
1,336✔
1801
    keychain: KeychainKind,
1,336✔
1802
    derivation_index: u32,
1,336✔
1803
    full_txo: FullTxOut<ConfirmationTimeAnchor>,
1,336✔
1804
) -> LocalUtxo {
1,336✔
1805
    LocalUtxo {
1,336✔
1806
        outpoint: full_txo.outpoint,
1,336✔
1807
        txout: full_txo.txout,
1,336✔
1808
        is_spent: full_txo.spent_by.is_some(),
1,336✔
1809
        confirmation_time: full_txo.chain_position.into(),
1,336✔
1810
        keychain,
1,336✔
1811
        derivation_index,
1,336✔
1812
    }
1,336✔
1813
}
1,336✔
1814

1815
fn new_tx_details(
32✔
1816
    indexed_graph: &IndexedTxGraph<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>,
32✔
1817
    canonical_tx: CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>,
32✔
1818
    include_raw: bool,
32✔
1819
) -> TransactionDetails {
32✔
1820
    let graph = indexed_graph.graph();
32✔
1821
    let index = &indexed_graph.index;
32✔
1822
    let tx = canonical_tx.node.tx;
32✔
1823

32✔
1824
    let received = tx
32✔
1825
        .output
32✔
1826
        .iter()
32✔
1827
        .map(|txout| {
32✔
1828
            if index.index_of_spk(&txout.script_pubkey).is_some() {
32✔
1829
                txout.value
32✔
1830
            } else {
1831
                0
×
1832
            }
1833
        })
32✔
1834
        .sum();
32✔
1835

32✔
1836
    let sent = tx
32✔
1837
        .input
32✔
1838
        .iter()
32✔
1839
        .map(|txin| {
32✔
1840
            if let Some((_, txout)) = index.txout(txin.previous_output) {
×
1841
                txout.value
×
1842
            } else {
1843
                0
×
1844
            }
1845
        })
32✔
1846
        .sum();
32✔
1847

32✔
1848
    let inputs = tx
32✔
1849
        .input
32✔
1850
        .iter()
32✔
1851
        .map(|txin| {
32✔
1852
            graph
×
1853
                .get_txout(txin.previous_output)
×
1854
                .map(|txout| txout.value)
×
1855
        })
32✔
1856
        .sum::<Option<u64>>();
32✔
1857
    let outputs = tx.output.iter().map(|txout| txout.value).sum();
32✔
1858
    let fee = inputs.map(|inputs| inputs.saturating_sub(outputs));
32✔
1859

32✔
1860
    TransactionDetails {
32✔
1861
        transaction: if include_raw { Some(tx.clone()) } else { None },
32✔
1862
        txid: canonical_tx.node.txid,
32✔
1863
        received,
32✔
1864
        sent,
32✔
1865
        fee,
32✔
1866
        confirmation_time: canonical_tx.observed_as.cloned().into(),
32✔
1867
    }
32✔
1868
}
32✔
1869

1870
#[macro_export]
1871
#[doc(hidden)]
1872
/// Macro for getting a wallet for use in a doctest
1873
macro_rules! doctest_wallet {
1874
    () => {{
1875
        use $crate::bitcoin::{BlockHash, Transaction, PackedLockTime, TxOut, Network, hashes::Hash};
1876
        use $crate::chain::{ConfirmationTime, BlockId};
1877
        use $crate::wallet::{AddressIndex, Wallet};
1878
        let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
1879
        let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
1880

1881
        let mut wallet = Wallet::new_no_persist(
1882
            descriptor,
1883
            Some(change_descriptor),
1884
            Network::Regtest,
1885
        )
1886
        .unwrap();
1887
        let address = wallet.get_address(AddressIndex::New).address;
1888
        let tx = Transaction {
1889
            version: 1,
1890
            lock_time: PackedLockTime(0),
1891
            input: vec![],
1892
            output: vec![TxOut {
1893
                value: 500_000,
1894
                script_pubkey: address.script_pubkey(),
1895
            }],
1896
        };
1897
        let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() });
1898
        let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed {
1899
            height: 500,
1900
            time: 50_000
1901
        });
1902

1903
        wallet
1904
    }}
1905
}
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