Skip to content

Commit 8ab58af

Browse files
committed
feat(chain)!: wrap TxGraph txs with Arc
Wrapping transactions as `Arc<Transaction>` allows us to share transactions cheaply between the chain-source and receiving structures. Therefore the chain-source can keep already-fetched transactions (save bandwidth) and have a shared pointer to the transactions (save memory). This is better than the current way we do things, which is to refer back to the receiving structures mid-sync. Documentation for `TxGraph` is also updated.
1 parent 80e190b commit 8ab58af

File tree

8 files changed

+150
-114
lines changed

8 files changed

+150
-114
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,7 @@ impl<D> Wallet<D> {
942942
/// # let mut wallet: Wallet<()> = todo!();
943943
/// # let txid:Txid = todo!();
944944
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
945-
/// let fee = wallet.calculate_fee(tx).expect("fee");
945+
/// let fee = wallet.calculate_fee(&tx).expect("fee");
946946
/// ```
947947
///
948948
/// ```rust, no_run
@@ -973,16 +973,16 @@ impl<D> Wallet<D> {
973973
/// # let mut wallet: Wallet<()> = todo!();
974974
/// # let txid:Txid = todo!();
975975
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
976-
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
976+
/// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
977977
/// ```
978978
///
979979
/// ```rust, no_run
980980
/// # use bitcoin::psbt::PartiallySignedTransaction;
981981
/// # use bdk::Wallet;
982982
/// # let mut wallet: Wallet<()> = todo!();
983983
/// # let mut psbt: PartiallySignedTransaction = todo!();
984-
/// let tx = &psbt.clone().extract_tx();
985-
/// let fee_rate = wallet.calculate_fee_rate(tx).expect("fee rate");
984+
/// let tx = psbt.clone().extract_tx();
985+
/// let fee_rate = wallet.calculate_fee_rate(&tx).expect("fee rate");
986986
/// ```
987987
/// [`insert_txout`]: Self::insert_txout
988988
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<FeeRate, CalculateFeeError> {
@@ -1003,8 +1003,8 @@ impl<D> Wallet<D> {
10031003
/// # use bdk::Wallet;
10041004
/// # let mut wallet: Wallet<()> = todo!();
10051005
/// # let txid:Txid = todo!();
1006-
/// let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
1007-
/// let (sent, received) = wallet.sent_and_received(tx);
1006+
/// let tx = wallet.get_tx(txid).expect("tx exists").tx_node.tx;
1007+
/// let (sent, received) = wallet.sent_and_received(&tx);
10081008
/// ```
10091009
///
10101010
/// ```rust, no_run
@@ -1065,7 +1065,7 @@ impl<D> Wallet<D> {
10651065
pub fn get_tx(
10661066
&self,
10671067
txid: Txid,
1068-
) -> Option<CanonicalTx<'_, Transaction, ConfirmationTimeHeightAnchor>> {
1068+
) -> Option<CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> {
10691069
let graph = self.indexed_graph.graph();
10701070

10711071
Some(CanonicalTx {
@@ -1167,7 +1167,8 @@ impl<D> Wallet<D> {
11671167
/// Iterate over the transactions in the wallet.
11681168
pub fn transactions(
11691169
&self,
1170-
) -> impl Iterator<Item = CanonicalTx<'_, Transaction, ConfirmationTimeHeightAnchor>> + '_ {
1170+
) -> impl Iterator<Item = CanonicalTx<'_, Arc<Transaction>, ConfirmationTimeHeightAnchor>> + '_
1171+
{
11711172
self.indexed_graph
11721173
.graph()
11731174
.list_chain_txs(&self.chain, self.chain.tip().block_id())
@@ -1670,6 +1671,7 @@ impl<D> Wallet<D> {
16701671
let mut tx = graph
16711672
.get_tx(txid)
16721673
.ok_or(BuildFeeBumpError::TransactionNotFound(txid))?
1674+
.as_ref()
16731675
.clone();
16741676

16751677
let pos = graph
@@ -1739,7 +1741,7 @@ impl<D> Wallet<D> {
17391741
sequence: Some(txin.sequence),
17401742
psbt_input: Box::new(psbt::Input {
17411743
witness_utxo: Some(txout.clone()),
1742-
non_witness_utxo: Some(prev_tx.clone()),
1744+
non_witness_utxo: Some(prev_tx.as_ref().clone()),
17431745
..Default::default()
17441746
}),
17451747
},
@@ -2295,7 +2297,7 @@ impl<D> Wallet<D> {
22952297
psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
22962298
}
22972299
if !desc.is_taproot() && (!desc.is_witness() || !only_witness_utxo) {
2298-
psbt_input.non_witness_utxo = Some(prev_tx.clone());
2300+
psbt_input.non_witness_utxo = Some(prev_tx.as_ref().clone());
22992301
}
23002302
}
23012303
Ok(psbt_input)

crates/bdk/tests/wallet.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,12 @@ fn test_get_funded_wallet_sent_and_received() {
208208

209209
let mut tx_amounts: Vec<(Txid, (u64, u64))> = wallet
210210
.transactions()
211-
.map(|ct| (ct.tx_node.txid, wallet.sent_and_received(ct.tx_node.tx)))
211+
.map(|ct| (ct.tx_node.txid, wallet.sent_and_received(&ct.tx_node)))
212212
.collect();
213213
tx_amounts.sort_by(|a1, a2| a1.0.cmp(&a2.0));
214214

215215
let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
216-
let (sent, received) = wallet.sent_and_received(tx);
216+
let (sent, received) = wallet.sent_and_received(&tx);
217217

218218
// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
219219
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
@@ -227,7 +227,7 @@ fn test_get_funded_wallet_tx_fees() {
227227
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
228228

229229
let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
230-
let tx_fee = wallet.calculate_fee(tx).expect("transaction fee");
230+
let tx_fee = wallet.calculate_fee(&tx).expect("transaction fee");
231231

232232
// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
233233
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
@@ -240,7 +240,9 @@ fn test_get_funded_wallet_tx_fee_rate() {
240240
let (wallet, txid) = get_funded_wallet(get_test_wpkh());
241241

242242
let tx = wallet.get_tx(txid).expect("transaction").tx_node.tx;
243-
let tx_fee_rate = wallet.calculate_fee_rate(tx).expect("transaction fee rate");
243+
let tx_fee_rate = wallet
244+
.calculate_fee_rate(&tx)
245+
.expect("transaction fee rate");
244246

245247
// The funded wallet contains a tx with a 76_000 sats input and two outputs, one spending 25_000
246248
// to a foreign address and one returning 50_000 back to the wallet as change. The remaining 1000
@@ -1307,7 +1309,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
13071309
.add_foreign_utxo(
13081310
utxo2.outpoint,
13091311
psbt::Input {
1310-
non_witness_utxo: Some(tx1),
1312+
non_witness_utxo: Some(tx1.as_ref().clone()),
13111313
..Default::default()
13121314
},
13131315
satisfaction_weight
@@ -1320,7 +1322,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
13201322
.add_foreign_utxo(
13211323
utxo2.outpoint,
13221324
psbt::Input {
1323-
non_witness_utxo: Some(tx2),
1325+
non_witness_utxo: Some(tx2.as_ref().clone()),
13241326
..Default::default()
13251327
},
13261328
satisfaction_weight
@@ -1384,7 +1386,7 @@ fn test_add_foreign_utxo_only_witness_utxo() {
13841386
let mut builder = builder.clone();
13851387
let tx2 = wallet2.get_tx(txid2).unwrap().tx_node.tx;
13861388
let psbt_input = psbt::Input {
1387-
non_witness_utxo: Some(tx2.clone()),
1389+
non_witness_utxo: Some(tx2.as_ref().clone()),
13881390
..Default::default()
13891391
};
13901392
builder
@@ -3050,7 +3052,8 @@ fn test_taproot_sign_using_non_witness_utxo() {
30503052
let mut psbt = builder.finish().unwrap();
30513053

30523054
psbt.inputs[0].witness_utxo = None;
3053-
psbt.inputs[0].non_witness_utxo = Some(wallet.get_tx(prev_txid).unwrap().tx_node.tx.clone());
3055+
psbt.inputs[0].non_witness_utxo =
3056+
Some(wallet.get_tx(prev_txid).unwrap().tx_node.as_ref().clone());
30543057
assert!(
30553058
psbt.inputs[0].non_witness_utxo.is_some(),
30563059
"Previous tx should be present in the database"

crates/chain/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ readme = "README.md"
1515
[dependencies]
1616
# For no-std, remember to enable the bitcoin/no-std feature
1717
bitcoin = { version = "0.30.0", default-features = false }
18-
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive"] }
18+
serde_crate = { package = "serde", version = "1", optional = true, features = ["derive", "rc"] }
1919

2020
# Use hashbrown as a feature flag to have HashSet and HashMap from it.
2121
hashbrown = { version = "0.9.1", optional = true, features = ["serde"] }

0 commit comments

Comments
 (0)