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

bitcoindevkit / bdk / 5398026791

pending completion
5398026791

Pull #1002

github

web-flow
Merge 8272aa35d into 26ade1172
Pull Request #1002: Implement linked-list `LocalChain` and add rpc-chain module/example

681 of 681 new or added lines in 9 files covered. (100.0%)

7903 of 10220 relevant lines covered (77.33%)

5078.37 hits per line

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

87.03
/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, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
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, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence,
36
    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 {
216✔
146
        write!(f, "{}", self.address)
216✔
147
    }
216✔
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> fmt::Display for NewError<P>
174
where
175
    P: 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)> {
6✔
364
        self.indexed_graph.index.index_of_spk(spk).copied()
6✔
365
    }
6✔
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().map(|cp| cp.block_id()).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) -> CheckPointIter {
×
381
        self.chain.iter_checkpoints(None)
×
382
    }
×
383

384
    /// Returns the latest checkpoint.
385
    pub fn latest_checkpoint(&self) -> Option<CheckPoint> {
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().map(|cp| cp.block_id()).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().map(|cp| cp.block_id()).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::InsertBlockError>
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
                    .checkpoints()
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(|(&_, cp)| ConfirmationTimeAnchor {
138✔
511
                        anchor_block: cp.block_id(),
138✔
512
                        confirmation_height: height,
138✔
513
                        confirmation_time: time,
138✔
514
                    })?;
138✔
515

516
                (Some(anchor), None)
138✔
517
            }
518
            ConfirmationTime::Unconfirmed { last_seen } => (None, Some(last_seen)),
23✔
519
        };
520

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

728
                // We choose the biggest between the required nlocktime and the fee sniping
729
                // height
730
                match requirements.timelock {
4✔
731
                    // No requirement, just use the fee_sniping_height
732
                    None => fee_sniping_height,
132✔
733
                    // There's a block-based requirement, but the value is lower than the fee_sniping_height
734
                    Some(value @ LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height,
4✔
735
                    // There's a time-based requirement or a block-based requirement greater
736
                    // than the fee_sniping_height use that value
737
                    Some(value) => value,
4✔
738
                }
739
            }
740
            // Specific nLockTime required and we have no constraints, so just set to that value
741
            Some(x) if requirements.timelock.is_none() => x,
1✔
742
            // Specific nLockTime required and it's compatible with the constraints
743
            Some(x) if requirements.timelock.unwrap().is_same_unit(x) && x >= requirements.timelock.unwrap() => x,
2✔
744
            // Invalid nLockTime required
745
            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✔
746
        };
747

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

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

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

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

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

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

133✔
818
        if params.manually_selected_only && params.utxos.is_empty() {
133✔
819
            return Err(Error::NoUtxosSelected);
1✔
820
        }
132✔
821

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

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

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

83✔
836
            if self.is_mine(script_pubkey) {
83✔
837
                received += value;
43✔
838
            }
44✔
839

840
            let new_out = TxOut {
83✔
841
                script_pubkey: script_pubkey.clone(),
83✔
842
                value,
83✔
843
            };
83✔
844

83✔
845
            tx.output.push(new_out);
83✔
846

83✔
847
            outgoing += value;
83✔
848
        }
849

850
        fee_amount += fee_rate.fee_wu(tx.weight());
131✔
851

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

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

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

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

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

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

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

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

118✔
953
                // create drain output
118✔
954
                let drain_output = TxOut {
118✔
955
                    value: *amount,
118✔
956
                    script_pubkey: drain_script,
118✔
957
                };
118✔
958

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

966
        // sort input/outputs according to the chosen algorithm
967
        params.ordering.sort_tx(&mut tx);
121✔
968

121✔
969
        let txid = tx.txid();
121✔
970
        let sent = coin_selection.local_selected_amount();
121✔
971
        let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
121✔
972

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

119✔
982
        Ok((psbt, transaction_details))
119✔
983
    }
142✔
984

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

1032
        let mut tx = graph
19✔
1033
            .get_tx(txid)
19✔
1034
            .ok_or(Error::TransactionNotFound)?
19✔
1035
            .clone();
19✔
1036

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

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

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

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

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

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

