Skip to content

Commit f69eab6

Browse files
committed
Merge #1477: feat(wallet): add transactions_sort_by function
83a0247 feat(wallet): add transactions_sort_by function (Steve Myers) Pull request description: ### Description Added new type alias `WalletTx` which represents a `CanonicalTx<'a, Arc<Transaction>, ConfirmationTimeHeightAnchor>` and new `Wallet::transactions_sort_by` that returns a `Vec<WalletTx>` sorted by the given compare function. ### Notes to the reviewers fixes #794 ### Changelog notice * Add new type alias `WalletTx` which represents a `CanonicalTx<'a, Arc<Transaction>, ConfirmationTimeHeightAnchor>`. * Add `Wallet::transactions_sort_by()` that returns a `Vec<WalletTx>` sorted by a given compare function. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added tests for the new feature * [x] I've added docs for the new feature Top commit has no ACKs. Tree-SHA512: 5758b5edf8200b5534b7a7f538de640e85083bed3da2585109190f0efda3e238f5483d4a2dc073dc4b907644f58b7158a922ebe09d03a47201030162d4b0f4d3
2 parents 23bae3e + 83a0247 commit f69eab6

2 files changed

Lines changed: 50 additions & 15 deletions

File tree

crates/wallet/src/wallet/mod.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use bitcoin::{
4545
use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
4646
use bitcoin::{constants::genesis_block, Amount};
4747
use bitcoin::{secp256k1::Secp256k1, Weight};
48+
use core::cmp::Ordering;
4849
use core::fmt;
4950
use core::mem;
5051
use core::ops::Deref;
@@ -291,6 +292,9 @@ impl fmt::Display for ApplyBlockError {
291292
#[cfg(feature = "std")]
292293
impl std::error::Error for ApplyBlockError {}
293294

295+
/// A `CanonicalTx` managed by a `Wallet`.
296+
pub type WalletTx<'a> = CanonicalTx<'a, Arc<Transaction>, ConfirmationBlockTime>;
297+
294298
impl Wallet {
295299
/// Build a new single descriptor [`Wallet`].
296300
///
@@ -1002,9 +1006,9 @@ impl Wallet {
10021006
self.indexed_graph.index.sent_and_received(tx, ..)
10031007
}
10041008

1005-
/// Get a single transaction from the wallet as a [`CanonicalTx`] (if the transaction exists).
1009+
/// Get a single transaction from the wallet as a [`WalletTx`] (if the transaction exists).
10061010
///
1007-
/// `CanonicalTx` contains the full transaction alongside meta-data such as:
1011+
/// `WalletTx` contains the full transaction alongside meta-data such as:
10081012
/// * Blocks that the transaction is [`Anchor`]ed in. These may or may not be blocks that exist
10091013
/// in the best chain.
10101014
/// * The [`ChainPosition`] of the transaction in the best chain - whether the transaction is
@@ -1018,21 +1022,21 @@ impl Wallet {
10181022
/// # let wallet: Wallet = todo!();
10191023
/// # let my_txid: bitcoin::Txid = todo!();
10201024
///
1021-
/// let canonical_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
1025+
/// let wallet_tx = wallet.get_tx(my_txid).expect("panic if tx does not exist");
10221026
///
10231027
/// // get reference to full transaction
1024-
/// println!("my tx: {:#?}", canonical_tx.tx_node.tx);
1028+
/// println!("my tx: {:#?}", wallet_tx.tx_node.tx);
10251029
///
10261030
/// // list all transaction anchors
1027-
/// for anchor in canonical_tx.tx_node.anchors {
1031+
/// for anchor in wallet_tx.tx_node.anchors {
10281032
/// println!(
10291033
/// "tx is anchored by block of hash {}",
10301034
/// anchor.anchor_block().hash
10311035
/// );
10321036
/// }
10331037
///
10341038
/// // get confirmation status of transaction
1035-
/// match canonical_tx.chain_position {
1039+
/// match wallet_tx.chain_position {
10361040
/// ChainPosition::Confirmed(anchor) => println!(
10371041
/// "tx is confirmed at height {}, we know this since {}:{} is in the best chain",
10381042
/// anchor.block_id.height, anchor.block_id.height, anchor.block_id.hash,
@@ -1045,13 +1049,10 @@ impl Wallet {
10451049
/// ```
10461050
///
10471051
/// [`Anchor`]: bdk_chain::Anchor
1048-
pub fn get_tx(
1049-
&self,
1050-
txid: Txid,
1051-
) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationBlockTime>> {
1052+
pub fn get_tx(&self, txid: Txid) -> Option<WalletTx> {
10521053
let graph = self.indexed_graph.graph();
10531054

1054-
Some(CanonicalTx {
1055+
Some(WalletTx {
10551056
chain_position: graph.get_chain_position(
10561057
&self.chain,
10571058
self.chain.tip().block_id(),
@@ -1102,14 +1103,33 @@ impl Wallet {
11021103
}
11031104

11041105
/// Iterate over the transactions in the wallet.
1105-
pub fn transactions(
1106-
&self,
1107-
) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationBlockTime>> + '_ {
1106+
pub fn transactions(&self) -> impl Iterator<Item = WalletTx> + '_ {
11081107
self.indexed_graph
11091108
.graph()
11101109
.list_canonical_txs(&self.chain, self.chain.tip().block_id())
11111110
}
11121111

1112+
/// Array of transactions in the wallet sorted with a comparator function.
1113+
///
1114+
/// # Example
1115+
///
1116+
/// ```rust,no_run
1117+
/// # use bdk_wallet::{LoadParams, Wallet, WalletTx};
1118+
/// # let mut wallet:Wallet = todo!();
1119+
/// // Transactions by chain position: first unconfirmed then descending by confirmed height.
1120+
/// let sorted_txs: Vec<WalletTx> =
1121+
/// wallet.transactions_sort_by(|tx1, tx2| tx2.chain_position.cmp(&tx1.chain_position));
1122+
/// # Ok::<(), anyhow::Error>(())
1123+
/// ```
1124+
pub fn transactions_sort_by<F>(&self, compare: F) -> Vec<WalletTx>
1125+
where
1126+
F: FnMut(&WalletTx, &WalletTx) -> Ordering,
1127+
{
1128+
let mut txs: Vec<WalletTx> = self.transactions().collect();
1129+
txs.sort_unstable_by(compare);
1130+
txs
1131+
}
1132+
11131133
/// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
11141134
/// values.
11151135
pub fn balance(&self) -> Balance {

crates/wallet/tests/wallet.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bdk_wallet::error::CreateTxError;
1313
use bdk_wallet::psbt::PsbtUtils;
1414
use bdk_wallet::signer::{SignOptions, SignerError};
1515
use bdk_wallet::tx_builder::AddForeignUtxoError;
16-
use bdk_wallet::{AddressInfo, Balance, ChangeSet, Wallet, WalletPersister};
16+
use bdk_wallet::{AddressInfo, Balance, ChangeSet, Wallet, WalletPersister, WalletTx};
1717
use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
1818
use bitcoin::constants::ChainHash;
1919
use bitcoin::hashes::Hash;
@@ -4203,3 +4203,18 @@ fn single_descriptor_wallet_can_create_tx_and_receive_change() {
42034203
"tx change should go to external keychain"
42044204
);
42054205
}
4206+
4207+
#[test]
4208+
fn test_transactions_sort_by() {
4209+
let (mut wallet, _txid) = get_funded_wallet_wpkh();
4210+
receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
4211+
4212+
// sort by chain position, unconfirmed then confirmed by descending block height
4213+
let sorted_txs: Vec<WalletTx> =
4214+
wallet.transactions_sort_by(|t1, t2| t2.chain_position.cmp(&t1.chain_position));
4215+
let conf_heights: Vec<Option<u32>> = sorted_txs
4216+
.iter()
4217+
.map(|tx| tx.chain_position.confirmation_height_upper_bound())
4218+
.collect();
4219+
assert_eq!([None, Some(2000), Some(1000)], conf_heights.as_slice());
4220+
}

0 commit comments

Comments
 (0)