Skip to content

Commit

Permalink
feat(wallet): add transactions_sort_by function
Browse files Browse the repository at this point in the history
  • Loading branch information
notmandatory committed Jun 18, 2024
1 parent 0543801 commit fb1599d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 14 deletions.
47 changes: 34 additions & 13 deletions crates/wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use bitcoin::{
};
use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
use bitcoin::{constants::genesis_block, Amount};
use core::cmp::Ordering;
use core::fmt;
use core::ops::Deref;
use descriptor::error::Error as DescriptorError;
Expand Down Expand Up @@ -343,6 +344,9 @@ impl fmt::Display for ApplyBlockError {
#[cfg(feature = "std")]
impl std::error::Error for ApplyBlockError {}

/// A `CanonicalTx` managed by a `Wallet`.
pub type WalletTx<'a> = CanonicalTx<'a, Arc<Transaction>, ConfirmationTimeHeightAnchor>;

impl Wallet {
/// Initialize an empty [`Wallet`].
pub fn new<E: IntoWalletDescriptor>(
Expand Down Expand Up @@ -1037,13 +1041,10 @@ impl Wallet {
/// ```
///
/// [`Anchor`]: bdk_chain::Anchor
pub fn get_tx(
&self,
txid: Txid,
) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> {
pub fn get_tx(&self, txid: Txid) -> Option<WalletTx> {
let graph = self.indexed_graph.graph();

Some(CanonicalTx {
Some(WalletTx {
chain_position: graph.get_chain_position(
&self.chain,
self.chain.tip().block_id(),
Expand Down Expand Up @@ -1136,15 +1137,35 @@ impl Wallet {
}

/// Iterate over the transactions in the wallet.
pub fn transactions(
&self,
) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> + '_
{
pub fn transactions(&self) -> impl Iterator<Item = WalletTx> + '_ {
self.indexed_graph
.graph()
.list_chain_txs(&self.chain, self.chain.tip().block_id())
}

/// Array of transactions in the wallet sorted with a comparator function.
///
/// # Example
///
/// ```rust,no_run
/// # use bdk_wallet::Wallet;
/// # use bdk_wallet::wallet::WalletTx;
/// # let changeset = Default::default();
/// # let mut wallet = Wallet::load_from_changeset(changeset)?;
/// // Transactions by chain position: first unconfirmed then descending by confirmed height.
/// let sorted_txs: Vec<WalletTx> =
/// wallet.transactions_sort_by(|tx1, tx2| tx2.chain_position.cmp(&tx1.chain_position));
/// # Ok::<(), anyhow::Error>(())
/// ```
pub fn transactions_sort_by<F>(&self, compare: F) -> Vec<WalletTx>
where
F: FnMut(&WalletTx, &WalletTx) -> Ordering,
{
let mut txs: Vec<WalletTx> = self.transactions().collect();
txs.sort_unstable_by(compare);
txs
}

/// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
/// values.
pub fn balance(&self) -> Balance {
Expand Down Expand Up @@ -1287,7 +1308,7 @@ impl Wallet {
let version = match params.version {
Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
Some(tx_builder::Version(1)) if requirements.csv.is_some() => {
return Err(CreateTxError::Version1Csv)
return Err(CreateTxError::Version1Csv);
}
Some(tx_builder::Version(x)) => x,
None if requirements.csv.is_some() => 2,
Expand Down Expand Up @@ -1340,7 +1361,7 @@ impl Wallet {
return Err(CreateTxError::LockTime {
requested: x,
required: requirements.timelock.unwrap(),
})
});
}
};

Expand All @@ -1360,13 +1381,13 @@ impl Wallet {

// RBF with a specific value but that value is too high
(Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
return Err(CreateTxError::RbfSequence)
return Err(CreateTxError::RbfSequence);
}
// RBF with a specific value requested, but the value is incompatible with CSV
(Some(tx_builder::RbfValue::Value(rbf)), Some(csv))
if !check_nsequence_rbf(rbf, csv) =>
{
return Err(CreateTxError::RbfSequenceCsv { rbf, csv })
return Err(CreateTxError::RbfSequenceCsv { rbf, csv });
}

// RBF enabled with the default value with CSV also enabled. CSV takes precedence
Expand Down
17 changes: 16 additions & 1 deletion crates/wallet/tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use bdk_wallet::signer::{SignOptions, SignerError};
use bdk_wallet::wallet::coin_selection::{self, LargestFirstCoinSelection};
use bdk_wallet::wallet::error::CreateTxError;
use bdk_wallet::wallet::tx_builder::AddForeignUtxoError;
use bdk_wallet::wallet::{AddressInfo, Balance, ChangeSet, NewError, Wallet};
use bdk_wallet::wallet::{AddressInfo, Balance, ChangeSet, NewError, Wallet, WalletTx};
use bdk_wallet::KeychainKind;
use bitcoin::hashes::Hash;
use bitcoin::key::Secp256k1;
Expand Down Expand Up @@ -4079,3 +4079,18 @@ fn test_thread_safety() {
fn thread_safe<T: Send + Sync>() {}
thread_safe::<Wallet>(); // compiles only if true
}

#[test]
fn test_transactions_sort_by() {
let (mut wallet, _txid) = get_funded_wallet_wpkh();
receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));

// sort by chain position, unconfirmed then confirmed by descending block height
let sorted_txs: Vec<WalletTx> =
wallet.transactions_sort_by(|t1, t2| t2.chain_position.cmp(&t1.chain_position));
let conf_heights: Vec<Option<u32>> = sorted_txs
.iter()
.map(|tx| tx.chain_position.confirmation_height_upper_bound())
.collect();
assert_eq!([None, Some(2000), Some(1000)], conf_heights.as_slice());
}

0 comments on commit fb1599d

Please sign in to comment.