From: 志宇 Date: Fri, 24 Jan 2025 04:31:41 +0000 (+1100) Subject: feat(core)!: Make `TxUpdate` non-exhaustive X-Git-Tag: wallet-1.2.0~21^2~1 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/struct.EncoderStringWriter.html?a=commitdiff_plain;h=ee527454b0dc451ea44fd9410db3e396cb48817f;p=bdk feat(core)!: Make `TxUpdate` non-exhaustive If we introduce new fields to `TxUpdate`, they can be minor non-breaking updates. --- diff --git a/crates/chain/benches/canonicalization.rs b/crates/chain/benches/canonicalization.rs index 3002a7ca..52cbf51d 100644 --- a/crates/chain/benches/canonicalization.rs +++ b/crates/chain/benches/canonicalization.rs @@ -132,10 +132,8 @@ pub fn many_conflicting_unconfirmed(c: &mut Criterion) { }], ..new_tx(i) }; - let update = TxUpdate { - txs: vec![Arc::new(tx)], - ..Default::default() - }; + let mut update = TxUpdate::default(); + update.txs = vec![Arc::new(tx)]; let _ = tx_graph.apply_update_at(update, Some(i as u64)); } })); @@ -169,10 +167,8 @@ pub fn many_chained_unconfirmed(c: &mut Criterion) { ..new_tx(i) }; let txid = tx.compute_txid(); - let update = TxUpdate { - txs: vec![Arc::new(tx)], - ..Default::default() - }; + let mut update = TxUpdate::default(); + update.txs = vec![Arc::new(tx)]; let _ = tx_graph.apply_update_at(update, Some(i as u64)); // Store the next prevout. previous_output = OutPoint::new(txid, 0); diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index 2d512cfe..d0f7380a 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -110,19 +110,19 @@ use core::{ impl From> for TxUpdate { fn from(graph: TxGraph) -> Self { - Self { - txs: graph.full_txs().map(|tx_node| tx_node.tx).collect(), - txouts: graph - .floating_txouts() - .map(|(op, txo)| (op, txo.clone())) - .collect(), - anchors: graph - .anchors - .into_iter() - .flat_map(|(txid, anchors)| anchors.into_iter().map(move |a| (a, txid))) - .collect(), - seen_ats: graph.last_seen.into_iter().collect(), - } + let mut tx_update = TxUpdate::default(); + tx_update.txs = graph.full_txs().map(|tx_node| tx_node.tx).collect(); + tx_update.txouts = graph + .floating_txouts() + .map(|(op, txo)| (op, txo.clone())) + .collect(); + tx_update.anchors = graph + .anchors + .into_iter() + .flat_map(|(txid, anchors)| anchors.into_iter().map(move |a| (a, txid))) + .collect(); + tx_update.seen_ats = graph.last_seen.into_iter().collect(); + tx_update } } diff --git a/crates/chain/tests/test_tx_graph.rs b/crates/chain/tests/test_tx_graph.rs index ef57ac15..16d2e6c6 100644 --- a/crates/chain/tests/test_tx_graph.rs +++ b/crates/chain/tests/test_tx_graph.rs @@ -1231,69 +1231,60 @@ fn tx_graph_update_conversion() { let test_cases: &[TestCase] = &[ ("empty_update", TxUpdate::default()), - ( - "single_tx", - TxUpdate { - txs: vec![make_tx(0).into()], - ..Default::default() - }, - ), - ( - "two_txs", - TxUpdate { - txs: vec![make_tx(0).into(), make_tx(1).into()], - ..Default::default() - }, - ), - ( - "with_floating_txouts", - TxUpdate { - txs: vec![make_tx(0).into(), make_tx(1).into()], - txouts: [ - (OutPoint::new(hash!("a"), 0), make_txout(0)), - (OutPoint::new(hash!("a"), 1), make_txout(1)), - (OutPoint::new(hash!("b"), 0), make_txout(2)), - ] - .into(), - ..Default::default() - }, - ), - ( - "with_anchors", - TxUpdate { - txs: vec![make_tx(0).into(), make_tx(1).into()], - txouts: [ - (OutPoint::new(hash!("a"), 0), make_txout(0)), - (OutPoint::new(hash!("a"), 1), make_txout(1)), - (OutPoint::new(hash!("b"), 0), make_txout(2)), - ] - .into(), - anchors: [ - (ConfirmationBlockTime::default(), hash!("a")), - (ConfirmationBlockTime::default(), hash!("b")), - ] - .into(), - ..Default::default() - }, - ), - ( - "with_seen_ats", - TxUpdate { - txs: vec![make_tx(0).into(), make_tx(1).into()], - txouts: [ - (OutPoint::new(hash!("a"), 0), make_txout(0)), - (OutPoint::new(hash!("a"), 1), make_txout(1)), - (OutPoint::new(hash!("d"), 0), make_txout(2)), - ] - .into(), - anchors: [ - (ConfirmationBlockTime::default(), hash!("a")), - (ConfirmationBlockTime::default(), hash!("b")), - ] - .into(), - seen_ats: [(hash!("c"), 12346)].into_iter().collect(), - }, - ), + ("single_tx", { + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![make_tx(0).into()]; + tx_update + }), + ("two_txs", { + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![make_tx(0).into(), make_tx(1).into()]; + tx_update + }), + ("with_floating_txouts", { + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![make_tx(0).into(), make_tx(1).into()]; + tx_update.txouts = [ + (OutPoint::new(hash!("a"), 0), make_txout(0)), + (OutPoint::new(hash!("a"), 1), make_txout(1)), + (OutPoint::new(hash!("b"), 0), make_txout(2)), + ] + .into(); + tx_update + }), + ("with_anchors", { + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![make_tx(0).into(), make_tx(1).into()]; + tx_update.txouts = [ + (OutPoint::new(hash!("a"), 0), make_txout(0)), + (OutPoint::new(hash!("a"), 1), make_txout(1)), + (OutPoint::new(hash!("b"), 0), make_txout(2)), + ] + .into(); + tx_update.anchors = [ + (ConfirmationBlockTime::default(), hash!("a")), + (ConfirmationBlockTime::default(), hash!("b")), + ] + .into(); + tx_update + }), + ("with_seen_ats", { + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![make_tx(0).into(), make_tx(1).into()]; + tx_update.txouts = [ + (OutPoint::new(hash!("a"), 0), make_txout(0)), + (OutPoint::new(hash!("a"), 1), make_txout(1)), + (OutPoint::new(hash!("d"), 0), make_txout(2)), + ] + .into(); + tx_update.anchors = [ + (ConfirmationBlockTime::default(), hash!("a")), + (ConfirmationBlockTime::default(), hash!("b")), + ] + .into(); + tx_update.seen_ats = [(hash!("c"), 12346)].into_iter().collect(); + tx_update + }), ]; for (test_name, update) in test_cases { diff --git a/crates/core/src/tx_update.rs b/crates/core/src/tx_update.rs index 7707578e..5da2bff8 100644 --- a/crates/core/src/tx_update.rs +++ b/crates/core/src/tx_update.rs @@ -4,7 +4,22 @@ use bitcoin::{OutPoint, Transaction, TxOut, Txid}; /// Data object used to communicate updates about relevant transactions from some chain data source /// to the core model (usually a `bdk_chain::TxGraph`). +/// +/// ```rust +/// use bdk_core::TxUpdate; +/// # use std::sync::Arc; +/// # use bitcoin::{Transaction, transaction::Version, absolute::LockTime}; +/// # let version = Version::ONE; +/// # let lock_time = LockTime::ZERO; +/// # let tx = Arc::new(Transaction { input: vec![], output: vec![], version, lock_time }); +/// # let txid = tx.compute_txid(); +/// # let anchor = (); +/// let mut tx_update = TxUpdate::default(); +/// tx_update.txs.push(tx); +/// tx_update.anchors.insert((anchor, txid)); +/// ``` #[derive(Debug, Clone)] +#[non_exhaustive] pub struct TxUpdate { /// Full transactions. These are transactions that were determined to be relevant to the wallet /// given the request. diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index 621a69e1..e187bf36 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -571,10 +571,8 @@ mod test { // `fetch_prev_txout` on a coinbase transaction will trigger a `fetch_tx` on a transaction // with a txid of all zeros. If `fetch_prev_txout` attempts to fetch this transaction, this // assertion will fail. - let mut tx_update = TxUpdate { - txs: vec![Arc::new(coinbase_tx)], - ..Default::default() - }; + let mut tx_update = TxUpdate::default(); + tx_update.txs = vec![Arc::new(coinbase_tx)]; assert!(client.fetch_prev_txout(&mut tx_update).is_ok()); // Ensure that the txouts are empty.