1111
                Ok(weighted_utxo)
18✔
1112
            })
18✔
1113
            .collect::<Result<Vec<_>, _>>()?;
17✔
1114

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

1125
            if let Some(change_index) = change_index {
10✔
1126
                tx.output.remove(change_index);
10✔
1127
            }
10✔
1128
        }
7✔
1129

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

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

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

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

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

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

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

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

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

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

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

31✔
1267
        let tx = &psbt.unsigned_tx;
31✔
1268
        let mut finished = true;
31✔
1269

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

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

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

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

1347
        Ok(finished)
30✔
1348
    }
31✔
1349

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

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

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

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

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

1387
    fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
1,057✔
1388
        if keychain == KeychainKind::Internal
1,057✔
1389
            && self.public_descriptor(KeychainKind::Internal).is_none()
114✔
1390
        {
1391
            KeychainKind::External
99✔
1392
        } else {
1393
            keychain
958✔
1394
        }
1395
    }
1,057✔
1396

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

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

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

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

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

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

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

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

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

125✔
1510
        if must_use_all_available {
125✔
1511
            must_spend.append(&mut may_spend);
42✔
1512
        }
83✔
1513

1514
        (must_spend, may_spend)
125✔
1515
    }
130✔
1516

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

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

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

1541
                psbt.xpub.insert(xpub.xkey, origin);
2✔
1542
            }
1543
        }
118✔
1544

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

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

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

1594
        self.update_psbt_with_descriptor(&mut psbt)?;
119✔
1595

1596
        Ok(psbt)
119✔
1597
    }
121✔
1598

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

1614
        let mut psbt_input = psbt::Input {
131✔
1615
            sighash_type,
131✔
1616
            ..psbt::Input::default()
131✔
1617
        };
131✔
1618

131✔
1619
        let desc = self.get_descriptor_for_keychain(keychain);
131✔
1620
        let derived_descriptor = desc.at_derivation_index(child);
131✔
1621

131✔
1622
        psbt_input
131✔
1623
            .update_with_descriptor_unchecked(&derived_descriptor)
131✔
1624
            .map_err(MiniscriptPsbtError::Conversion)?;
131✔
1625

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

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

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

1668
                let desc = self.get_descriptor_for_keychain(keychain);
358✔
1669
                let desc = desc.at_derivation_index(child);
358✔
1670

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

1681
        Ok(())
158✔
1682
    }
158✔
1683

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

1696
    /// Applies an update to the wallet and stages the changes (but does not [`commit`] them).
1697
    /// Returns whether the `update` resulted in any changes.
1698
    ///
1699
    /// If `prune` is set, irrelevant transactions are pruned. Relevant transactions change the UTXO
1700
    /// set of tracked script pubkeys (script pubkeys derived from tracked descriptors).
1701
    ///
1702
    /// Usually you create an `update` by interacting with some blockchain data source and inserting
1703
    /// transactions related to your wallet into it.
1704
    ///
1705
    /// [`commit`]: Self::commit
1706
    pub fn apply_update(&mut self, update: Update, prune: bool) -> Result<bool, CannotConnectError>
×
1707
    where
×
1708
        D: PersistBackend<ChangeSet>,
×
1709
    {
×
1710
        let mut changeset = ChangeSet::from(self.chain.update(update.tip)?);
×
1711
        let (_, index_additions) = self
×
1712
            .indexed_graph
×
1713
            .index
×
1714
            .reveal_to_target_multi(&update.keychain);
×
1715
        changeset.append(ChangeSet::from(IndexedAdditions::from(index_additions)));
×
1716
        changeset.append(
×
1717
            if prune {
×
1718
                self.indexed_graph.prune_and_apply_update(update.graph)
×
1719
            } else {
1720
                self.indexed_graph.apply_update(update.graph)
×
1721
            }
1722
            .into(),
×
1723
        );
×
1724

×
1725
        let changed = !changeset.is_empty();
×
1726
        self.persist.stage(changeset);
×
1727
        Ok(changed)
×
1728
    }
×
1729

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

1743
    /// Returns the changes that will be staged with the next call to [`commit`].
1744
    ///
1745
    /// [`commit`]: Self::commit
