diff --git a/crates/bdk/src/types.rs b/crates/bdk/src/types.rs index 2a88cc374..b52635e55 100644 --- a/crates/bdk/src/types.rs +++ b/crates/bdk/src/types.rs @@ -161,7 +161,7 @@ impl Vbytes for usize { /// /// [`Wallet`]: crate::Wallet #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -pub struct LocalUtxo { +pub struct LocalOutput { /// Reference to a transaction output pub outpoint: OutPoint, /// Transaction output @@ -192,7 +192,7 @@ pub struct WeightedUtxo { /// An unspent transaction output (UTXO). pub enum Utxo { /// A UTXO owned by the local wallet. - Local(LocalUtxo), + Local(LocalOutput), /// A UTXO owned by another wallet. Foreign { /// The location of the output. diff --git a/crates/bdk/src/wallet/coin_selection.rs b/crates/bdk/src/wallet/coin_selection.rs index 3f4116f5f..8829ba3aa 100644 --- a/crates/bdk/src/wallet/coin_selection.rs +++ b/crates/bdk/src/wallet/coin_selection.rs @@ -742,7 +742,7 @@ mod test { .unwrap(); WeightedUtxo { satisfaction_weight: P2WPKH_SATISFACTION_SIZE, - utxo: Utxo::Local(LocalUtxo { + utxo: Utxo::Local(LocalOutput { outpoint, txout: TxOut { value, @@ -802,7 +802,7 @@ mod test { for _ in 0..utxos_number { res.push(WeightedUtxo { satisfaction_weight: P2WPKH_SATISFACTION_SIZE, - utxo: Utxo::Local(LocalUtxo { + utxo: Utxo::Local(LocalOutput { outpoint: OutPoint::from_str( "ebd9813ecebc57ff8f30797de7c205e3c7498ca950ea4341ee51a685ff2fa30a:0", ) @@ -831,7 +831,7 @@ mod test { fn generate_same_value_utxos(utxos_value: u64, utxos_number: usize) -> Vec { let utxo = WeightedUtxo { satisfaction_weight: P2WPKH_SATISFACTION_SIZE, - utxo: Utxo::Local(LocalUtxo { + utxo: Utxo::Local(LocalOutput { outpoint: OutPoint::from_str( "ebd9813ecebc57ff8f30797de7c205e3c7498ca950ea4341ee51a685ff2fa30a:0", ) diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index b91b70ca2..4f0687690 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -735,7 +735,7 @@ impl Wallet { } /// Return the list of unspent outputs of this wallet - pub fn list_unspent(&self) -> impl Iterator + '_ { + pub fn list_unspent(&self) -> impl Iterator + '_ { self.indexed_graph .graph() .filter_chain_unspents( @@ -746,6 +746,20 @@ impl Wallet { .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo)) } + /// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed). + /// + /// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead. + pub fn list_output(&self) -> impl Iterator + '_ { + self.indexed_graph + .graph() + .filter_chain_txouts( + &self.chain, + self.chain.tip().block_id(), + self.indexed_graph.index.outpoints().iter().cloned(), + ) + .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo)) + } + /// Get all the checkpoints the wallet is currently storing indexed by height. pub fn checkpoints(&self) -> CheckPointIter { self.chain.iter_checkpoints() @@ -784,7 +798,7 @@ impl Wallet { /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the /// wallet's database. - pub fn get_utxo(&self, op: OutPoint) -> Option { + pub fn get_utxo(&self, op: OutPoint) -> Option { let (&spk_i, _) = self.indexed_graph.index.txout(op)?; self.indexed_graph .graph() @@ -798,15 +812,20 @@ impl Wallet { } /// Inserts a [`TxOut`] at [`OutPoint`] into the wallet's transaction graph. - /// Any inserted TxOuts are not persisted until [`commit`] is called. /// - /// This can be used to add a `TxOut` that the wallet doesn't own but is used as an input to - /// a [`Transaction`] passed to the [`calculate_fee`] or [`calculate_fee_rate`] functions. + /// This is used for providing a previous output's value so that we can use [`calculate_fee`] + /// or [`calculate_fee_rate`] on a given transaction. Outputs inserted with this method will + /// not be returned in [`list_unspent`] or [`list_output`]. + /// + /// Any inserted `TxOut`s are not persisted until [`commit`] is called. /// - /// Only insert TxOuts you trust the values for! + /// **WARNING:** This should only be used to add `TxOut`s that the wallet does not own. Only + /// insert `TxOut`s that you trust the values for! /// /// [`calculate_fee`]: Self::calculate_fee /// [`calculate_fee_rate`]: Self::calculate_fee_rate + /// [`list_unspent`]: Self::list_unspent + /// [`list_output`]: Self::list_output /// [`commit`]: Self::commit pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) where @@ -1614,7 +1633,7 @@ impl Wallet { .max_satisfaction_weight() .unwrap(); WeightedUtxo { - utxo: Utxo::Local(LocalUtxo { + utxo: Utxo::Local(LocalOutput { outpoint: txin.previous_output, txout: txout.clone(), keychain, @@ -1933,7 +1952,7 @@ impl Wallet { descriptor.at_derivation_index(child).ok() } - fn get_available_utxos(&self) -> Vec<(LocalUtxo, usize)> { + fn get_available_utxos(&self) -> Vec<(LocalOutput, usize)> { self.list_unspent() .map(|utxo| { let keychain = utxo.keychain; @@ -2130,7 +2149,7 @@ impl Wallet { /// get the corresponding PSBT Input for a LocalUtxo pub fn get_psbt_input( &self, - utxo: LocalUtxo, + utxo: LocalOutput, sighash_type: Option, only_witness_utxo: bool, ) -> Result> @@ -2336,8 +2355,8 @@ fn new_local_utxo( keychain: KeychainKind, derivation_index: u32, full_txo: FullTxOut, -) -> LocalUtxo { - LocalUtxo { +) -> LocalOutput { + LocalOutput { outpoint: full_txo.outpoint, txout: full_txo.txout, is_spent: full_txo.spent_by.is_some(), diff --git a/crates/bdk/src/wallet/tx_builder.rs b/crates/bdk/src/wallet/tx_builder.rs index e99d2fe25..e3474209c 100644 --- a/crates/bdk/src/wallet/tx_builder.rs +++ b/crates/bdk/src/wallet/tx_builder.rs @@ -53,7 +53,7 @@ use bitcoin::{absolute, script::PushBytes, OutPoint, ScriptBuf, Sequence, Transa use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm}; use super::ChangeSet; -use crate::types::{FeeRate, KeychainKind, LocalUtxo, WeightedUtxo}; +use crate::types::{FeeRate, KeychainKind, LocalOutput, WeightedUtxo}; use crate::wallet::CreateTxError; use crate::{Utxo, Wallet}; @@ -889,7 +889,7 @@ impl Default for ChangeSpendPolicy { } impl ChangeSpendPolicy { - pub(crate) fn is_satisfied_by(&self, utxo: &LocalUtxo) -> bool { + pub(crate) fn is_satisfied_by(&self, utxo: &LocalOutput) -> bool { match self { ChangeSpendPolicy::ChangeAllowed => true, ChangeSpendPolicy::OnlyChange => utxo.keychain == KeychainKind::Internal, @@ -994,11 +994,11 @@ mod test { ); } - fn get_test_utxos() -> Vec { + fn get_test_utxos() -> Vec { use bitcoin::hashes::Hash; vec![ - LocalUtxo { + LocalOutput { outpoint: OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), vout: 0, @@ -1009,7 +1009,7 @@ mod test { confirmation_time: ConfirmationTime::Unconfirmed { last_seen: 0 }, derivation_index: 0, }, - LocalUtxo { + LocalOutput { outpoint: OutPoint { txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(), vout: 1, diff --git a/crates/bdk/tests/common.rs b/crates/bdk/tests/common.rs index ee8ed74e1..3e0292a29 100644 --- a/crates/bdk/tests/common.rs +++ b/crates/bdk/tests/common.rs @@ -1,6 +1,6 @@ #![allow(unused)] -use bdk::{wallet::AddressIndex, KeychainKind, LocalUtxo, Wallet}; +use bdk::{wallet::AddressIndex, KeychainKind, LocalOutput, Wallet}; use bdk_chain::indexed_tx_graph::Indexer; use bdk_chain::{BlockId, ConfirmationTime}; use bitcoin::hashes::Hash; diff --git a/crates/bdk/tests/wallet.rs b/crates/bdk/tests/wallet.rs index 77ec1c8b1..4fa399d86 100644 --- a/crates/bdk/tests/wallet.rs +++ b/crates/bdk/tests/wallet.rs @@ -237,6 +237,25 @@ fn test_get_funded_wallet_tx_fee_rate() { assert_eq!(tx_fee_rate.as_sat_per_vb(), 8.849558); } +#[test] +fn test_list_output() { + let (wallet, txid) = get_funded_wallet(get_test_wpkh()); + let txos = wallet + .list_output() + .map(|op| (op.outpoint, op)) + .collect::>(); + assert_eq!(txos.len(), 2); + for (op, txo) in txos { + if op.txid == txid { + assert_eq!(txo.txout.value, 50_000); + assert!(!txo.is_spent); + } else { + assert_eq!(txo.txout.value, 76_000); + assert!(txo.is_spent); + } + } +} + macro_rules! assert_fee_rate { ($psbt:expr, $fees:expr, $fee_rate:expr $( ,@dust_change $( $dust_change:expr )* )* $( ,@add_signature $( $add_signature:expr )* )* ) => ({ let psbt = $psbt.clone();