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