Skip to content

Bring back TxDetails #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use bitcoin::{
secp256k1::Secp256k1,
sighash::{EcdsaSighashType, TapSighashType},
transaction, Address, Amount, Block, BlockHash, FeeRate, Network, OutPoint, Psbt, ScriptBuf,
Sequence, Transaction, TxOut, Txid, Weight, Witness,
Sequence, SignedAmount, Transaction, TxOut, Txid, Weight, Witness,
};
use miniscript::{
descriptor::KeyMap,
Expand Down Expand Up @@ -72,7 +72,7 @@ use crate::wallet::{
error::{BuildFeeBumpError, CreateTxError, MiniscriptPsbtError},
signer::{SignOptions, SignerError, SignerOrdering, SignersContainer, TransactionSigner},
tx_builder::{FeePolicy, TxBuilder, TxParams},
utils::{check_nsequence_rbf, After, Older, SecpCtx},
utils::{check_nsequence_rbf, After, Older, SecpCtx, TxDetails},
};

// re-exports
Expand Down Expand Up @@ -822,6 +822,33 @@ impl Wallet {
.map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
}

/// Build a [`TxDetails`] struct for a given transaction.
///
/// If the transaction with txid [`Txid`] cannot be found in the wallet's transaction graph,
/// `None` is returned.
pub fn get_tx_details(&self, txid: Txid) -> Option<TxDetails> {
let tx: WalletTx = self.transactions().find(|c| c.tx_node.txid == txid)?;

let (sent, received) = self.sent_and_received(&tx.tx_node.tx);
let fee: Option<Amount> = self.calculate_fee(&tx.tx_node.tx).ok();
let fee_rate: Option<FeeRate> = self.calculate_fee_rate(&tx.tx_node.tx).ok();
let balance_delta: SignedAmount = self.indexed_graph.index.net_value(&tx.tx_node.tx, ..);
let chain_position = tx.chain_position;

let tx_details: TxDetails = TxDetails {
txid,
received,
sent,
fee,
fee_rate,
balance_delta,
chain_position,
tx: tx.tx_node.tx,
};

Some(tx_details)
}

/// List all relevant outputs (includes both spent and unspent, confirmed and unconfirmed).
///
/// To list only unspent outputs (UTXOs), use [`Wallet::list_unspent`] instead.
Expand Down
2 changes: 1 addition & 1 deletion wallet/src/wallet/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ fn sign_psbt_schnorr(
let keypair = match leaf_hash {
None => keypair
.tap_tweak(secp, psbt_input.tap_merkle_root)
.to_inner(),
.to_keypair(),
Some(_) => keypair, // no tweak for script spend
};

Expand Down
20 changes: 18 additions & 2 deletions wallet/src/wallet/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
// You may not use this file except in accordance with one or both of these
// licenses.

use alloc::sync::Arc;
use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::{absolute, relative, Amount, Script, Sequence};

use bitcoin::{
absolute, relative, Amount, FeeRate, Script, Sequence, SignedAmount, Transaction, Txid,
};
use chain::{ChainPosition, ConfirmationBlockTime};
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};

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

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

/// Details about a transaction the wallet knows about.
#[derive(Debug)]
pub struct TxDetails {
pub txid: Txid,
pub received: Amount,
pub sent: Amount,
pub fee: Option<Amount>,
pub fee_rate: Option<FeeRate>,
pub balance_delta: SignedAmount,
pub chain_position: ChainPosition<ConfirmationBlockTime>,
pub tx: Arc<Transaction>,
}

#[cfg(test)]
mod test {
// When nSequence is lower than this flag the timelock is interpreted as block-height-based,
Expand Down
2 changes: 1 addition & 1 deletion wallet/tests/psbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ fn test_psbt_multiple_internalkey_signers() {
let message = Message::from(sighash);

// add tweak. this was taken from `signer::sign_psbt_schnorr`
let keypair = keypair.tap_tweak(&secp, None).to_inner();
let keypair = keypair.tap_tweak(&secp, None).to_keypair();
let (xonlykey, _parity) = XOnlyPublicKey::from_keypair(&keypair);

// Must verify if we used the correct key to sign
Expand Down
26 changes: 25 additions & 1 deletion wallet/tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ use bdk_wallet::{KeychainKind, LoadError, LoadMismatch, LoadWithPersistError};
use bitcoin::constants::{ChainHash, COINBASE_MATURITY};
use bitcoin::hashes::Hash;
use bitcoin::key::Secp256k1;
use bitcoin::psbt;
use bitcoin::script::PushBytesBuf;
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::taproot::TapNodeHash;
use bitcoin::{
absolute, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, ScriptBuf,
Sequence, Transaction, TxIn, TxOut, Txid, Weight,
};
use bitcoin::{psbt, SignedAmount};
use miniscript::{descriptor::KeyMap, Descriptor, DescriptorPublicKey};
use rand::rngs::StdRng;
use rand::SeedableRng;
Expand Down Expand Up @@ -4314,3 +4314,27 @@ fn test_wallet_transactions_relevant() {
assert!(full_tx_count_before < full_tx_count_after);
assert!(canonical_tx_count_before < canonical_tx_count_after);
}

#[test]
fn test_tx_details_method() {
let (test_wallet, txid_1) = get_funded_wallet_wpkh();
let tx_details_1_option = test_wallet.get_tx_details(txid_1);
dbg!(&tx_details_1_option);

assert!(tx_details_1_option.is_some());
let tx_details_1 = tx_details_1_option.unwrap();

assert_eq!(
tx_details_1.txid.to_string(),
"f2a03cdfe1bb6a295b0a4bb4385ca42f95e4b2c6d9a7a59355d32911f957a5b3"
);
assert_eq!(tx_details_1.received, Amount::from_sat(50000));
assert_eq!(tx_details_1.sent, Amount::from_sat(76000));
assert_eq!(tx_details_1.fee.unwrap(), Amount::from_sat(1000));
assert_eq!(tx_details_1.balance_delta, SignedAmount::from_sat(-26000));

// Transaction id not part of the TxGraph
let txid_2 = Txid::from_raw_hash(Hash::all_zeros());
let tx_details_2_option = test_wallet.get_tx_details(txid_2);
assert!(tx_details_2_option.is_none());
}
Loading