bdk_wallet/wallet/mod.rs
1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12//! Wallet
13//!
14//! This module defines the [`Wallet`].
15
16use alloc::{
17 boxed::Box,
18 string::{String, ToString},
19 sync::Arc,
20 vec::Vec,
21};
22use core::{cmp::Ordering, fmt, mem, ops::Deref};
23
24use bdk_chain::{
25 indexed_tx_graph,
26 indexer::keychain_txout::KeychainTxOutIndex,
27 local_chain::{ApplyHeaderError, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
28 spk_client::{
29 FullScanRequest, FullScanRequestBuilder, FullScanResponse, SyncRequest, SyncRequestBuilder,
30 SyncResponse,
31 },
32 tx_graph::{CalculateFeeError, CanonicalTx, TxGraph, TxUpdate},
33 BlockId, CanonicalizationParams, ChainPosition, ConfirmationBlockTime, DescriptorExt,
34 FullTxOut, Indexed, IndexedTxGraph, Indexer, Merge,
35};
36use bitcoin::{
37 absolute,
38 consensus::encode::serialize,
39 constants::genesis_block,
40 psbt,
41 secp256k1::Secp256k1,
42 sighash::{EcdsaSighashType, TapSighashType},
43 transaction, Address, Amount, Block, BlockHash, FeeRate, Network, OutPoint, Psbt, ScriptBuf,
44 Sequence, SignedAmount, Transaction, TxOut, Txid, Weight, Witness,
45};
46use miniscript::{
47 descriptor::KeyMap,
48 psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier},
49};
50use rand_core::RngCore;
51
52mod changeset;
53pub mod coin_selection;
54pub mod error;
55pub mod export;
56mod params;
57mod persisted;
58pub mod signer;
59pub mod tx_builder;
60pub(crate) mod utils;
61
62use crate::collections::{BTreeMap, HashMap, HashSet};
63use crate::descriptor::{
64 check_wallet_descriptor, error::Error as DescriptorError, policy::BuildSatisfaction,
65 DerivedDescriptor, DescriptorMeta, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
66 Policy, XKeyUtils,
67};
68use crate::psbt::PsbtUtils;
69use crate::types::*;
70use crate::wallet::{
71 coin_selection::{DefaultCoinSelectionAlgorithm, Excess, InsufficientFunds},
72 error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError},
73 signer::{SignOptions, SignerError, SignerOrdering, SignersContainer, TransactionSigner},
74 tx_builder::{FeePolicy, TxBuilder, TxParams},
75 utils::{check_nsequence_rbf, After, Older, SecpCtx},
76};
77
78// re-exports
79pub use bdk_chain::Balance;
80pub use changeset::ChangeSet;
81pub use params::*;
82pub use persisted::*;
83pub use utils::IsDust;
84pub use utils::TxDetails;
85
86/// A Bitcoin wallet
87///
88/// The `Wallet` acts as a way of coherently interfacing with output descriptors and related
89/// transactions. Its main components are:
90///
91/// 1. output *descriptors* from which it can derive addresses.
92/// 2. [`signer`]s that can contribute signatures to addresses instantiated from the descriptors.
93///
94/// The user is responsible for loading and writing wallet changes which are represented as
95/// [`ChangeSet`]s (see [`take_staged`]). Also see individual functions and example for instructions
96/// on when [`Wallet`] state needs to be persisted.
97///
98/// The `Wallet` descriptor (external) and change descriptor (internal) must not derive the same
99/// script pubkeys. See [`KeychainTxOutIndex::insert_descriptor()`] for more details.
100///
101/// [`signer`]: crate::signer
102/// [`take_staged`]: Wallet::take_staged
103#[derive(Debug)]
104pub struct Wallet {
105 signers: Arc<SignersContainer>,
106 change_signers: Arc<SignersContainer>,
107 chain: LocalChain,
108 indexed_graph: IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>,
109 stage: ChangeSet,
110 network: Network,
111 secp: SecpCtx,
112}
113
114/// An update to [`Wallet`].
115///
116/// It updates [`KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`LocalChain`] atomically.
117#[derive(Debug, Clone, Default)]
118pub struct Update {
119 /// Contains the last active derivation indices per keychain (`K`), which is used to update the
120 /// [`KeychainTxOutIndex`].
121 pub last_active_indices: BTreeMap<KeychainKind, u32>,
122
123 /// Update for the wallet's internal [`TxGraph`].
124 pub tx_update: TxUpdate<ConfirmationBlockTime>,
125
126 /// Update for the wallet's internal [`LocalChain`].
127 pub chain: Option<CheckPoint>,
128}
129
130impl From<FullScanResponse<KeychainKind>> for Update {
131 fn from(value: FullScanResponse<KeychainKind>) -> Self {
132 Self {
133 last_active_indices: value.last_active_indices,
134 tx_update: value.tx_update,
135 chain: value.chain_update,
136 }
137 }
138}
139
140impl From<SyncResponse> for Update {
141 fn from(value: SyncResponse) -> Self {
142 Self {
143 last_active_indices: BTreeMap::new(),
144 tx_update: value.tx_update,
145 chain: value.chain_update,
146 }
147 }
148}
149
150/// A derived address and the index it was found at.
151/// For convenience this automatically derefs to `Address`
152#[derive(Debug, Clone, PartialEq, Eq)]
153pub struct AddressInfo {
154 /// Child index of this address
155 pub index: u32,
156 /// Address
157 pub address: Address,
158 /// Type of keychain
159 pub keychain: KeychainKind,
160}
161
162impl Deref for AddressInfo {
163 type Target = Address;
164
165 fn deref(&self) -> &Self::Target {
166 &self.address
167 }
168}
169
170impl fmt::Display for AddressInfo {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "{}", self.address)
173 }
174}
175
176/// The error type when loading a [`Wallet`] from a [`ChangeSet`].
177#[derive(Debug, PartialEq)]
178pub enum LoadError {
179 /// There was a problem with the passed-in descriptor(s).
180 Descriptor(crate::descriptor::DescriptorError),
181 /// Data loaded from persistence is missing network type.
182 MissingNetwork,
183 /// Data loaded from persistence is missing genesis hash.
184 MissingGenesis,
185 /// Data loaded from persistence is missing descriptor.
186 MissingDescriptor(KeychainKind),
187 /// Data loaded is unexpected.
188 Mismatch(LoadMismatch),
189}
190
191impl fmt::Display for LoadError {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match self {
194 LoadError::Descriptor(e) => e.fmt(f),
195 LoadError::MissingNetwork => write!(f, "loaded data is missing network type"),
196 LoadError::MissingGenesis => write!(f, "loaded data is missing genesis hash"),
197 LoadError::MissingDescriptor(k) => {
198 write!(f, "loaded data is missing descriptor for {k} keychain")
199 }
200 LoadError::Mismatch(e) => write!(f, "{e}"),
201 }
202 }
203}
204
205#[cfg(feature = "std")]
206impl std::error::Error for LoadError {}
207
208/// Represents a mismatch with what is loaded and what is expected from [`LoadParams`].
209#[derive(Debug, PartialEq)]
210pub enum LoadMismatch {
211 /// Network does not match.
212 Network {
213 /// The network that is loaded.
214 loaded: Network,
215 /// The expected network.
216 expected: Network,
217 },
218 /// Genesis hash does not match.
219 Genesis {
220 /// The genesis hash that is loaded.
221 loaded: BlockHash,
222 /// The expected genesis hash.
223 expected: BlockHash,
224 },
225 /// Descriptor's [`DescriptorId`](bdk_chain::DescriptorId) does not match.
226 Descriptor {
227 /// Keychain identifying the descriptor.
228 keychain: KeychainKind,
229 /// The loaded descriptor.
230 loaded: Option<ExtendedDescriptor>,
231 /// The expected descriptor.
232 expected: Option<ExtendedDescriptor>,
233 },
234}
235
236impl fmt::Display for LoadMismatch {
237 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 match self {
239 LoadMismatch::Network { loaded, expected } => {
240 write!(f, "Network mismatch: loaded {loaded}, expected {expected}")
241 }
242 LoadMismatch::Genesis { loaded, expected } => {
243 write!(
244 f,
245 "Genesis hash mismatch: loaded {loaded}, expected {expected}"
246 )
247 }
248 LoadMismatch::Descriptor {
249 keychain,
250 loaded,
251 expected,
252 } => {
253 write!(
254 f,
255 "Descriptor mismatch for {} keychain: loaded {}, expected {}",
256 keychain,
257 loaded
258 .as_ref()
259 .map_or("None".to_string(), |d| d.to_string()),
260 expected
261 .as_ref()
262 .map_or("None".to_string(), |d| d.to_string())
263 )
264 }
265 }
266 }
267}
268
269impl From<LoadMismatch> for LoadError {
270 fn from(mismatch: LoadMismatch) -> Self {
271 Self::Mismatch(mismatch)
272 }
273}
274
275impl<E> From<LoadMismatch> for LoadWithPersistError<E> {
276 fn from(mismatch: LoadMismatch) -> Self {
277 Self::InvalidChangeSet(LoadError::Mismatch(mismatch))
278 }
279}
280
281/// An error that may occur when applying a block to [`Wallet`].
282#[derive(Debug)]
283pub enum ApplyBlockError {
284 /// Occurs when the update chain cannot connect with original chain.
285 CannotConnect(CannotConnectError),
286 /// Occurs when the `connected_to` hash does not match the hash derived from `block`.
287 UnexpectedConnectedToHash {
288 /// Block hash of `connected_to`.
289 connected_to_hash: BlockHash,
290 /// Expected block hash of `connected_to`, as derived from `block`.
291 expected_hash: BlockHash,
292 },
293}
294
295impl fmt::Display for ApplyBlockError {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 match self {
298 ApplyBlockError::CannotConnect(err) => err.fmt(f),
299 ApplyBlockError::UnexpectedConnectedToHash {
300 expected_hash: block_hash,
301 connected_to_hash: checkpoint_hash,
302 } => write!(
303 f,
304 "`connected_to` hash {checkpoint_hash} differs from the expected hash {block_hash} (which is derived from `block`)"
305 ),
306 }
307 }
308}
309
310#[cfg(feature = "std")]
311impl std::error::Error for ApplyBlockError {}
312
313/// A `CanonicalTx` managed by a `Wallet`.
314pub type WalletTx<'a> = CanonicalTx<'a, Arc<Transaction>, ConfirmationBlockTime>;
315
316impl Wallet {
317 /// Build a new single descriptor [`Wallet`].
318 ///
319 /// If you have previously created a wallet, use [`load`](Self::load) instead.
320 ///
321 /// # Note
322 ///
323 /// Only use this method when creating a wallet designed to be used with a single
324 /// descriptor and keychain. Otherwise the recommended way to construct a new wallet is
325 /// by using [`Wallet::create`]. It's worth noting that not all features are available
326 /// with single descriptor wallets, for example setting a [`change_policy`] on [`TxBuilder`]
327 /// and related methods such as [`do_not_spend_change`]. This is because all payments are
328 /// received on the external keychain (including change), and without a change keychain
329 /// BDK lacks enough information to distinguish between change and outside payments.
330 ///
331 /// Additionally because this wallet has no internal (change) keychain, all methods that
332 /// require a [`KeychainKind`] as input, e.g. [`reveal_next_address`] should only be called
333 /// using the [`External`] variant. In most cases passing [`Internal`] is treated as the
334 /// equivalent of [`External`] but this behavior must not be relied on.
335 ///
336 /// # Example
337 ///
338 /// ```rust
339 /// # use bdk_wallet::Wallet;
340 /// # use bitcoin::Network;
341 /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
342 /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
343 /// # let file_path = temp_dir.path().join("store.db");
344 /// // Create a wallet that is persisted to SQLite database.
345 /// use bdk_wallet::rusqlite::Connection;
346 /// let mut conn = Connection::open(file_path)?;
347 /// let wallet = Wallet::create_single(EXTERNAL_DESC)
348 /// .network(Network::Testnet)
349 /// .create_wallet(&mut conn)?;
350 /// # Ok::<_, anyhow::Error>(())
351 /// ```
352 /// [`change_policy`]: TxBuilder::change_policy
353 /// [`do_not_spend_change`]: TxBuilder::do_not_spend_change
354 /// [`External`]: KeychainKind::External
355 /// [`Internal`]: KeychainKind::Internal
356 /// [`reveal_next_address`]: Self::reveal_next_address
357 pub fn create_single<D>(descriptor: D) -> CreateParams
358 where
359 D: IntoWalletDescriptor + Send + Clone + 'static,
360 {
361 CreateParams::new_single(descriptor)
362 }
363
364 /// Build a new [`Wallet`].
365 ///
366 /// If you have previously created a wallet, use [`load`](Self::load) instead.
367 ///
368 /// # Synopsis
369 ///
370 /// ```rust
371 /// # use bdk_wallet::Wallet;
372 /// # use bitcoin::Network;
373 /// # fn main() -> anyhow::Result<()> {
374 /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
375 /// # const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
376 /// // Create a non-persisted wallet.
377 /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
378 /// .network(Network::Testnet)
379 /// .create_wallet_no_persist()?;
380 ///
381 /// // Create a wallet that is persisted to SQLite database.
382 /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
383 /// # let file_path = temp_dir.path().join("store.db");
384 /// use bdk_wallet::rusqlite::Connection;
385 /// let mut conn = Connection::open(file_path)?;
386 /// let wallet = Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
387 /// .network(Network::Testnet)
388 /// .create_wallet(&mut conn)?;
389 /// # Ok(())
390 /// # }
391 /// ```
392 pub fn create<D>(descriptor: D, change_descriptor: D) -> CreateParams
393 where
394 D: IntoWalletDescriptor + Send + Clone + 'static,
395 {
396 CreateParams::new(descriptor, change_descriptor)
397 }
398
399 /// Build a new [`Wallet`] from a two-path descriptor.
400 ///
401 /// This function parses a multipath descriptor with exactly 2 paths and creates a wallet
402 /// using the existing receive and change wallet creation logic.
403 ///
404 /// Multipath descriptors follow [BIP 389] and allow defining both receive and change
405 /// derivation paths in a single descriptor using the `<0;1>` syntax.
406 ///
407 /// If you have previously created a wallet, use [`load`](Self::load) instead.
408 ///
409 /// # Errors
410 /// Returns an error if the descriptor is invalid or not a 2-path multipath descriptor.
411 ///
412 /// # Synopsis
413 ///
414 /// ```rust
415 /// # use bdk_wallet::Wallet;
416 /// # use bitcoin::Network;
417 /// # use bdk_wallet::KeychainKind;
418 /// # const TWO_PATH_DESC: &str = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
419 /// let wallet = Wallet::create_from_two_path_descriptor(TWO_PATH_DESC)
420 /// .network(Network::Testnet)
421 /// .create_wallet_no_persist()
422 /// .unwrap();
423 ///
424 /// // The multipath descriptor automatically creates separate receive and change descriptors
425 /// let receive_addr = wallet.peek_address(KeychainKind::External, 0); // Uses path /0/*
426 /// let change_addr = wallet.peek_address(KeychainKind::Internal, 0); // Uses path /1/*
427 /// assert_ne!(receive_addr.address, change_addr.address);
428 /// ```
429 ///
430 /// [BIP 389]: https://github.com/bitcoin/bips/blob/master/bip-0389.mediawiki
431 pub fn create_from_two_path_descriptor<D>(two_path_descriptor: D) -> CreateParams
432 where
433 D: IntoWalletDescriptor + Send + Clone + 'static,
434 {
435 CreateParams::new_two_path(two_path_descriptor)
436 }
437
438 /// Create a new [`Wallet`] with given `params`.
439 ///
440 /// Refer to [`Wallet::create`] for more.
441 pub fn create_with_params(params: CreateParams) -> Result<Self, DescriptorError> {
442 let secp = SecpCtx::new();
443 let network = params.network;
444 let genesis_hash = params
445 .genesis_hash
446 .unwrap_or(genesis_block(network).block_hash());
447 let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
448
449 let (descriptor, mut descriptor_keymap) = (params.descriptor)(&secp, network)?;
450 check_wallet_descriptor(&descriptor)?;
451 descriptor_keymap.extend(params.descriptor_keymap);
452
453 let signers = Arc::new(SignersContainer::build(
454 descriptor_keymap,
455 &descriptor,
456 &secp,
457 ));
458
459 let (change_descriptor, change_signers) = match params.change_descriptor {
460 Some(make_desc) => {
461 let (change_descriptor, mut internal_keymap) = make_desc(&secp, network)?;
462 check_wallet_descriptor(&change_descriptor)?;
463 internal_keymap.extend(params.change_descriptor_keymap);
464 let change_signers = Arc::new(SignersContainer::build(
465 internal_keymap,
466 &change_descriptor,
467 &secp,
468 ));
469 (Some(change_descriptor), change_signers)
470 }
471 None => (None, Arc::new(SignersContainer::new())),
472 };
473
474 let mut stage = ChangeSet {
475 descriptor: Some(descriptor.clone()),
476 change_descriptor: change_descriptor.clone(),
477 local_chain: chain_changeset,
478 network: Some(network),
479 ..Default::default()
480 };
481
482 let indexed_graph = make_indexed_graph(
483 &mut stage,
484 Default::default(),
485 Default::default(),
486 descriptor,
487 change_descriptor,
488 params.lookahead,
489 params.use_spk_cache,
490 )?;
491
492 Ok(Wallet {
493 signers,
494 change_signers,
495 network,
496 chain,
497 indexed_graph,
498 stage,
499 secp,
500 })
501 }
502
503 /// Build [`Wallet`] by loading from persistence or [`ChangeSet`].
504 ///
505 /// Note that the descriptor secret keys are not persisted to the db. You can add
506 /// signers after-the-fact with [`Wallet::add_signer`] or [`Wallet::set_keymap`]. You
507 /// can also add keys when building the wallet by using [`LoadParams::keymap`]. Finally
508 /// you can check the wallet's descriptors are what you expect with [`LoadParams::descriptor`]
509 /// which will try to populate signers if [`LoadParams::extract_keys`] is enabled.
510 ///
511 /// # Synopsis
512 ///
513 /// ```rust,no_run
514 /// # use bdk_wallet::{Wallet, ChangeSet, KeychainKind};
515 /// # use bitcoin::{BlockHash, Network, hashes::Hash};
516 /// # fn main() -> anyhow::Result<()> {
517 /// # const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
518 /// # const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
519 /// # let changeset = ChangeSet::default();
520 /// // Load a wallet from changeset (no persistence).
521 /// let wallet = Wallet::load()
522 /// .load_wallet_no_persist(changeset)?
523 /// .expect("must have data to load wallet");
524 ///
525 /// // Load a wallet that is persisted to SQLite database.
526 /// # let temp_dir = tempfile::tempdir().expect("must create tempdir");
527 /// # let file_path = temp_dir.path().join("store.db");
528 /// # let external_keymap = Default::default();
529 /// # let internal_keymap = Default::default();
530 /// # let genesis_hash = BlockHash::all_zeros();
531 /// let mut conn = bdk_wallet::rusqlite::Connection::open(file_path)?;
532 /// let mut wallet = Wallet::load()
533 /// // check loaded descriptors matches these values and extract private keys
534 /// .descriptor(KeychainKind::External, Some(EXTERNAL_DESC))
535 /// .descriptor(KeychainKind::Internal, Some(INTERNAL_DESC))
536 /// .extract_keys()
537 /// // you can also manually add private keys
538 /// .keymap(KeychainKind::External, external_keymap)
539 /// .keymap(KeychainKind::Internal, internal_keymap)
540 /// // ensure loaded wallet's genesis hash matches this value
541 /// .check_genesis_hash(genesis_hash)
542 /// // set a lookahead for our indexer
543 /// .lookahead(101)
544 /// .load_wallet(&mut conn)?
545 /// .expect("must have data to load wallet");
546 /// # Ok(())
547 /// # }
548 /// ```
549 pub fn load() -> LoadParams {
550 LoadParams::new()
551 }
552
553 /// Load [`Wallet`] from the given previously persisted [`ChangeSet`] and `params`.
554 ///
555 /// Returns `Ok(None)` if the changeset is empty. Refer to [`Wallet::load`] for more.
556 pub fn load_with_params(
557 changeset: ChangeSet,
558 params: LoadParams,
559 ) -> Result<Option<Self>, LoadError> {
560 if changeset.is_empty() {
561 return Ok(None);
562 }
563 let secp = Secp256k1::new();
564 let network = changeset.network.ok_or(LoadError::MissingNetwork)?;
565 let chain = LocalChain::from_changeset(changeset.local_chain)
566 .map_err(|_| LoadError::MissingGenesis)?;
567
568 if let Some(exp_network) = params.check_network {
569 if network != exp_network {
570 return Err(LoadError::Mismatch(LoadMismatch::Network {
571 loaded: network,
572 expected: exp_network,
573 }));
574 }
575 }
576 if let Some(exp_genesis_hash) = params.check_genesis_hash {
577 if chain.genesis_hash() != exp_genesis_hash {
578 return Err(LoadError::Mismatch(LoadMismatch::Genesis {
579 loaded: chain.genesis_hash(),
580 expected: exp_genesis_hash,
581 }));
582 }
583 }
584
585 let descriptor = changeset
586 .descriptor
587 .ok_or(LoadError::MissingDescriptor(KeychainKind::External))?;
588 check_wallet_descriptor(&descriptor).map_err(LoadError::Descriptor)?;
589 let mut external_keymap = params.descriptor_keymap;
590
591 if let Some(expected) = params.check_descriptor {
592 if let Some(make_desc) = expected {
593 let (exp_desc, keymap) =
594 make_desc(&secp, network).map_err(LoadError::Descriptor)?;
595 if descriptor.descriptor_id() != exp_desc.descriptor_id() {
596 return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
597 keychain: KeychainKind::External,
598 loaded: Some(descriptor),
599 expected: Some(exp_desc),
600 }));
601 }
602 if params.extract_keys {
603 external_keymap.extend(keymap);
604 }
605 } else {
606 return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
607 keychain: KeychainKind::External,
608 loaded: Some(descriptor),
609 expected: None,
610 }));
611 }
612 }
613 let signers = Arc::new(SignersContainer::build(external_keymap, &descriptor, &secp));
614
615 let mut change_descriptor = None;
616 let mut internal_keymap = params.change_descriptor_keymap;
617
618 match (changeset.change_descriptor, params.check_change_descriptor) {
619 // empty signer
620 (None, None) => {}
621 (None, Some(expect)) => {
622 // expected desc but none loaded
623 if let Some(make_desc) = expect {
624 let (exp_desc, _) = make_desc(&secp, network).map_err(LoadError::Descriptor)?;
625 return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
626 keychain: KeychainKind::Internal,
627 loaded: None,
628 expected: Some(exp_desc),
629 }));
630 }
631 }
632 // nothing expected
633 (Some(desc), None) => {
634 check_wallet_descriptor(&desc).map_err(LoadError::Descriptor)?;
635 change_descriptor = Some(desc);
636 }
637 (Some(desc), Some(expect)) => match expect {
638 // expected none for existing
639 None => {
640 return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
641 keychain: KeychainKind::Internal,
642 loaded: Some(desc),
643 expected: None,
644 }))
645 }
646 // parameters must match
647 Some(make_desc) => {
648 check_wallet_descriptor(&desc).map_err(LoadError::Descriptor)?;
649 let (exp_desc, keymap) =
650 make_desc(&secp, network).map_err(LoadError::Descriptor)?;
651 if desc.descriptor_id() != exp_desc.descriptor_id() {
652 return Err(LoadError::Mismatch(LoadMismatch::Descriptor {
653 keychain: KeychainKind::Internal,
654 loaded: Some(desc),
655 expected: Some(exp_desc),
656 }));
657 }
658 if params.extract_keys {
659 internal_keymap.extend(keymap);
660 }
661 change_descriptor = Some(desc);
662 }
663 },
664 }
665
666 let change_signers = match change_descriptor {
667 Some(ref change_descriptor) => Arc::new(SignersContainer::build(
668 internal_keymap,
669 change_descriptor,
670 &secp,
671 )),
672 None => Arc::new(SignersContainer::new()),
673 };
674
675 let mut stage = ChangeSet::default();
676
677 let indexed_graph = make_indexed_graph(
678 &mut stage,
679 changeset.tx_graph,
680 changeset.indexer,
681 descriptor,
682 change_descriptor,
683 params.lookahead,
684 params.use_spk_cache,
685 )
686 .map_err(LoadError::Descriptor)?;
687
688 Ok(Some(Wallet {
689 signers,
690 change_signers,
691 chain,
692 indexed_graph,
693 stage,
694 network,
695 secp,
696 }))
697 }
698
699 /// Get the Bitcoin network the wallet is using.
700 pub fn network(&self) -> Network {
701 self.network
702 }
703
704 /// Iterator over all keychains in this wallet
705 pub fn keychains(&self) -> impl Iterator<Item = (KeychainKind, &ExtendedDescriptor)> {
706 self.indexed_graph.index.keychains()
707 }
708
709 /// Peek an address of the given `keychain` at `index` without revealing it.
710 ///
711 /// For non-wildcard descriptors this returns the same address at every provided index.
712 ///
713 /// # Panics
714 ///
715 /// This panics when the caller requests for an address of derivation index greater than the
716 /// [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) max index.
717 pub fn peek_address(&self, keychain: KeychainKind, mut index: u32) -> AddressInfo {
718 let keychain = self.map_keychain(keychain);
719 let mut spk_iter = self
720 .indexed_graph
721 .index
722 .unbounded_spk_iter(keychain)
723 .expect("keychain must exist");
724 if !spk_iter.descriptor().has_wildcard() {
725 index = 0;
726 }
727 let (index, spk) = spk_iter
728 .nth(index as usize)
729 .expect("derivation index is out of bounds");
730
731 AddressInfo {
732 index,
733 address: Address::from_script(&spk, self.network).expect("must have address form"),
734 keychain,
735 }
736 }
737
738 /// Attempt to reveal the next address of the given `keychain`.
739 ///
740 /// This will increment the keychain's derivation index. If the keychain's descriptor doesn't
741 /// contain a wildcard or every address is already revealed up to the maximum derivation
742 /// index defined in [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki),
743 /// then the last revealed address will be returned.
744 ///
745 /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
746 /// calls to this method before closing the wallet. For example:
747 ///
748 /// ```rust,no_run
749 /// # use bdk_wallet::{LoadParams, ChangeSet, KeychainKind};
750 /// use bdk_chain::rusqlite::Connection;
751 /// let mut conn = Connection::open_in_memory().expect("must open connection");
752 /// let mut wallet = LoadParams::new()
753 /// .load_wallet(&mut conn)
754 /// .expect("database is okay")
755 /// .expect("database has data");
756 /// let next_address = wallet.reveal_next_address(KeychainKind::External);
757 /// wallet.persist(&mut conn).expect("write is okay");
758 ///
759 /// // Now it's safe to show the user their next address!
760 /// println!("Next address: {}", next_address.address);
761 /// # Ok::<(), anyhow::Error>(())
762 /// ```
763 pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo {
764 let keychain = self.map_keychain(keychain);
765 let index = &mut self.indexed_graph.index;
766 let stage = &mut self.stage;
767
768 let ((index, spk), index_changeset) = index
769 .reveal_next_spk(keychain)
770 .expect("keychain must exist");
771
772 stage.merge(index_changeset.into());
773
774 AddressInfo {
775 index,
776 address: Address::from_script(spk.as_script(), self.network)
777 .expect("must have address form"),
778 keychain,
779 }
780 }
781
782 /// Reveal addresses up to and including the target `index` and return an iterator
783 /// of newly revealed addresses.
784 ///
785 /// If the target `index` is unreachable, we make a best effort to reveal up to the last
786 /// possible index. If all addresses up to the given `index` are already revealed, then
787 /// no new addresses are returned.
788 ///
789 /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
790 /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
791 pub fn reveal_addresses_to(
792 &mut self,
793 keychain: KeychainKind,
794 index: u32,
795 ) -> impl Iterator<Item = AddressInfo> + '_ {
796 let keychain = self.map_keychain(keychain);
797 let (spks, index_changeset) = self
798 .indexed_graph
799 .index
800 .reveal_to_target(keychain, index)
801 .expect("keychain must exist");
802
803 self.stage.merge(index_changeset.into());
804
805 spks.into_iter().map(move |(index, spk)| AddressInfo {
806 index,
807 address: Address::from_script(&spk, self.network).expect("must have address form"),
808 keychain,
809 })
810 }
811
812 /// Get the next unused address for the given `keychain`, i.e. the address with the lowest
813 /// derivation index that hasn't been used in a transaction.
814 ///
815 /// This will attempt to reveal a new address if all previously revealed addresses have
816 /// been used, in which case the returned address will be the same as calling
817 /// [`Wallet::reveal_next_address`].
818 ///
819 /// **WARNING**: To avoid address reuse you must persist the changes resulting from one or more
820 /// calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
821 pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo {
822 let keychain = self.map_keychain(keychain);
823 let index = &mut self.indexed_graph.index;
824
825 let ((index, spk), index_changeset) = index
826 .next_unused_spk(keychain)
827 .expect("keychain must exist");
828
829 self.stage
830 .merge(indexed_tx_graph::ChangeSet::from(index_changeset).into());
831
832 AddressInfo {
833 index,
834 address: Address::from_script(spk.as_script(), self.network)
835 .expect("must have address form"),
836 keychain,
837 }
838 }
839
840 /// Marks an address used of the given `keychain` at `index`.
841 ///
842 /// Returns whether the given index was present and then removed from the unused set.
843 pub fn mark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
844 self.indexed_graph.index.mark_used(keychain, index)
845 }
846
847 /// Undoes the effect of [`mark_used`] and returns whether the `index` was inserted
848 /// back into the unused set.
849 ///
850 /// Since this is only a superficial marker, it will have no effect if the address at the given
851 /// `index` was actually used, i.e. the wallet has previously indexed a tx output for the
852 /// derived spk.
853 ///
854 /// [`mark_used`]: Self::mark_used
855 pub fn unmark_used(&mut self, keychain: KeychainKind, index: u32) -> bool {
856 self.indexed_graph.index.unmark_used(keychain, index)
857 }
858
859 /// List addresses that are revealed but unused.
860 ///
861 /// Note if the returned iterator is empty you can reveal more addresses
862 /// by using [`reveal_next_address`](Self::reveal_next_address) or
863 /// [`reveal_addresses_to`](Self::reveal_addresses_to).
864 pub fn list_unused_addresses(
865 &self,
866 keychain: KeychainKind,
867 ) -> impl DoubleEndedIterator<Item = AddressInfo> + '_ {
868 self.indexed_graph
869 .index
870 .unused_keychain_spks(self.map_keychain(keychain))
871 .map(move |(index, spk)| AddressInfo {
872 index,
873 address: Address::from_script(spk.as_script(), self.network)
874 .expect("must have address form"),
875 keychain,
876 })
877 }
878
879 /// Return whether or not a `script` is part of this wallet (either internal or external)
880 pub fn is_mine(&self, script: ScriptBuf) -> bool {
881 self.indexed_graph.index.index_of_spk(script).is_some()
882 }
883
884 /// Finds how the wallet derived the script pubkey `spk`.
885 ///
886 /// Will only return `Some(_)` if the wallet has given out the spk.
887 pub fn derivation_of_spk(&self, spk: ScriptBuf) -> Option<(KeychainKind, u32)> {
888 self.indexed_graph.index.index_of_spk(spk).cloned()
889 }
890
891 /// Return the list of unspent outputs of this wallet
892 pub fn list_unspent(&self) -> impl Iterator<Item = LocalOutput> + '_ {
893 self.indexed_graph
894 .graph()
895 .filter_chain_unspents(
896 &self.chain,
897 self.chain.tip().block_id(),
898 CanonicalizationParams::default(),
899 self.indexed_graph.index.outpoints().iter().cloned(),
900 )
901 .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
902 }
903
904 /// Get the [`TxDetails`] of a wallet transaction.
905 ///
906 /// If the transaction with txid [`Txid`] cannot be found in the wallet's transactions, `None`
907 /// is returned.
908 pub fn tx_details(&self, txid: Txid) -> Option<TxDetails> {
909 let tx: WalletTx = self.transactions().find(|c| c.tx_node.txid == txid)?;
910
911 let (sent, received) = self.sent_and_received(&tx.tx_node.tx);
912 let fee: Option<Amount> = self.calculate_fee(&tx.tx_node.tx).ok();
913 let fee_rate: Option<FeeRate> = self.calculate_fee_rate(&tx.tx_node.tx).ok();
914 let balance_delta: SignedAmount = self.indexed_graph.index.net_value(&tx.tx_node.tx, ..);
915 let chain_position = tx.chain_position;
916
917 let tx_details: TxDetails = TxDetails {
918 txid,
919 received,
920 sent,
921 fee,
922 fee_rate,
923 balance_delta,
924 chain_position,
925 tx: tx.tx_node.tx,
926 };
927
928 Some(tx_details)
929 }
930
931 /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
932 ///
933 /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
934 pub fn list_output(&self) -> impl Iterator<Item = LocalOutput> + '_ {
935 self.indexed_graph
936 .graph()
937 .filter_chain_txouts(
938 &self.chain,
939 self.chain.tip().block_id(),
940 CanonicalizationParams::default(),
941 self.indexed_graph.index.outpoints().iter().cloned(),
942 )
943 .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
944 }
945
946 /// Get all the checkpoints the wallet is currently storing indexed by height.
947 pub fn checkpoints(&self) -> CheckPointIter {
948 self.chain.iter_checkpoints()
949 }
950
951 /// Returns the latest checkpoint.
952 pub fn latest_checkpoint(&self) -> CheckPoint {
953 self.chain.tip()
954 }
955
956 /// Get unbounded script pubkey iterators for both `Internal` and `External` keychains.
957 ///
958 /// This is intended to be used when doing a full scan of your addresses (e.g. after restoring
959 /// from seed words). You pass the `BTreeMap` of iterators to a blockchain data source (e.g.
960 /// electrum server) which will go through each address until it reaches a *stop gap*.
961 ///
962 /// Note carefully that iterators go over **all** script pubkeys on the keychains (not what
963 /// script pubkeys the wallet is storing internally).
964 pub fn all_unbounded_spk_iters(
965 &self,
966 ) -> BTreeMap<KeychainKind, impl Iterator<Item = Indexed<ScriptBuf>> + Clone> {
967 self.indexed_graph.index.all_unbounded_spk_iters()
968 }
969
970 /// Get an unbounded script pubkey iterator for the given `keychain`.
971 ///
972 /// See [`all_unbounded_spk_iters`] for more documentation
973 ///
974 /// [`all_unbounded_spk_iters`]: Self::all_unbounded_spk_iters
975 pub fn unbounded_spk_iter(
976 &self,
977 keychain: KeychainKind,
978 ) -> impl Iterator<Item = Indexed<ScriptBuf>> + Clone {
979 self.indexed_graph
980 .index
981 .unbounded_spk_iter(self.map_keychain(keychain))
982 .expect("keychain must exist")
983 }
984
985 /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
986 /// wallet's database.
987 pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
988 let ((keychain, index), _) = self.indexed_graph.index.txout(op)?;
989 self.indexed_graph
990 .graph()
991 .filter_chain_unspents(
992 &self.chain,
993 self.chain.tip().block_id(),
994 CanonicalizationParams::default(),
995 core::iter::once(((), op)),
996 )
997 .map(|(_, full_txo)| new_local_utxo(keychain, index, full_txo))
998 .next()
999 }
1000
1001 /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph.
1002 ///
1003 /// This is used for providing a previous output's value so that we can use [`calculate_fee`]
1004 /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will
1005 /// not be returned in [`list_unspent`] or [`list_output`].
1006 ///
1007 /// **WARNINGS:** This should only be used to add `TxOut`s that the wallet does not own. Only
1008 /// insert `TxOut`s that you trust the values for!
1009 ///
1010 /// You must persist the changes resulting from one or more calls to this method if you need
1011 /// the inserted `TxOut` data to be reloaded after closing the wallet.
1012 /// See [`Wallet::reveal_next_address`].
1013 ///
1014 /// [`calculate_fee`]: Self::calculate_fee
1015 /// [`calculate_fee_rate`]: Self::calculate_fee_rate
1016 /// [`list_unspent`]: Self::list_unspent
1017 /// [`list_output`]: Self::list_output
1018 pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) {
1019 let additions = self.indexed_graph.insert_txout(outpoint, txout);
1020 self.stage.merge(additions.into());
1021 }
1022
1023 /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase
1024 /// transaction.
1025 ///
1026 /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must
1027 /// manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
1028 ///
1029 /// Note `tx` does not have to be in the graph for this to work.
1030 ///
1031 /// # Examples
1032 ///
1033 /// ```rust, no_run
1034 /// # use bitcoin::Txid;
1035 /// # use bdk_wallet::Wallet;
1036 /// # let mut wallet: Wallet = todo!();
1037 /// # let txid:Txid = todo!();
1038 /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
1039 /// let fee = wallet.calculate_fee(&tx).expect("fee");
1040 /// ```
1041 ///
1042 /// ```rust, no_run
1043 /// # use bitcoin::Psbt;
1044 /// # use bdk_wallet::Wallet;
1045 /// # let mut wallet: Wallet = todo!();
1046 /// # let mut psbt: Psbt = todo!();
1047 /// let tx = &psbt.clone().extract_tx().expect("tx");
1048 /// let fee = wallet.calculate_fee(tx).expect("fee");
1049 /// ```
1050 /// [`insert_txout`]: Self::insert_txout
1051 pub fn calculate_fee(&self, tx: &Transaction) -> Result<Amount, CalculateFeeError> {
1052 self.indexed_graph.graph().calculate_fee(tx)
1053 }
1054
1055 /// Calculate the [`FeeRate`] for a given transaction.
1056 ///
1057 /// To calculate the fee rate for a [`Transaction`] with inputs not owned by this wallet you
1058 /// must manually insert the TxOut(s) into the tx graph using the [`insert_txout`] function.
1059 ///
1060 /// Note `tx` does not have to be in the graph for this to work.
1061 ///
1062 /// # Examples
1063 ///
1064 /// ```rust, no_run
1065 /// # use bitcoin::Txid;
1066 /// # use bdk_wallet::Wallet;
1067 /// # let mut wallet: Wallet = todo!();
1068 /// # let txid:Txid = todo!();
1069 /// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
1070 /// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
1071 /// ```
1072 ///
1073 /// ```rust, no_run
1074 /// # use bitcoin::Psbt;
1075 /// # use bdk_wallet::Wallet;
1076 /// # let mut wallet: Wallet = todo!();
1077 /// # let mut psbt: Psbt = todo!();
1078 /// let tx = &psbt.clone().extract_tx().expect("tx");
1079 /// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
1080 /// ```
1081 /// [`insert_txout`]: Self::insert_txout
1082 pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
1083 self.calculate_fee(tx).map(|fee| fee / tx.weight())
1084 }
1085
1086 /// Compute the `tx`'s sent and received [`Amount`]s.
1087 ///
1088 /// This method returns a tuple `(sent, received)`. Sent is the sum of the txin amounts
1089 /// that spend from previous txouts tracked by this wallet. Received is the summation
1090 /// of this tx's outputs that send to script pubkeys tracked by this wallet.
1091 ///
1092 /// # Examples
1093 ///
1094 /// ```rust, no_run
1095 /// # use bitcoin::Txid;
1096 /// # use bdk_wallet::Wallet;
1097 /// # let mut wallet: Wallet = todo!();
1098 /// # let txid:Txid = todo!();
1099 /// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
1100 /// let (sent, received) = wallet.sent_and_received(&tx);
1101 /// ```
1102 ///
1103 /// ```rust, no_run
1104 /// # use bitcoin::Psbt;
1105 /// # use bdk_wallet::Wallet;
1106 /// # let mut wallet: Wallet = todo!();
1107 /// # let mut psbt: Psbt = todo!();
1108 /// let tx = &psbt.clone().extract_tx().expect("tx");
1109 /// let (sent, received) = wallet.sent_and_received(tx);
1110 /// ```
1111 pub fn sent_and_received(&self, tx: &Transaction) -> (Amount, Amount) {
1112 self.indexed_graph.index.sent_and_received(tx, ..)
1113 }
1114
1115 /// Get a single transaction from the wallet as a [`WalletTx`] (if the transaction exists).
1116 ///
1117 /// `WalletTx` contains the full transaction alongside meta-data such as:
1118 /// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
1119 /// in the best chain.
1120 /// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
1121 /// confirmed or unconfirmed. If the transaction is confirmed, the anchor which proves the
1122 /// confirmation is provided. If the transaction is unconfirmed, the unix timestamp of when
1123 /// the transaction was last seen in the mempool is provided.
1124 ///
1125 /// ```rust, no_run
1126 /// use bdk_chain::Anchor;
1127 /// use bdk_wallet::{chain::ChainPosition, Wallet};
1128 /// # let wallet: Wallet = todo!();
1129 /// # let my_txid: bitcoin::Txid = todo!();
1130 ///
1131 /// let wallet_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
1132 ///
1133 /// // get reference to full transaction
1134 /// println!("my tx: {:#?}", wallet_tx.tx_node.tx);
1135 ///
1136 /// // list all transaction anchors
1137 /// for anchor in wallet_tx.tx_node.anchors {
1138 /// println!(
1139 /// "tx is anchored by block of hash {}",
1140 /// anchor.anchor_block().hash
1141 /// );
1142 /// }
1143 ///
1144 /// // get confirmation status of transaction
1145 /// match wallet_tx.chain_position {
1146 /// ChainPosition::Confirmed {
1147 /// anchor,
1148 /// transitively: None,
1149 /// } => println!(
1150 /// "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
1151 /// anchor.block_id.height, anchor.block_id.height, anchor.block_id.hash,
1152 /// ),
1153 /// ChainPosition::Confirmed {
1154 /// anchor,
1155 /// transitively: Some(_),
1156 /// } => println!(
1157 /// "tx is an ancestor of a tx anchored in {}:{}",
1158 /// anchor.block_id.height, anchor.block_id.hash,
1159 /// ),
1160 /// ChainPosition::Unconfirmed { first_seen, last_seen } => println!(
1161 /// "tx is first seen at {:?}, last seen at {:?}, it is unconfirmed as it is not anchored in the best chain",
1162 /// first_seen, last_seen
1163 /// ),
1164 /// }
1165 /// ```
1166 ///
1167 /// [`Anchor`]: bdk_chain::Anchor
1168 pub fn get_tx(&self, txid: Txid) -> Option<WalletTx> {
1169 let graph = self.indexed_graph.graph();
1170 graph
1171 .list_canonical_txs(
1172 &self.chain,
1173 self.chain.tip().block_id(),
1174 CanonicalizationParams::default(),
1175 )
1176 .find(|tx| tx.tx_node.txid == txid)
1177 }
1178
1179 /// Iterate over relevant and canonical transactions in the wallet.
1180 ///
1181 /// A transaction is relevant when it spends from or spends to at least one tracked output. A
1182 /// transaction is canonical when it is confirmed in the best chain, or does not conflict
1183 /// with any transaction confirmed in the best chain.
1184 ///
1185 /// To iterate over all transactions, including those that are irrelevant and not canonical, use
1186 /// [`TxGraph::full_txs`].
1187 ///
1188 /// To iterate over all canonical transactions, including those that are irrelevant, use
1189 /// [`TxGraph::list_canonical_txs`].
1190 pub fn transactions(&self) -> impl Iterator<Item = WalletTx> + '_ {
1191 let tx_graph = self.indexed_graph.graph();
1192 let tx_index = &self.indexed_graph.index;
1193 tx_graph
1194 .list_canonical_txs(
1195 &self.chain,
1196 self.chain.tip().block_id(),
1197 CanonicalizationParams::default(),
1198 )
1199 .filter(|c_tx| tx_index.is_tx_relevant(&c_tx.tx_node.tx))
1200 }
1201
1202 /// Array of relevant and canonical transactions in the wallet sorted with a comparator
1203 /// function.
1204 ///
1205 /// This is a helper method equivalent to collecting the result of [`Wallet::transactions`]
1206 /// into a [`Vec`] and then sorting it.
1207 ///
1208 /// # Example
1209 ///
1210 /// ```rust,no_run
1211 /// # use bdk_wallet::{LoadParams, Wallet, WalletTx};
1212 /// # let mut wallet:Wallet = todo!();
1213 /// // Transactions by chain position: first unconfirmed then descending by confirmed height.
1214 /// let sorted_txs: Vec<WalletTx> =
1215 /// wallet.transactions_sort_by(|tx1, tx2| tx2.chain_position.cmp(&tx1.chain_position));
1216 /// # Ok::<(), anyhow::Error>(())
1217 /// ```
1218 pub fn transactions_sort_by<F>(&self, compare: F) -> Vec<WalletTx>
1219 where
1220 F: FnMut(&WalletTx, &WalletTx) -> Ordering,
1221 {
1222 let mut txs: Vec<WalletTx> = self.transactions().collect();
1223 txs.sort_unstable_by(compare);
1224 txs
1225 }
1226
1227 /// Return the balance, separated into available, trusted-pending, untrusted-pending and
1228 /// immature values.
1229 pub fn balance(&self) -> Balance {
1230 self.indexed_graph.graph().balance(
1231 &self.chain,
1232 self.chain.tip().block_id(),
1233 CanonicalizationParams::default(),
1234 self.indexed_graph.index.outpoints().iter().cloned(),
1235 |&(k, _), _| k == KeychainKind::Internal,
1236 )
1237 }
1238
1239 /// Add an external signer
1240 ///
1241 /// See [the `signer` module](signer) for an example.
1242 pub fn add_signer(
1243 &mut self,
1244 keychain: KeychainKind,
1245 ordering: SignerOrdering,
1246 signer: Arc<dyn TransactionSigner>,
1247 ) {
1248 let signers = match keychain {
1249 KeychainKind::External => Arc::make_mut(&mut self.signers),
1250 KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
1251 };
1252
1253 signers.add_external(signer.id(&self.secp), ordering, signer);
1254 }
1255
1256 /// Set the keymap for a given keychain.
1257 ///
1258 /// Note this does nothing if the given keychain has no descriptor because we won't
1259 /// know the context (segwit, taproot, etc) in which to create signatures.
1260 pub fn set_keymap(&mut self, keychain: KeychainKind, keymap: KeyMap) {
1261 let wallet_signers = match keychain {
1262 KeychainKind::External => Arc::make_mut(&mut self.signers),
1263 KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
1264 };
1265 if let Some(descriptor) = self.indexed_graph.index.get_descriptor(keychain) {
1266 *wallet_signers = SignersContainer::build(keymap, descriptor, &self.secp)
1267 }
1268 }
1269
1270 /// Set the keymap for each keychain.
1271 pub fn set_keymaps(&mut self, keymaps: impl IntoIterator<Item = (KeychainKind, KeyMap)>) {
1272 for (keychain, keymap) in keymaps {
1273 self.set_keymap(keychain, keymap);
1274 }
1275 }
1276
1277 /// Get the signers
1278 ///
1279 /// ## Example
1280 ///
1281 /// ```
1282 /// # use bdk_wallet::{Wallet, KeychainKind};
1283 /// # use bdk_wallet::bitcoin::Network;
1284 /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
1285 /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
1286 /// let wallet = Wallet::create(descriptor, change_descriptor)
1287 /// .network(Network::Testnet)
1288 /// .create_wallet_no_persist()?;
1289 /// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
1290 /// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
1291 /// println!("secret_key: {}", secret_key);
1292 /// }
1293 ///
1294 /// Ok::<(), Box<dyn std::error::Error>>(())
1295 /// ```
1296 pub fn get_signers(&self, keychain: KeychainKind) -> Arc<SignersContainer> {
1297 match keychain {
1298 KeychainKind::External => Arc::clone(&self.signers),
1299 KeychainKind::Internal => Arc::clone(&self.change_signers),
1300 }
1301 }
1302
1303 /// Start building a transaction.
1304 ///
1305 /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the
1306 /// transaction.
1307 ///
1308 /// ## Example
1309 ///
1310 /// ```
1311 /// # use std::str::FromStr;
1312 /// # use bitcoin::*;
1313 /// # use bdk_wallet::*;
1314 /// # use bdk_wallet::ChangeSet;
1315 /// # use bdk_wallet::error::CreateTxError;
1316 /// # use anyhow::Error;
1317 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1318 /// # let mut wallet = doctest_wallet!();
1319 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1320 /// let psbt = {
1321 /// let mut builder = wallet.build_tx();
1322 /// builder
1323 /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1324 /// builder.finish()?
1325 /// };
1326 ///
1327 /// // sign and broadcast ...
1328 /// # Ok::<(), anyhow::Error>(())
1329 /// ```
1330 ///
1331 /// [`TxBuilder`]: crate::TxBuilder
1332 pub fn build_tx(&mut self) -> TxBuilder<'_, DefaultCoinSelectionAlgorithm> {
1333 TxBuilder {
1334 wallet: self,
1335 params: TxParams::default(),
1336 coin_selection: DefaultCoinSelectionAlgorithm::default(),
1337 }
1338 }
1339
1340 pub(crate) fn create_tx<Cs: coin_selection::CoinSelectionAlgorithm>(
1341 &mut self,
1342 coin_selection: Cs,
1343 params: TxParams,
1344 rng: &mut impl RngCore,
1345 ) -> Result<Psbt, CreateTxError> {
1346 let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
1347 let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
1348 let internal_descriptor = keychains.get(&KeychainKind::Internal);
1349
1350 let external_policy = external_descriptor
1351 .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
1352 .unwrap();
1353 let internal_policy = internal_descriptor
1354 .map(|desc| {
1355 Ok::<_, CreateTxError>(
1356 desc.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
1357 .unwrap(),
1358 )
1359 })
1360 .transpose()?;
1361
1362 // The policy allows spending external outputs, but it requires a policy path that hasn't
1363 // been provided
1364 if params.change_policy != tx_builder::ChangeSpendPolicy::OnlyChange
1365 && external_policy.requires_path()
1366 && params.external_policy_path.is_none()
1367 {
1368 return Err(CreateTxError::SpendingPolicyRequired(
1369 KeychainKind::External,
1370 ));
1371 };
1372 // Same for the internal_policy path
1373 if let Some(internal_policy) = &internal_policy {
1374 if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
1375 && internal_policy.requires_path()
1376 && params.internal_policy_path.is_none()
1377 {
1378 return Err(CreateTxError::SpendingPolicyRequired(
1379 KeychainKind::Internal,
1380 ));
1381 };
1382 }
1383
1384 let external_requirements = external_policy.get_condition(
1385 params
1386 .external_policy_path
1387 .as_ref()
1388 .unwrap_or(&BTreeMap::new()),
1389 )?;
1390 let internal_requirements = internal_policy
1391 .map(|policy| {
1392 Ok::<_, CreateTxError>(
1393 policy.get_condition(
1394 params
1395 .internal_policy_path
1396 .as_ref()
1397 .unwrap_or(&BTreeMap::new()),
1398 )?,
1399 )
1400 })
1401 .transpose()?;
1402
1403 let requirements =
1404 external_requirements.merge(&internal_requirements.unwrap_or_default())?;
1405
1406 let version = match params.version {
1407 Some(transaction::Version(0)) => return Err(CreateTxError::Version0),
1408 Some(transaction::Version::ONE) if requirements.csv.is_some() => {
1409 return Err(CreateTxError::Version1Csv)
1410 }
1411 Some(v) => v,
1412 None => transaction::Version::TWO,
1413 };
1414
1415 // We use a match here instead of a unwrap_or_else as it's way more readable :)
1416 let current_height = match params.current_height {
1417 // If they didn't tell us the current height, we assume it's the latest sync height.
1418 None => {
1419 let tip_height = self.chain.tip().height();
1420 absolute::LockTime::from_height(tip_height).expect("invalid height")
1421 }
1422 Some(h) => h,
1423 };
1424
1425 let lock_time = match params.locktime {
1426 // When no nLockTime is specified, we try to prevent fee sniping, if possible
1427 None => {
1428 // Fee sniping can be partially prevented by setting the timelock
1429 // to current_height. If we don't know the current_height,
1430 // we default to 0.
1431 let fee_sniping_height = current_height;
1432
1433 // We choose the biggest between the required nlocktime and the fee sniping
1434 // height
1435 match requirements.timelock {
1436 // No requirement, just use the fee_sniping_height
1437 None => fee_sniping_height,
1438 // There's a block-based requirement, but the value is lower than the
1439 // fee_sniping_height
1440 Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => {
1441 fee_sniping_height
1442 }
1443 // There's a time-based requirement or a block-based requirement greater
1444 // than the fee_sniping_height use that value
1445 Some(value) => value,
1446 }
1447 }
1448 // Specific nLockTime required and we have no constraints, so just set to that value
1449 Some(x) if requirements.timelock.is_none() => x,
1450 // Specific nLockTime required and it's compatible with the constraints
1451 Some(x)
1452 if requirements.timelock.unwrap().is_same_unit(x)
1453 && x >= requirements.timelock.unwrap() =>
1454 {
1455 x
1456 }
1457 // Invalid nLockTime required
1458 Some(x) => {
1459 return Err(CreateTxError::LockTime {
1460 requested: x,
1461 required: requirements.timelock.unwrap(),
1462 })
1463 }
1464 };
1465
1466 // nSequence value for inputs
1467 // When not explicitly specified, defaults to 0xFFFFFFFD,
1468 // meaning RBF signaling is enabled
1469 let n_sequence = match (params.sequence, requirements.csv) {
1470 // Enable RBF by default
1471 (None, None) => Sequence::ENABLE_RBF_NO_LOCKTIME,
1472 // None requested, use required
1473 (None, Some(csv)) => csv,
1474 // Requested sequence is incompatible with requirements
1475 (Some(sequence), Some(csv)) if !check_nsequence_rbf(sequence, csv) => {
1476 return Err(CreateTxError::RbfSequenceCsv { sequence, csv })
1477 }
1478 // Use requested nSequence value
1479 (Some(sequence), _) => sequence,
1480 };
1481
1482 let (fee_rate, mut fee_amount) = match params.fee_policy.unwrap_or_default() {
1483 //FIXME: see https://github.com/bitcoindevkit/bdk/issues/256
1484 FeePolicy::FeeAmount(fee) => {
1485 if let Some(previous_fee) = params.bumping_fee {
1486 if fee < previous_fee.absolute {
1487 return Err(CreateTxError::FeeTooLow {
1488 required: previous_fee.absolute,
1489 });
1490 }
1491 }
1492 (FeeRate::ZERO, fee)
1493 }
1494 FeePolicy::FeeRate(rate) => {
1495 if let Some(previous_fee) = params.bumping_fee {
1496 let required_feerate = FeeRate::from_sat_per_kwu(
1497 previous_fee.rate.to_sat_per_kwu()
1498 + FeeRate::BROADCAST_MIN.to_sat_per_kwu(), // +1 sat/vb
1499 );
1500 if rate < required_feerate {
1501 return Err(CreateTxError::FeeRateTooLow {
1502 required: required_feerate,
1503 });
1504 }
1505 }
1506 (rate, Amount::ZERO)
1507 }
1508 };
1509
1510 let mut tx = Transaction {
1511 version,
1512 lock_time,
1513 input: vec![],
1514 output: vec![],
1515 };
1516
1517 if params.manually_selected_only && params.utxos.is_empty() {
1518 return Err(CreateTxError::NoUtxosSelected);
1519 }
1520
1521 let mut outgoing = Amount::ZERO;
1522 let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
1523
1524 for (index, (script_pubkey, value)) in recipients.enumerate() {
1525 if !params.allow_dust && value.is_dust(script_pubkey) && !script_pubkey.is_op_return() {
1526 return Err(CreateTxError::OutputBelowDustLimit(index));
1527 }
1528
1529 let new_out = TxOut {
1530 script_pubkey: script_pubkey.clone(),
1531 value,
1532 };
1533
1534 tx.output.push(new_out);
1535
1536 outgoing += value;
1537 }
1538
1539 fee_amount += fee_rate * tx.weight();
1540
1541 let (required_utxos, optional_utxos) = {
1542 // NOTE: manual selection overrides unspendable
1543 let mut required: Vec<WeightedUtxo> = params.utxos.clone();
1544 let optional = self.filter_utxos(¶ms, current_height.to_consensus_u32());
1545
1546 // if drain_wallet is true, all UTxOs are required
1547 if params.drain_wallet {
1548 required.extend(optional);
1549 (required, vec![])
1550 } else {
1551 (required, optional)
1552 }
1553 };
1554
1555 // get drain script
1556 let mut drain_index = Option::<(KeychainKind, u32)>::None;
1557 let drain_script = match params.drain_to {
1558 Some(ref drain_recipient) => drain_recipient.clone(),
1559 None => {
1560 let change_keychain = self.map_keychain(KeychainKind::Internal);
1561 let (index, spk) = self
1562 .indexed_graph
1563 .index
1564 .unused_keychain_spks(change_keychain)
1565 .next()
1566 .unwrap_or_else(|| {
1567 let (next_index, _) = self
1568 .indexed_graph
1569 .index
1570 .next_index(change_keychain)
1571 .expect("keychain must exist");
1572 let spk = self
1573 .peek_address(change_keychain, next_index)
1574 .script_pubkey();
1575 (next_index, spk)
1576 });
1577 drain_index = Some((change_keychain, index));
1578 spk
1579 }
1580 };
1581
1582 let coin_selection = coin_selection
1583 .coin_select(
1584 required_utxos,
1585 optional_utxos,
1586 fee_rate,
1587 outgoing + fee_amount,
1588 &drain_script,
1589 rng,
1590 )
1591 .map_err(CreateTxError::CoinSelection)?;
1592
1593 let excess = &coin_selection.excess;
1594 tx.input = coin_selection
1595 .selected
1596 .iter()
1597 .map(|u| bitcoin::TxIn {
1598 previous_output: u.outpoint(),
1599 script_sig: ScriptBuf::default(),
1600 sequence: u.sequence().unwrap_or(n_sequence),
1601 witness: Witness::new(),
1602 })
1603 .collect();
1604
1605 if tx.output.is_empty() {
1606 // Uh oh, our transaction has no outputs.
1607 // We allow this when:
1608 // - We have a drain_to address and the utxos we must spend (this happens,
1609 // for example, when we RBF)
1610 // - We have a drain_to address and drain_wallet set
1611 // Otherwise, we don't know who we should send the funds to, and how much
1612 // we should send!
1613 if params.drain_to.is_some() && (params.drain_wallet || !params.utxos.is_empty()) {
1614 if let Excess::NoChange {
1615 dust_threshold,
1616 remaining_amount,
1617 change_fee,
1618 } = excess
1619 {
1620 return Err(CreateTxError::CoinSelection(InsufficientFunds {
1621 needed: *dust_threshold,
1622 available: remaining_amount
1623 .checked_sub(*change_fee)
1624 .unwrap_or_default(),
1625 }));
1626 }
1627 } else {
1628 return Err(CreateTxError::NoRecipients);
1629 }
1630 }
1631
1632 // if there's change, create and add a change output
1633 if let Excess::Change { amount, .. } = excess {
1634 // create drain output
1635 let drain_output = TxOut {
1636 value: *amount,
1637 script_pubkey: drain_script,
1638 };
1639
1640 // TODO: We should pay attention when adding a new output: this might increase
1641 // the length of the "number of vouts" parameter by 2 bytes, potentially making
1642 // our feerate too low
1643 tx.output.push(drain_output);
1644 }
1645
1646 // sort input/outputs according to the chosen algorithm
1647 params.ordering.sort_tx_with_aux_rand(&mut tx, rng);
1648
1649 let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
1650
1651 // recording changes to the change keychain
1652 if let (Excess::Change { .. }, Some((keychain, index))) = (excess, drain_index) {
1653 if let Some((_, index_changeset)) =
1654 self.indexed_graph.index.reveal_to_target(keychain, index)
1655 {
1656 self.stage.merge(index_changeset.into());
1657 self.mark_used(keychain, index);
1658 }
1659 }
1660
1661 Ok(psbt)
1662 }
1663
1664 /// Bump the fee of a transaction previously created with this wallet.
1665 ///
1666 /// Returns an error if the transaction is already confirmed or doesn't explicitly signal
1667 /// *replace by fee* (RBF). If the transaction can be fee bumped then it returns a [`TxBuilder`]
1668 /// pre-populated with the inputs and outputs of the original transaction.
1669 ///
1670 /// ## Example
1671 ///
1672 /// ```no_run
1673 /// # // TODO: remove norun -- bumping fee seems to need the tx in the wallet database first.
1674 /// # use std::str::FromStr;
1675 /// # use bitcoin::*;
1676 /// # use bdk_wallet::*;
1677 /// # use bdk_wallet::ChangeSet;
1678 /// # use bdk_wallet::error::CreateTxError;
1679 /// # use anyhow::Error;
1680 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1681 /// # let mut wallet = doctest_wallet!();
1682 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1683 /// let mut psbt = {
1684 /// let mut builder = wallet.build_tx();
1685 /// builder
1686 /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1687 /// builder.finish()?
1688 /// };
1689 /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1690 /// let tx = psbt.clone().extract_tx().expect("tx");
1691 /// // broadcast tx but it's taking too long to confirm so we want to bump the fee
1692 /// let mut psbt = {
1693 /// let mut builder = wallet.build_fee_bump(tx.compute_txid())?;
1694 /// builder
1695 /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
1696 /// builder.finish()?
1697 /// };
1698 ///
1699 /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
1700 /// let fee_bumped_tx = psbt.extract_tx();
1701 /// // broadcast fee_bumped_tx to replace original
1702 /// # Ok::<(), anyhow::Error>(())
1703 /// ```
1704 // TODO: support for merging multiple transactions while bumping the fees
1705 pub fn build_fee_bump(
1706 &mut self,
1707 txid: Txid,
1708 ) -> Result<TxBuilder<'_, DefaultCoinSelectionAlgorithm>, BuildFeeBumpError> {
1709 let graph = self.indexed_graph.graph();
1710 let txout_index = &self.indexed_graph.index;
1711 let chain_tip = self.chain.tip().block_id();
1712 let chain_positions = graph
1713 .list_canonical_txs(&self.chain, chain_tip, CanonicalizationParams::default())
1714 .map(|canon_tx| (canon_tx.tx_node.txid, canon_tx.chain_position))
1715 .collect::<HashMap<Txid, _>>();
1716
1717 let mut tx = graph
1718 .get_tx(txid)
1719 .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
1720 .as_ref()
1721 .clone();
1722
1723 if chain_positions
1724 .get(&txid)
1725 .ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
1726 .is_confirmed()
1727 {
1728 return Err(BuildFeeBumpError::TransactionConfirmed(txid));
1729 }
1730
1731 if !tx
1732 .input
1733 .iter()
1734 .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
1735 {
1736 return Err(BuildFeeBumpError::IrreplaceableTransaction(
1737 tx.compute_txid(),
1738 ));
1739 }
1740
1741 let fee = self
1742 .calculate_fee(&tx)
1743 .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
1744 let fee_rate = self
1745 .calculate_fee_rate(&tx)
1746 .map_err(|_| BuildFeeBumpError::FeeRateUnavailable)?;
1747
1748 // remove the inputs from the tx and process them
1749 let utxos = tx
1750 .input
1751 .drain(..)
1752 .map(|txin| -> Result<_, BuildFeeBumpError> {
1753 graph
1754 // Get previous transaction
1755 .get_tx(txin.previous_output.txid)
1756 .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))
1757 // Get chain position
1758 .and_then(|prev_tx| {
1759 let chain_position = chain_positions
1760 .get(&txin.previous_output.txid)
1761 .cloned()
1762 .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
1763 let prev_txout = prev_tx
1764 .output
1765 .get(txin.previous_output.vout as usize)
1766 .ok_or(BuildFeeBumpError::InvalidOutputIndex(txin.previous_output))
1767 .cloned()?;
1768 Ok((prev_tx, prev_txout, chain_position))
1769 })
1770 .map(|(prev_tx, prev_txout, chain_position)| {
1771 match txout_index.index_of_spk(prev_txout.script_pubkey.clone()) {
1772 Some(&(keychain, derivation_index)) => WeightedUtxo {
1773 satisfaction_weight: self
1774 .public_descriptor(keychain)
1775 .max_weight_to_satisfy()
1776 .unwrap(),
1777 utxo: Utxo::Local(LocalOutput {
1778 outpoint: txin.previous_output,
1779 txout: prev_txout.clone(),
1780 keychain,
1781 is_spent: true,
1782 derivation_index,
1783 chain_position,
1784 }),
1785 },
1786 None => {
1787 let satisfaction_weight = Weight::from_wu_usize(
1788 serialize(&txin.script_sig).len() * 4
1789 + serialize(&txin.witness).len(),
1790 );
1791 WeightedUtxo {
1792 utxo: Utxo::Foreign {
1793 outpoint: txin.previous_output,
1794 sequence: txin.sequence,
1795 psbt_input: Box::new(psbt::Input {
1796 witness_utxo: prev_txout
1797 .script_pubkey
1798 .witness_version()
1799 .map(|_| prev_txout.clone()),
1800 non_witness_utxo: Some(prev_tx.as_ref().clone()),
1801 ..Default::default()
1802 }),
1803 },
1804 satisfaction_weight,
1805 }
1806 }
1807 }
1808 })
1809 })
1810 .collect::<Result<Vec<WeightedUtxo>, BuildFeeBumpError>>()?;
1811
1812 if tx.output.len() > 1 {
1813 let mut change_index = None;
1814 for (index, txout) in tx.output.iter().enumerate() {
1815 let change_keychain = self.map_keychain(KeychainKind::Internal);
1816 match txout_index.index_of_spk(txout.script_pubkey.clone()) {
1817 Some((keychain, _)) if *keychain == change_keychain => {
1818 change_index = Some(index)
1819 }
1820 _ => {}
1821 }
1822 }
1823
1824 if let Some(change_index) = change_index {
1825 tx.output.remove(change_index);
1826 }
1827 }
1828
1829 let params = TxParams {
1830 // TODO: figure out what rbf option should be?
1831 version: Some(tx.version),
1832 recipients: tx
1833 .output
1834 .into_iter()
1835 .map(|txout| (txout.script_pubkey, txout.value))
1836 .collect(),
1837 utxos,
1838 bumping_fee: Some(tx_builder::PreviousFee {
1839 absolute: fee,
1840 rate: fee_rate,
1841 }),
1842 ..Default::default()
1843 };
1844
1845 Ok(TxBuilder {
1846 wallet: self,
1847 params,
1848 coin_selection: DefaultCoinSelectionAlgorithm::default(),
1849 })
1850 }
1851
1852 /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
1853 /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that
1854 /// has the value true if the PSBT was finalized, or false otherwise.
1855 ///
1856 /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
1857 /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
1858 /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
1859 /// in this library will.
1860 ///
1861 /// ## Example
1862 ///
1863 /// ```
1864 /// # use std::str::FromStr;
1865 /// # use bitcoin::*;
1866 /// # use bdk_wallet::*;
1867 /// # use bdk_wallet::ChangeSet;
1868 /// # use bdk_wallet::error::CreateTxError;
1869 /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
1870 /// # let mut wallet = doctest_wallet!();
1871 /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
1872 /// let mut psbt = {
1873 /// let mut builder = wallet.build_tx();
1874 /// builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
1875 /// builder.finish()?
1876 /// };
1877 /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
1878 /// assert!(finalized, "we should have signed all the inputs");
1879 /// # Ok::<(),anyhow::Error>(())
1880 pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, SignerError> {
1881 // This adds all the PSBT metadata for the inputs, which will help us later figure out how
1882 // to derive our keys
1883 self.update_psbt_with_descriptor(psbt)
1884 .map_err(SignerError::MiniscriptPsbt)?;
1885
1886 // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and
1887 // finalized ones) has the `non_witness_utxo`
1888 if !sign_options.trust_witness_utxo
1889 && psbt
1890 .inputs
1891 .iter()
1892 .filter(|i| i.final_script_witness.is_none() && i.final_script_sig.is_none())
1893 .filter(|i| i.tap_internal_key.is_none() && i.tap_merkle_root.is_none())
1894 .any(|i| i.non_witness_utxo.is_none())
1895 {
1896 return Err(SignerError::MissingNonWitnessUtxo);
1897 }
1898
1899 // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
1900 // is using `SIGHASH_ALL` or `SIGHASH_DEFAULT` for taproot
1901 if !sign_options.allow_all_sighashes
1902 && !psbt.inputs.iter().all(|i| {
1903 i.sighash_type.is_none()
1904 || i.sighash_type == Some(EcdsaSighashType::All.into())
1905 || i.sighash_type == Some(TapSighashType::All.into())
1906 || i.sighash_type == Some(TapSighashType::Default.into())
1907 })
1908 {
1909 return Err(SignerError::NonStandardSighash);
1910 }
1911
1912 for signer in self
1913 .signers
1914 .signers()
1915 .iter()
1916 .chain(self.change_signers.signers().iter())
1917 {
1918 signer.sign_transaction(psbt, &sign_options, &self.secp)?;
1919 }
1920
1921 // attempt to finalize
1922 if sign_options.try_finalize {
1923 self.finalize_psbt(psbt, sign_options)
1924 } else {
1925 Ok(false)
1926 }
1927 }
1928
1929 /// Return the spending policies for the wallet's descriptor
1930 pub fn policies(&self, keychain: KeychainKind) -> Result<Option<Policy>, DescriptorError> {
1931 let signers = match keychain {
1932 KeychainKind::External => &self.signers,
1933 KeychainKind::Internal => &self.change_signers,
1934 };
1935
1936 self.public_descriptor(keychain).extract_policy(
1937 signers,
1938 BuildSatisfaction::None,
1939 &self.secp,
1940 )
1941 }
1942
1943 /// Returns the descriptor used to create addresses for a particular `keychain`.
1944 ///
1945 /// It's the "public" version of the wallet's descriptor, meaning a new descriptor that has
1946 /// the same structure but with the all secret keys replaced by their corresponding public key.
1947 /// This can be used to build a watch-only version of a wallet.
1948 pub fn public_descriptor(&self, keychain: KeychainKind) -> &ExtendedDescriptor {
1949 self.indexed_graph
1950 .index
1951 .get_descriptor(self.map_keychain(keychain))
1952 .expect("keychain must exist")
1953 }
1954
1955 /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
1956 /// validation and construct the respective `scriptSig` or `scriptWitness`. Please refer to
1957 /// [BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#Input_Finalizer),
1958 /// and [BIP371](https://github.com/bitcoin/bips/blob/master/bip-0371.mediawiki)
1959 /// for further information.
1960 ///
1961 /// Returns `true` if the PSBT could be finalized, and `false` otherwise.
1962 ///
1963 /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
1964 pub fn finalize_psbt(
1965 &self,
1966 psbt: &mut Psbt,
1967 sign_options: SignOptions,
1968 ) -> Result<bool, SignerError> {
1969 let tx = &psbt.unsigned_tx;
1970 let chain_tip = self.chain.tip().block_id();
1971 let prev_txids = tx
1972 .input
1973 .iter()
1974 .map(|txin| txin.previous_output.txid)
1975 .collect::<HashSet<Txid>>();
1976 let confirmation_heights = self
1977 .indexed_graph
1978 .graph()
1979 .list_canonical_txs(&self.chain, chain_tip, CanonicalizationParams::default())
1980 .filter(|canon_tx| prev_txids.contains(&canon_tx.tx_node.txid))
1981 // This is for a small performance gain. Although `.filter` filters out excess txs, it
1982 // will still consume the internal `CanonicalIter` entirely. Having a `.take` here
1983 // allows us to stop further unnecessary canonicalization.
1984 .take(prev_txids.len())
1985 .map(|canon_tx| {
1986 let txid = canon_tx.tx_node.txid;
1987 match canon_tx.chain_position {
1988 ChainPosition::Confirmed { anchor, .. } => (txid, anchor.block_id.height),
1989 ChainPosition::Unconfirmed { .. } => (txid, u32::MAX),
1990 }
1991 })
1992 .collect::<HashMap<Txid, u32>>();
1993
1994 let mut finished = true;
1995
1996 for (n, input) in tx.input.iter().enumerate() {
1997 let psbt_input = &psbt
1998 .inputs
1999 .get(n)
2000 .ok_or(SignerError::InputIndexOutOfRange)?;
2001 if psbt_input.final_script_sig.is_some() || psbt_input.final_script_witness.is_some() {
2002 continue;
2003 }
2004 let confirmation_height = confirmation_heights
2005 .get(&input.previous_output.txid)
2006 .copied();
2007 let current_height = sign_options
2008 .assume_height
2009 .unwrap_or_else(|| self.chain.tip().height());
2010
2011 // - Try to derive the descriptor by looking at the txout. If it's in our database, we
2012 // know exactly which `keychain` to use, and which derivation index it is
2013 // - If that fails, try to derive it by looking at the psbt input: the complete logic is
2014 // in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
2015 // `redeem_script` and `witness_script` to determine the right derivation
2016 // - If that also fails, it will try it on the internal descriptor, if present
2017 let desc = psbt
2018 .get_utxo_for(n)
2019 .and_then(|txout| self.get_descriptor_for_txout(&txout))
2020 .or_else(|| {
2021 self.indexed_graph.index.keychains().find_map(|(_, desc)| {
2022 desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
2023 })
2024 });
2025
2026 match desc {
2027 Some(desc) => {
2028 let mut tmp_input = bitcoin::TxIn::default();
2029 match desc.satisfy(
2030 &mut tmp_input,
2031 (
2032 PsbtInputSatisfier::new(psbt, n),
2033 After::new(Some(current_height), false),
2034 Older::new(Some(current_height), confirmation_height, false),
2035 ),
2036 ) {
2037 Ok(_) => {
2038 // Set the UTXO fields, final script_sig and witness
2039 // and clear everything else.
2040 let psbt_input = psbt
2041 .inputs
2042 .get_mut(n)
2043 .ok_or(SignerError::InputIndexOutOfRange)?;
2044 let original = mem::take(psbt_input);
2045 psbt_input.non_witness_utxo = original.non_witness_utxo;
2046 psbt_input.witness_utxo = original.witness_utxo;
2047 if !tmp_input.script_sig.is_empty() {
2048 psbt_input.final_script_sig = Some(tmp_input.script_sig);
2049 }
2050 if !tmp_input.witness.is_empty() {
2051 psbt_input.final_script_witness = Some(tmp_input.witness);
2052 }
2053 }
2054 Err(_) => finished = false,
2055 }
2056 }
2057 None => finished = false,
2058 }
2059 }
2060
2061 // Clear derivation paths from outputs
2062 if finished {
2063 for output in &mut psbt.outputs {
2064 output.bip32_derivation.clear();
2065 output.tap_key_origins.clear();
2066 }
2067 }
2068
2069 Ok(finished)
2070 }
2071
2072 /// Return the secp256k1 context used for all signing operations
2073 pub fn secp_ctx(&self) -> &SecpCtx {
2074 &self.secp
2075 }
2076
2077 /// The derivation index of this wallet. It will return `None` if it has not derived any
2078 /// addresses. Otherwise, it will return the index of the highest address it has derived.
2079 pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
2080 self.indexed_graph.index.last_revealed_index(keychain)
2081 }
2082
2083 /// The index of the next address that you would get if you were to ask the wallet for a new
2084 /// address
2085 pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
2086 self.indexed_graph
2087 .index
2088 .next_index(self.map_keychain(keychain))
2089 .expect("keychain must exist")
2090 .0
2091 }
2092
2093 /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
2094 ///
2095 /// This frees up the change address used when creating the tx for use in future transactions.
2096 // TODO: Make this free up reserved utxos when that's implemented
2097 pub fn cancel_tx(&mut self, tx: &Transaction) {
2098 let txout_index = &mut self.indexed_graph.index;
2099 for txout in &tx.output {
2100 if let Some((keychain, index)) = txout_index.index_of_spk(txout.script_pubkey.clone()) {
2101 // NOTE: unmark_used will **not** make something unused if it has actually been used
2102 // by a tx in the tracker. It only removes the superficial marking.
2103 txout_index.unmark_used(*keychain, *index);
2104 }
2105 }
2106 }
2107
2108 fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
2109 let &(keychain, child) = self
2110 .indexed_graph
2111 .index
2112 .index_of_spk(txout.script_pubkey.clone())?;
2113 let descriptor = self.public_descriptor(keychain);
2114 descriptor.at_derivation_index(child).ok()
2115 }
2116
2117 /// Given the options returns the list of utxos that must be used to form the
2118 /// transaction and any further that may be used if needed.
2119 fn filter_utxos(&self, params: &TxParams, current_height: u32) -> Vec<WeightedUtxo> {
2120 if params.manually_selected_only {
2121 vec![]
2122 // only process optional UTxOs if manually_selected_only is false
2123 } else {
2124 let manually_selected_outpoints = params
2125 .utxos
2126 .iter()
2127 .map(|wutxo| wutxo.utxo.outpoint())
2128 .collect::<HashSet<OutPoint>>();
2129 self.indexed_graph
2130 .graph()
2131 // get all unspent UTxOs from wallet
2132 // NOTE: the UTxOs returned by the following method already belong to wallet as the
2133 // call chain uses get_tx_node infallibly
2134 .filter_chain_unspents(
2135 &self.chain,
2136 self.chain.tip().block_id(),
2137 CanonicalizationParams::default(),
2138 self.indexed_graph.index.outpoints().iter().cloned(),
2139 )
2140 // only create LocalOutput if UTxO is mature
2141 .filter_map(move |((k, i), full_txo)| {
2142 full_txo
2143 .is_mature(current_height)
2144 .then(|| new_local_utxo(k, i, full_txo))
2145 })
2146 // only process UTXOs not selected manually, they will be considered later in the
2147 // chain
2148 // NOTE: this avoid UTXOs in both required and optional list
2149 .filter(|may_spend| !manually_selected_outpoints.contains(&may_spend.outpoint))
2150 // only add to optional UTxOs those which satisfy the change policy if we reuse
2151 // change
2152 .filter(|local_output| {
2153 self.keychains().count() == 1
2154 || params.change_policy.is_satisfied_by(local_output)
2155 })
2156 // only add to optional UTxOs those marked as spendable
2157 .filter(|local_output| !params.unspendable.contains(&local_output.outpoint))
2158 // if bumping fees only add to optional UTxOs those confirmed
2159 .filter(|local_output| {
2160 params.bumping_fee.is_none() || local_output.chain_position.is_confirmed()
2161 })
2162 .map(|utxo| WeightedUtxo {
2163 satisfaction_weight: self
2164 .public_descriptor(utxo.keychain)
2165 .max_weight_to_satisfy()
2166 .unwrap(),
2167 utxo: Utxo::Local(utxo),
2168 })
2169 .collect()
2170 }
2171 }
2172
2173 fn complete_transaction(
2174 &self,
2175 tx: Transaction,
2176 selected: Vec<Utxo>,
2177 params: TxParams,
2178 ) -> Result<Psbt, CreateTxError> {
2179 let mut psbt = Psbt::from_unsigned_tx(tx)?;
2180
2181 if params.add_global_xpubs {
2182 let all_xpubs = self
2183 .keychains()
2184 .flat_map(|(_, desc)| desc.get_extended_keys())
2185 .collect::<Vec<_>>();
2186
2187 for xpub in all_xpubs {
2188 let origin = match xpub.origin {
2189 Some(origin) => origin,
2190 None if xpub.xkey.depth == 0 => {
2191 (xpub.root_fingerprint(&self.secp), vec![].into())
2192 }
2193 _ => return Err(CreateTxError::MissingKeyOrigin(xpub.xkey.to_string())),
2194 };
2195
2196 psbt.xpub.insert(xpub.xkey, origin);
2197 }
2198 }
2199
2200 let mut lookup_output = selected
2201 .into_iter()
2202 .map(|utxo| (utxo.outpoint(), utxo))
2203 .collect::<HashMap<_, _>>();
2204
2205 // add metadata for the inputs
2206 for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
2207 let utxo = match lookup_output.remove(&input.previous_output) {
2208 Some(utxo) => utxo,
2209 None => continue,
2210 };
2211
2212 match utxo {
2213 Utxo::Local(utxo) => {
2214 *psbt_input =
2215 match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
2216 Ok(psbt_input) => psbt_input,
2217 Err(e) => match e {
2218 CreateTxError::UnknownUtxo => psbt::Input {
2219 sighash_type: params.sighash,
2220 ..psbt::Input::default()
2221 },
2222 _ => return Err(e),
2223 },
2224 }
2225 }
2226 Utxo::Foreign {
2227 outpoint,
2228 psbt_input: foreign_psbt_input,
2229 ..
2230 } => {
2231 let is_taproot = foreign_psbt_input
2232 .witness_utxo
2233 .as_ref()
2234 .map(|txout| txout.script_pubkey.is_p2tr())
2235 .unwrap_or(false);
2236 if !is_taproot
2237 && !params.only_witness_utxo
2238 && foreign_psbt_input.non_witness_utxo.is_none()
2239 {
2240 return Err(CreateTxError::MissingNonWitnessUtxo(outpoint));
2241 }
2242 *psbt_input = *foreign_psbt_input;
2243 }
2244 }
2245 }
2246
2247 self.update_psbt_with_descriptor(&mut psbt)?;
2248
2249 Ok(psbt)
2250 }
2251
2252 /// get the corresponding PSBT Input for a LocalUtxo
2253 pub fn get_psbt_input(
2254 &self,
2255 utxo: LocalOutput,
2256 sighash_type: Option<psbt::PsbtSighashType>,
2257 only_witness_utxo: bool,
2258 ) -> Result<psbt::Input, CreateTxError> {
2259 // Try to find the prev_script in our db to figure out if this is internal or external,
2260 // and the derivation index
2261 let &(keychain, child) = self
2262 .indexed_graph
2263 .index
2264 .index_of_spk(utxo.txout.script_pubkey)
2265 .ok_or(CreateTxError::UnknownUtxo)?;
2266
2267 let mut psbt_input = psbt::Input {
2268 sighash_type,
2269 ..psbt::Input::default()
2270 };
2271
2272 let desc = self.public_descriptor(keychain);
2273 let derived_descriptor = desc
2274 .at_derivation_index(child)
2275 .expect("child can't be hardened");
2276
2277 psbt_input
2278 .update_with_descriptor_unchecked(&derived_descriptor)
2279 .map_err(MiniscriptPsbtError::Conversion)?;
2280
2281 let prev_output = utxo.outpoint;
2282 if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
2283 // We want to check that the prevout actually exists in the tx before continuing.
2284 let prevout = prev_tx.output.get(prev_output.vout as usize).ok_or(
2285 MiniscriptPsbtError::UtxoUpdate(miniscript::psbt::UtxoUpdateError::UtxoCheck),
2286 )?;
2287 if desc.is_witness() || desc.is_taproot() {
2288 psbt_input.witness_utxo = Some(prevout.clone());
2289 }
2290 if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
2291 psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
2292 }
2293 }
2294 Ok(psbt_input)
2295 }
2296
2297 fn update_psbt_with_descriptor(&self, psbt: &mut Psbt) -> Result<(), MiniscriptPsbtError> {
2298 // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
2299 // the input utxos and outputs
2300 let utxos = (0..psbt.inputs.len())
2301 .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
2302 .chain(
2303 psbt.unsigned_tx
2304 .output
2305 .iter()
2306 .enumerate()
2307 .map(|(i, out)| (false, i, out.clone())),
2308 )
2309 .collect::<Vec<_>>();
2310
2311 // Try to figure out the keychain and derivation for every input and output
2312 for (is_input, index, out) in utxos.into_iter() {
2313 if let Some(&(keychain, child)) =
2314 self.indexed_graph.index.index_of_spk(out.script_pubkey)
2315 {
2316 let desc = self.public_descriptor(keychain);
2317 let desc = desc
2318 .at_derivation_index(child)
2319 .expect("child can't be hardened");
2320
2321 if is_input {
2322 psbt.update_input_with_descriptor(index, &desc)
2323 .map_err(MiniscriptPsbtError::UtxoUpdate)?;
2324 } else {
2325 psbt.update_output_with_descriptor(index, &desc)
2326 .map_err(MiniscriptPsbtError::OutputUpdate)?;
2327 }
2328 }
2329 }
2330
2331 Ok(())
2332 }
2333
2334 /// Return the checksum of the public descriptor associated to `keychain`
2335 ///
2336 /// Internally calls [`Self::public_descriptor`] to fetch the right descriptor
2337 pub fn descriptor_checksum(&self, keychain: KeychainKind) -> String {
2338 self.public_descriptor(keychain)
2339 .to_string()
2340 .split_once('#')
2341 .unwrap()
2342 .1
2343 .to_string()
2344 }
2345
2346 /// Applies an update to the wallet and stages the changes (but does not persist them).
2347 ///
2348 /// Usually you create an `update` by interacting with some blockchain data source and inserting
2349 /// transactions related to your wallet into it.
2350 ///
2351 /// After applying updates you should persist the staged wallet changes. For an example of how
2352 /// to persist staged wallet changes see [`Wallet::reveal_next_address`].
2353 pub fn apply_update(&mut self, update: impl Into<Update>) -> Result<(), CannotConnectError> {
2354 let update = update.into();
2355 let mut changeset = match update.chain {
2356 Some(chain_update) => ChangeSet::from(self.chain.apply_update(chain_update)?),
2357 None => ChangeSet::default(),
2358 };
2359
2360 let index_changeset = self
2361 .indexed_graph
2362 .index
2363 .reveal_to_target_multi(&update.last_active_indices);
2364 changeset.merge(index_changeset.into());
2365 changeset.merge(self.indexed_graph.apply_update(update.tx_update).into());
2366 self.stage.merge(changeset);
2367 Ok(())
2368 }
2369
2370 /// Get a reference of the staged [`ChangeSet`] that is yet to be committed (if any).
2371 pub fn staged(&self) -> Option<&ChangeSet> {
2372 if self.stage.is_empty() {
2373 None
2374 } else {
2375 Some(&self.stage)
2376 }
2377 }
2378
2379 /// Get a mutable reference of the staged [`ChangeSet`] that is yet to be committed (if any).
2380 pub fn staged_mut(&mut self) -> Option<&mut ChangeSet> {
2381 if self.stage.is_empty() {
2382 None
2383 } else {
2384 Some(&mut self.stage)
2385 }
2386 }
2387
2388 /// Take the staged [`ChangeSet`] to be persisted now (if any).
2389 pub fn take_staged(&mut self) -> Option<ChangeSet> {
2390 self.stage.take()
2391 }
2392
2393 /// Get a reference to the inner [`TxGraph`].
2394 pub fn tx_graph(&self) -> &TxGraph<ConfirmationBlockTime> {
2395 self.indexed_graph.graph()
2396 }
2397
2398 /// Get a reference to the inner [`KeychainTxOutIndex`].
2399 pub fn spk_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
2400 &self.indexed_graph.index
2401 }
2402
2403 /// Get a reference to the inner [`LocalChain`].
2404 pub fn local_chain(&self) -> &LocalChain {
2405 &self.chain
2406 }
2407
2408 /// Introduces a `block` of `height` to the wallet, and tries to connect it to the
2409 /// `prev_blockhash` of the block's header.
2410 ///
2411 /// This is a convenience method that is equivalent to calling [`apply_block_connected_to`]
2412 /// with `prev_blockhash` and `height-1` as the `connected_to` parameter.
2413 ///
2414 /// [`apply_block_connected_to`]: Self::apply_block_connected_to
2415 pub fn apply_block(&mut self, block: &Block, height: u32) -> Result<(), CannotConnectError> {
2416 let connected_to = match height.checked_sub(1) {
2417 Some(prev_height) => BlockId {
2418 height: prev_height,
2419 hash: block.header.prev_blockhash,
2420 },
2421 None => BlockId {
2422 height,
2423 hash: block.block_hash(),
2424 },
2425 };
2426 self.apply_block_connected_to(block, height, connected_to)
2427 .map_err(|err| match err {
2428 ApplyHeaderError::InconsistentBlocks => {
2429 unreachable!("connected_to is derived from the block so must be consistent")
2430 }
2431 ApplyHeaderError::CannotConnect(err) => err,
2432 })
2433 }
2434
2435 /// Applies relevant transactions from `block` of `height` to the wallet, and connects the
2436 /// block to the internal chain.
2437 ///
2438 /// The `connected_to` parameter informs the wallet how this block connects to the internal
2439 /// [`LocalChain`]. Relevant transactions are filtered from the `block` and inserted into the
2440 /// internal [`TxGraph`].
2441 ///
2442 /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2443 /// if you need the inserted block data to be reloaded after closing the wallet.
2444 /// See [`Wallet::reveal_next_address`].
2445 pub fn apply_block_connected_to(
2446 &mut self,
2447 block: &Block,
2448 height: u32,
2449 connected_to: BlockId,
2450 ) -> Result<(), ApplyHeaderError> {
2451 let mut changeset = ChangeSet::default();
2452 changeset.merge(
2453 self.chain
2454 .apply_header_connected_to(&block.header, height, connected_to)?
2455 .into(),
2456 );
2457 changeset.merge(
2458 self.indexed_graph
2459 .apply_block_relevant(block, height)
2460 .into(),
2461 );
2462 self.stage.merge(changeset);
2463 Ok(())
2464 }
2465
2466 /// Apply relevant unconfirmed transactions to the wallet.
2467 ///
2468 /// Transactions that are not relevant are filtered out.
2469 ///
2470 /// This method takes in an iterator of `(tx, last_seen)` where `last_seen` is the timestamp of
2471 /// when the transaction was last seen in the mempool. This is used for conflict resolution
2472 /// when there is conflicting unconfirmed transactions. The transaction with the later
2473 /// `last_seen` is prioritized.
2474 ///
2475 /// **WARNING**: You must persist the changes resulting from one or more calls to this method
2476 /// if you need the applied unconfirmed transactions to be reloaded after closing the wallet.
2477 /// See [`Wallet::reveal_next_address`].
2478 pub fn apply_unconfirmed_txs<T: Into<Arc<Transaction>>>(
2479 &mut self,
2480 unconfirmed_txs: impl IntoIterator<Item = (T, u64)>,
2481 ) {
2482 let indexed_graph_changeset = self
2483 .indexed_graph
2484 .batch_insert_relevant_unconfirmed(unconfirmed_txs);
2485 self.stage.merge(indexed_graph_changeset.into());
2486 }
2487
2488 /// Apply evictions of the given transaction IDs with their associated timestamps.
2489 ///
2490 /// This function is used to mark specific unconfirmed transactions as evicted from the mempool.
2491 /// Eviction means that these transactions are not considered canonical by default, and will
2492 /// no longer be part of the wallet's [`transactions`] set. This can happen for example when
2493 /// a transaction is dropped from the mempool due to low fees or conflicts with another
2494 /// transaction.
2495 ///
2496 /// Only transactions that are currently unconfirmed and canonical are considered for eviction.
2497 /// Transactions that are not relevant to the wallet are ignored. Note that an evicted
2498 /// transaction can become canonical again if it is later observed on-chain or seen in the
2499 /// mempool with a higher priority (e.g., due to a fee bump).
2500 ///
2501 /// ## Parameters
2502 ///
2503 /// `evicted_txs`: An iterator of `(Txid, u64)` tuples, where:
2504 /// - `Txid`: The transaction ID of the transaction to be evicted.
2505 /// - `u64`: The timestamp indicating when the transaction was evicted from the mempool. This
2506 /// will usually correspond to the time of the latest chain sync. See docs for
2507 /// [`start_sync_with_revealed_spks`].
2508 ///
2509 /// ## Notes
2510 ///
2511 /// - Not all blockchain backends support automatic mempool eviction handling - this method may
2512 /// be used in such cases. It can also be used to negate the effect of
2513 /// [`apply_unconfirmed_txs`] for a particular transaction without the need for an additional
2514 /// sync.
2515 /// - The changes are staged in the wallet's internal state and must be persisted to ensure they
2516 /// are retained across wallet restarts. Use [`Wallet::take_staged`] to retrieve the staged
2517 /// changes and persist them to your database of choice.
2518 /// - Evicted transactions are removed from the wallet's canonical transaction set, but the data
2519 /// remains in the wallet's internal transaction graph for historical purposes.
2520 /// - Ensure that the timestamps provided are accurate and monotonically increasing, as they
2521 /// influence the wallet's canonicalization logic.
2522 ///
2523 /// [`transactions`]: Wallet::transactions
2524 /// [`apply_unconfirmed_txs`]: Wallet::apply_unconfirmed_txs
2525 /// [`start_sync_with_revealed_spks`]: Wallet::start_sync_with_revealed_spks
2526 pub fn apply_evicted_txs(&mut self, evicted_txs: impl IntoIterator<Item = (Txid, u64)>) {
2527 let chain = &self.chain;
2528 let canon_txids: Vec<Txid> = self
2529 .indexed_graph
2530 .graph()
2531 .list_canonical_txs(
2532 chain,
2533 chain.tip().block_id(),
2534 CanonicalizationParams::default(),
2535 )
2536 .map(|c| c.tx_node.txid)
2537 .collect();
2538
2539 let changeset = self.indexed_graph.batch_insert_relevant_evicted_at(
2540 evicted_txs
2541 .into_iter()
2542 .filter(|(txid, _)| canon_txids.contains(txid)),
2543 );
2544
2545 self.stage.merge(changeset.into());
2546 }
2547
2548 /// Used internally to ensure that all methods requiring a [`KeychainKind`] will use a
2549 /// keychain with an associated descriptor. For example in case the wallet was created
2550 /// with only one keychain, passing [`KeychainKind::Internal`] here will instead return
2551 /// [`KeychainKind::External`].
2552 fn map_keychain(&self, keychain: KeychainKind) -> KeychainKind {
2553 if self.keychains().count() == 1 {
2554 KeychainKind::External
2555 } else {
2556 keychain
2557 }
2558 }
2559}
2560
2561/// Methods to construct sync/full-scan requests for spk-based chain sources.
2562impl Wallet {
2563 /// Create a partial [`SyncRequest`] for all revealed spks at `start_time`.
2564 ///
2565 /// The `start_time` is used to record the time that a mempool transaction was last seen
2566 /// (or evicted). See [`Wallet::start_sync_with_revealed_spks`] for more.
2567 pub fn start_sync_with_revealed_spks_at(
2568 &self,
2569 start_time: u64,
2570 ) -> SyncRequestBuilder<(KeychainKind, u32)> {
2571 use bdk_chain::keychain_txout::SyncRequestBuilderExt;
2572 SyncRequest::builder_at(start_time)
2573 .chain_tip(self.chain.tip())
2574 .revealed_spks_from_indexer(&self.indexed_graph.index, ..)
2575 .expected_spk_txids(self.indexed_graph.list_expected_spk_txids(
2576 &self.chain,
2577 self.chain.tip().block_id(),
2578 ..,
2579 ))
2580 }
2581
2582 /// Create a partial [`SyncRequest`] for this wallet for all revealed spks.
2583 ///
2584 /// This is the first step when performing a spk-based wallet partial sync, the returned
2585 /// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
2586 /// start a blockchain sync with a spk based blockchain client.
2587 ///
2588 /// The time of the sync is the current system time and is used to record the
2589 /// tx last-seen for mempool transactions. Or if an expected transaction is missing
2590 /// or evicted, it is the time of the eviction. Note that timestamps may only increase
2591 /// to be counted by the tx graph. To supply your own start time see
2592 /// [`Wallet::start_sync_with_revealed_spks_at`].
2593 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
2594 #[cfg(feature = "std")]
2595 pub fn start_sync_with_revealed_spks(&self) -> SyncRequestBuilder<(KeychainKind, u32)> {
2596 use bdk_chain::keychain_txout::SyncRequestBuilderExt;
2597 SyncRequest::builder()
2598 .chain_tip(self.chain.tip())
2599 .revealed_spks_from_indexer(&self.indexed_graph.index, ..)
2600 .expected_spk_txids(self.indexed_graph.list_expected_spk_txids(
2601 &self.chain,
2602 self.chain.tip().block_id(),
2603 ..,
2604 ))
2605 }
2606
2607 /// Create a [`FullScanRequest] for this wallet.
2608 ///
2609 /// This is the first step when performing a spk-based wallet full scan, the returned
2610 /// [`FullScanRequest] collects iterators for the wallet's keychain script pub keys needed to
2611 /// start a blockchain full scan with a spk based blockchain client.
2612 ///
2613 /// This operation is generally only used when importing or restoring a previously used wallet
2614 /// in which the list of used scripts is not known.
2615 ///
2616 /// The time of the scan is the current system time and is used to record the tx last-seen for
2617 /// mempool transactions. To supply your own start time see [`Wallet::start_full_scan_at`].
2618 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
2619 #[cfg(feature = "std")]
2620 pub fn start_full_scan(&self) -> FullScanRequestBuilder<KeychainKind> {
2621 use bdk_chain::keychain_txout::FullScanRequestBuilderExt;
2622 FullScanRequest::builder()
2623 .chain_tip(self.chain.tip())
2624 .spks_from_indexer(&self.indexed_graph.index)
2625 }
2626
2627 /// Create a [`FullScanRequest`] builder at `start_time`.
2628 pub fn start_full_scan_at(&self, start_time: u64) -> FullScanRequestBuilder<KeychainKind> {
2629 use bdk_chain::keychain_txout::FullScanRequestBuilderExt;
2630 FullScanRequest::builder_at(start_time)
2631 .chain_tip(self.chain.tip())
2632 .spks_from_indexer(&self.indexed_graph.index)
2633 }
2634}
2635
2636impl AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime>> for Wallet {
2637 fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationBlockTime> {
2638 self.indexed_graph.graph()
2639 }
2640}
2641
2642/// Deterministically generate a unique name given the descriptors defining the wallet
2643///
2644/// Compatible with [`wallet_name_from_descriptor`]
2645pub fn wallet_name_from_descriptor<T>(
2646 descriptor: T,
2647 change_descriptor: Option<T>,
2648 network: Network,
2649 secp: &SecpCtx,
2650) -> Result<String, DescriptorError>
2651where
2652 T: IntoWalletDescriptor,
2653{
2654 //TODO check descriptors contains only public keys
2655 let descriptor = descriptor
2656 .into_wallet_descriptor(secp, network)?
2657 .0
2658 .to_string();
2659 let mut wallet_name = descriptor.split_once('#').unwrap().1.to_string();
2660 if let Some(change_descriptor) = change_descriptor {
2661 let change_descriptor = change_descriptor
2662 .into_wallet_descriptor(secp, network)?
2663 .0
2664 .to_string();
2665 wallet_name.push_str(change_descriptor.split_once('#').unwrap().1);
2666 }
2667
2668 Ok(wallet_name)
2669}
2670
2671fn new_local_utxo(
2672 keychain: KeychainKind,
2673 derivation_index: u32,
2674 full_txo: FullTxOut<ConfirmationBlockTime>,
2675) -> LocalOutput {
2676 LocalOutput {
2677 outpoint: full_txo.outpoint,
2678 txout: full_txo.txout,
2679 is_spent: full_txo.spent_by.is_some(),
2680 chain_position: full_txo.chain_position,
2681 keychain,
2682 derivation_index,
2683 }
2684}
2685
2686fn make_indexed_graph(
2687 stage: &mut ChangeSet,
2688 tx_graph_changeset: chain::tx_graph::ChangeSet<ConfirmationBlockTime>,
2689 indexer_changeset: chain::keychain_txout::ChangeSet,
2690 descriptor: ExtendedDescriptor,
2691 change_descriptor: Option<ExtendedDescriptor>,
2692 lookahead: u32,
2693 use_spk_cache: bool,
2694) -> Result<IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<KeychainKind>>, DescriptorError>
2695{
2696 let (indexed_graph, changeset) = IndexedTxGraph::from_changeset(
2697 chain::indexed_tx_graph::ChangeSet {
2698 tx_graph: tx_graph_changeset,
2699 indexer: indexer_changeset,
2700 },
2701 |idx_cs| -> Result<KeychainTxOutIndex<KeychainKind>, DescriptorError> {
2702 let mut idx = KeychainTxOutIndex::from_changeset(lookahead, use_spk_cache, idx_cs);
2703
2704 let descriptor_inserted = idx
2705 .insert_descriptor(KeychainKind::External, descriptor)
2706 .expect("already checked to be a unique, wildcard, non-multipath descriptor");
2707 assert!(
2708 descriptor_inserted,
2709 "this must be the first time we are seeing this descriptor"
2710 );
2711
2712 let change_descriptor = match change_descriptor {
2713 Some(change_descriptor) => change_descriptor,
2714 None => return Ok(idx),
2715 };
2716
2717 let change_descriptor_inserted = idx
2718 .insert_descriptor(KeychainKind::Internal, change_descriptor)
2719 .map_err(|e| {
2720 use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2721 match e {
2722 InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2723 crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2724 }
2725 InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2726 unreachable!("this is the first time we're assigning internal")
2727 }
2728 }
2729 })?;
2730 assert!(
2731 change_descriptor_inserted,
2732 "this must be the first time we are seeing this descriptor"
2733 );
2734
2735 Ok(idx)
2736 },
2737 )?;
2738 stage.tx_graph.merge(changeset.tx_graph);
2739 stage.indexer.merge(changeset.indexer);
2740 Ok(indexed_graph)
2741}
2742
2743/// Transforms a [`FeeRate`] to `f64` with unit as sat/vb.
2744#[macro_export]
2745#[doc(hidden)]
2746macro_rules! floating_rate {
2747 ($rate:expr) => {{
2748 use $crate::bitcoin::constants::WITNESS_SCALE_FACTOR;
2749 // sat_kwu / 250.0 -> sat_vb
2750 $rate.to_sat_per_kwu() as f64 / ((1000 / WITNESS_SCALE_FACTOR) as f64)
2751 }};
2752}
2753
2754#[macro_export]
2755#[doc(hidden)]
2756/// Macro for getting a wallet for use in a doctest
2757macro_rules! doctest_wallet {
2758 () => {{
2759 use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash};
2760 use $crate::chain::{ConfirmationBlockTime, BlockId, TxGraph, tx_graph};
2761 use $crate::{Update, KeychainKind, Wallet};
2762 use $crate::test_utils::*;
2763 let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)";
2764 let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)";
2765
2766 let mut wallet = Wallet::create(descriptor, change_descriptor)
2767 .network(Network::Regtest)
2768 .create_wallet_no_persist()
2769 .unwrap();
2770 let address = wallet.peek_address(KeychainKind::External, 0).address;
2771 let tx = Transaction {
2772 version: transaction::Version::TWO,
2773 lock_time: absolute::LockTime::ZERO,
2774 input: vec![],
2775 output: vec![TxOut {
2776 value: Amount::from_sat(500_000),
2777 script_pubkey: address.script_pubkey(),
2778 }],
2779 };
2780 let txid = tx.compute_txid();
2781 let block_id = BlockId { height: 500, hash: BlockHash::all_zeros() };
2782 insert_checkpoint(&mut wallet, block_id);
2783 insert_checkpoint(&mut wallet, BlockId { height: 1_000, hash: BlockHash::all_zeros() });
2784 insert_tx(&mut wallet, tx);
2785 let anchor = ConfirmationBlockTime {
2786 confirmation_time: 50_000,
2787 block_id,
2788 };
2789 insert_anchor(&mut wallet, txid, anchor);
2790 wallet
2791 }}
2792}
2793
2794#[cfg(test)]
2795mod test {
2796 use super::*;
2797 use crate::test_utils::get_test_tr_single_sig_xprv_and_change_desc;
2798 use crate::test_utils::insert_tx;
2799
2800 #[test]
2801 fn not_duplicated_utxos_across_optional_and_required() {
2802 let (external_desc, internal_desc) = get_test_tr_single_sig_xprv_and_change_desc();
2803
2804 // create new wallet
2805 let mut wallet = Wallet::create(external_desc, internal_desc)
2806 .network(Network::Testnet)
2807 .create_wallet_no_persist()
2808 .unwrap();
2809
2810 let two_output_tx = Transaction {
2811 input: vec![],
2812 output: vec![
2813 TxOut {
2814 script_pubkey: wallet
2815 .next_unused_address(KeychainKind::External)
2816 .script_pubkey(),
2817 value: Amount::from_sat(25_000),
2818 },
2819 TxOut {
2820 script_pubkey: wallet
2821 .next_unused_address(KeychainKind::External)
2822 .script_pubkey(),
2823 value: Amount::from_sat(75_000),
2824 },
2825 ],
2826 version: transaction::Version::non_standard(0),
2827 lock_time: absolute::LockTime::ZERO,
2828 };
2829
2830 let txid = two_output_tx.compute_txid();
2831 insert_tx(&mut wallet, two_output_tx);
2832
2833 let outpoint = OutPoint { txid, vout: 0 };
2834 let mut builder = wallet.build_tx();
2835 builder.add_utxo(outpoint).expect("should add local utxo");
2836 let params = builder.params.clone();
2837 // enforce selection of first output in transaction
2838 let received = wallet.filter_utxos(¶ms, wallet.latest_checkpoint().block_id().height);
2839 // notice expected doesn't include the first output from two_output_tx as it should be
2840 // filtered out
2841 let expected = vec![wallet
2842 .get_utxo(OutPoint { txid, vout: 1 })
2843 .map(|utxo| WeightedUtxo {
2844 satisfaction_weight: wallet
2845 .public_descriptor(utxo.keychain)
2846 .max_weight_to_satisfy()
2847 .unwrap(),
2848 utxo: Utxo::Local(utxo),
2849 })
2850 .unwrap()];
2851
2852 assert_eq!(expected, received);
2853 }
2854
2855 #[test]
2856 fn test_create_two_path_wallet() {
2857 let two_path_descriptor = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)";
2858
2859 // Test successful creation of a two-path wallet
2860 let params = Wallet::create_from_two_path_descriptor(two_path_descriptor);
2861 let wallet = params.network(Network::Testnet).create_wallet_no_persist();
2862 assert!(wallet.is_ok());
2863
2864 let wallet = wallet.unwrap();
2865
2866 // Verify that the wallet has both external and internal keychains
2867 let keychains: Vec<_> = wallet.keychains().collect();
2868 assert_eq!(keychains.len(), 2);
2869
2870 // Verify that the descriptors are different (receive vs change)
2871 let external_desc = keychains
2872 .iter()
2873 .find(|(k, _)| *k == KeychainKind::External)
2874 .unwrap()
2875 .1;
2876 let internal_desc = keychains
2877 .iter()
2878 .find(|(k, _)| *k == KeychainKind::Internal)
2879 .unwrap()
2880 .1;
2881 assert_ne!(external_desc.to_string(), internal_desc.to_string());
2882
2883 // Verify that addresses can be generated
2884 let external_addr = wallet.peek_address(KeychainKind::External, 0);
2885 let internal_addr = wallet.peek_address(KeychainKind::Internal, 0);
2886 assert_ne!(external_addr.address, internal_addr.address);
2887 }
2888
2889 #[test]
2890 fn test_create_two_path_wallet_invalid_descriptor() {
2891 // Test with invalid single-path descriptor
2892 let single_path_descriptor = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/0/*)";
2893 let params = Wallet::create_from_two_path_descriptor(single_path_descriptor);
2894 let wallet = params.network(Network::Testnet).create_wallet_no_persist();
2895 assert!(matches!(wallet, Err(DescriptorError::MultiPath)));
2896
2897 // Test with invalid 3-path multipath descriptor
2898 let three_path_descriptor = "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1;2>/*)";
2899 let params = Wallet::create_from_two_path_descriptor(three_path_descriptor);
2900 let wallet = params.network(Network::Testnet).create_wallet_no_persist();
2901 assert!(matches!(wallet, Err(DescriptorError::MultiPath)));
2902
2903 // Test with completely invalid descriptor
2904 let invalid_descriptor = "invalid_descriptor";
2905 let params = Wallet::create_from_two_path_descriptor(invalid_descriptor);
2906 let wallet = params.network(Network::Testnet).create_wallet_no_persist();
2907 assert!(wallet.is_err());
2908 }
2909}