Skip to content

Commit 151e49e

Browse files
committed
Merge #201: Bring back TxDetails
6aef191 feat: add Wallet::tx_details method (thunderbiscuit) Pull request description: Fixes #100. The `TxDetails` feature was put aside to focus on the 1.0 release, but now that we're done with that I want to revive the idea. This is very much WIP. I'm trying to figure out exactly what we want in this data structure, and I'm not even sure my way to get at the data is the most efficient way to do it. ### Notes to the reviewers ### Changelog notice ```md Added: - Wallet::get_tx_details method [#201] [#201]: #201 ``` ### Checklists #### All Submissions: * [ ] I've signed all my commits * [ ] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [ ] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [ ] I've added tests for the new feature * [ ] I've added docs for the new feature #### Bugfixes: * [ ] This pull request breaks the existing API * [ ] I've added tests to reproduce the issue which are now passing * [ ] I'm linking the issue being fixed by this PR ACKs for top commit: ValuedMammal: ACK 6aef191 Tree-SHA512: ae6d509279601b70cbcd000d42a8755928b66062d3efc4f69b0b452018e82057e7879e2cde9bfcdbf1ff8ae82daa9b660041a464bde2190fdc9338484f165984
2 parents f0840c0 + 6aef191 commit 151e49e

File tree

3 files changed

+85
-4
lines changed

3 files changed

+85
-4
lines changed