1746
    pub fn staged(&self) -> &ChangeSet
×
1747
    where
×
1748
        D: PersistBackend<ChangeSet>,
×
1749
    {
×
1750
        self.persist.staged()
×
1751
    }
×
1752

1753
    /// Get a reference to the inner [`TxGraph`].
1754
    pub fn tx_graph(&self) -> &TxGraph<ConfirmationTimeAnchor> {
×
1755
        self.indexed_graph.graph()
×
1756
    }
×
1757

1758
    /// Get a reference to the inner [`KeychainTxOutIndex`].
1759
    pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
×
1760
        &self.indexed_graph.index
×
1761
    }
×
1762

1763
    /// Get a reference to the inner [`LocalChain`].
1764
    pub fn local_chain(&self) -> &LocalChain {
×
1765
        &self.chain
×
1766
    }
×
1767
}
1768

1769
impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor>> for Wallet<D> {
1770
    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor> {
×
1771
        self.indexed_graph.graph()
×
1772
    }
×
1773
}
1774

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

1803
    Ok(wallet_name)
×
1804
}
×
1805

1806
fn new_local_utxo(
1,328✔
1807
    keychain: KeychainKind,
1,328✔
1808
    derivation_index: u32,
1,328✔
1809
    full_txo: FullTxOut<ConfirmationTimeAnchor>,
1,328✔
1810
) -> LocalUtxo {
1,328✔
1811
    LocalUtxo {
1,328✔
1812
        outpoint: full_txo.outpoint,
1,328✔
1813
        txout: full_txo.txout,
1,328✔
1814
        is_spent: full_txo.spent_by.is_some(),
1,328✔
1815
        confirmation_time: full_txo.chain_position.into(),
1,328✔
1816
        keychain,
1,328✔
1817
        derivation_index,
1,328✔
1818
    }
1,328✔
1819
}
1,328✔
1820

1821
fn new_tx_details(
32✔
1822
    indexed_graph: &IndexedTxGraph<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>,
32✔
1823
    canonical_tx: CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>,
32✔
1824
    include_raw: bool,
32✔
1825
) -> TransactionDetails {
32✔
1826
    let graph = indexed_graph.graph();
32✔
1827
    let index = &indexed_graph.index;
32✔
1828
    let tx = canonical_tx.node.tx;
32✔
1829

32✔
1830
    let received = tx
32✔
1831
        .output
32✔
1832
        .iter()
32✔
1833
        .map(|txout| {
32✔
1834
            if index.index_of_spk(&txout.script_pubkey).is_some() {
32✔
1835
                txout.value
32✔
1836
            } else {
1837
                0
×
1838
            }
1839
        })
32✔
1840
        .sum();
32✔
1841

32✔
1842
    let sent = tx
32✔
1843
        .input
32✔
1844
        .iter()
32✔
1845
        .map(|txin| {
32✔
1846
            if let Some((_, txout)) = index.txout(txin.previous_output) {
×
1847
                txout.value
×
1848
            } else {
1849
                0
×
1850
            }
1851
        })
32✔
1852
        .sum();
32✔
1853

32✔
1854
    let inputs = tx
32✔
1855
        .input
32✔
1856
        .iter()
32✔
1857
        .map(|txin| {
32✔
1858
            graph
×
1859
                .get_txout(txin.previous_output)
×
1860
                .map(|txout| txout.value)
×
1861
        })
32✔
1862
        .sum::<Option<u64>>();
32✔
1863
    let outputs = tx.output.iter().map(|txout| txout.value).sum();
32✔
1864
    let fee = inputs.map(|inputs| inputs.saturating_sub(outputs));
32✔
1865

32✔
1866
    TransactionDetails {
32✔
1867
        transaction: if include_raw { Some(tx.clone()) } else { None },
32✔
1868
        txid: canonical_tx.node.txid,
32✔
1869
        received,
32✔
1870
        sent,
32✔
1871
        fee,
32✔
1872
        confirmation_time: canonical_tx.observed_as.cloned().into(),
32✔
1873
    }
32✔
1874
}
32✔
1875

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

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

1909
        wallet
1910
    }}
1911
}
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