wallet/src/wallet/mod.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use bitcoin::{
4141
secp256k1::Secp256k1,
4242
sighash::{EcdsaSighashType, TapSighashType},
4343
transaction, Address, Amount, Block, BlockHash, FeeRate, Network, OutPoint, Psbt, ScriptBuf,
44-
Sequence, Transaction, TxOut, Txid, Weight, Witness,
44+
Sequence, SignedAmount, Transaction, TxOut, Txid, Weight, Witness,
4545
};
4646
use miniscript::{
4747
descriptor::KeyMap,
@@ -81,6 +81,7 @@ pub use changeset::ChangeSet;
8181
pub use params::*;
8282
pub use persisted::*;
8383
pub use utils::IsDust;
84+
pub use utils::TxDetails;
8485

8586
/// A Bitcoin wallet
8687
///
@@ -861,6 +862,33 @@ impl Wallet {
861862
.map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
862863
}
863864

865+
/// Get the [`TxDetails`] of a wallet transaction.
866+
///
867+
/// If the transaction with txid [`Txid`] cannot be found in the wallet's transactions, `None`
868+
/// is returned.
869+
pub fn tx_details(&self, txid: Txid) -> Option<TxDetails> {
870+
let tx: WalletTx = self.transactions().find(|c| c.tx_node.txid == txid)?;
871+
872+
let (sent, received) = self.sent_and_received(&tx.tx_node.tx);
873+
let fee: Option<Amount> = self.calculate_fee(&tx.tx_node.tx).ok();
874+
let fee_rate: Option<FeeRate> = self.calculate_fee_rate(&tx.tx_node.tx).ok();
875+
let balance_delta: SignedAmount = self.indexed_graph.index.net_value(&tx.tx_node.tx, ..);
876+
let chain_position = tx.chain_position;
877+
878+
let tx_details: TxDetails = TxDetails {
879+
txid,
880+
received,
881+
sent,
882+
fee,
883+
fee_rate,
884+
balance_delta,
885+
chain_position,
886+
tx: tx.tx_node.tx,
887+
};
888+
889+
Some(tx_details)
890+
}
891+
864892
/// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
865893
///
866894
/// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.

wallet/src/wallet/utils.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
// You may not use this file except in accordance with one or both of these
1010
// licenses.
1111

12+
use alloc::sync::Arc;
1213
use bitcoin::secp256k1::{All, Secp256k1};
13-
use bitcoin::{absolute, relative, Amount, Script, Sequence};
14-
14+
use bitcoin::{
15+
absolute, relative, Amount, FeeRate, Script, Sequence, SignedAmount, Transaction, Txid,
16+
};
17+
use chain::{ChainPosition, ConfirmationBlockTime};
1518
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
1619

1720
use rand_core::RngCore;
@@ -133,6 +136,33 @@ pub(crate) fn shuffle_slice<T>(list: &mut [T], rng: &mut impl RngCore) {
133136

134137
pub(crate) type SecpCtx = Secp256k1<All>;
135138

139+
/// Details about a transaction affecting the wallet (relevant and canonical).
140+
#[derive(Debug)]
141+
pub struct TxDetails {
142+
/// The transaction id.
143+
pub txid: Txid,
144+
/// The sum of the transaction input amounts that spend from previous outputs tracked by this
145+
/// wallet.
146+
pub sent: Amount,
147+
/// The sum of the transaction outputs that send to script pubkeys tracked by this wallet.
148+
pub received: Amount,
149+
/// The fee paid for the transaction. Note that to calculate the fee for a transaction with
150+
/// inputs not owned by this wallet you must manually insert the TxOut(s) into the tx graph
151+
/// using the insert_txout function. If those are not available, the field will be `None`.
152+
pub fee: Option<Amount>,
153+
/// The fee rate paid for the transaction. Note that to calculate the fee rate for a
154+
/// transaction with inputs not owned by this wallet you must manually insert the TxOut(s) into
155+
/// the tx graph using the insert_txout function. If those are not available, the field will be
156+
/// `None`.
157+
pub fee_rate: Option<FeeRate>,
158+
/// The net effect of the transaction on the balance of the wallet.
159+
pub balance_delta: SignedAmount,
160+
/// The position of the transaction in the chain.
161+
pub chain_position: ChainPosition<ConfirmationBlockTime>,
162+
/// The complete [`Transaction`].
163+
pub tx: Arc<Transaction>,
164+
}
165+
136166
#[cfg(test)]
137167
mod test {
138168
// When nSequence is lower than this flag the timelock is interpreted as block-height-based,

wallet/tests/wallet.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
1919
use bitcoin::constants::{ChainHash, COINBASE_MATURITY};
2020
use bitcoin::hashes::Hash;
2121
use bitcoin::key::Secp256k1;
22-
use bitcoin::psbt;
2322
use bitcoin::script::PushBytesBuf;
2423
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
2524
use bitcoin::taproot::TapNodeHash;
2625
use bitcoin::{
2726
absolute, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, ScriptBuf,
2827
Sequence, Transaction, TxIn, TxOut, Txid, Weight,
2928
};
29+
use bitcoin::{psbt, SignedAmount};
3030
use miniscript::{descriptor::KeyMap, Descriptor, DescriptorPublicKey};
3131
use rand::rngs::StdRng;
3232
use rand::SeedableRng;
@@ -4617,3 +4617,26 @@ fn test_wallet_transactions_relevant() {
46174617
assert!(full_tx_count_before < full_tx_count_after);
46184618
assert!(canonical_tx_count_before < canonical_tx_count_after);
46194619
}
4620+
4621+
#[test]
4622+
fn test_tx_details_method() {
4623+
let (test_wallet, txid_1) = get_funded_wallet_wpkh();
4624+
let tx_details_1_option = test_wallet.tx_details(txid_1);
4625+
4626+
assert!(tx_details_1_option.is_some());
4627+
let tx_details_1 = tx_details_1_option.unwrap();
4628+
4629+
assert_eq!(
4630+
tx_details_1.txid.to_string(),
4631+
"f2a03cdfe1bb6a295b0a4bb4385ca42f95e4b2c6d9a7a59355d32911f957a5b3"
4632+
);
4633+
assert_eq!(tx_details_1.received, Amount::from_sat(50000));
4634+
assert_eq!(tx_details_1.sent, Amount::from_sat(76000));
4635+
assert_eq!(tx_details_1.fee.unwrap(), Amount::from_sat(1000));
4636+
assert_eq!(tx_details_1.balance_delta, SignedAmount::from_sat(-26000));
4637+
4638+
// Transaction id not part of the TxGraph
4639+
let txid_2 = Txid::from_raw_hash(Hash::all_zeros());
4640+
let tx_details_2_option = test_wallet.tx_details(txid_2);
4641+
assert!(tx_details_2_option.is_none());
4642+
}

0 commit comments

Comments
 (0)