Skip to content

Commit c1a34a8

Browse files
committed
feat!: Add #[non_exhaustive] to ChangeSet structs in tx_graph, keychain_txout, local_chain
Add #[non_exhaustive] attribute to the ChangeSet structs to prevent downstream crates from exhaustively matching or constructing instances using struct literal syntax, allowing future additions without breaking compatibility. The change improves forward compatibility by allowing new fields to be added without breaking downstream code in future releases.
1 parent 63923c6 commit c1a34a8

File tree

8 files changed

+123
-146
lines changed

8 files changed

+123
-146
lines changed

crates/bitcoind_rpc/tests/test_emitter.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,10 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> {
102102
assert_eq!(
103103
local_chain.apply_update(emission.checkpoint,)?,
104104
if exp_height == exp_hashes.len() - reorged_blocks.len() {
105-
bdk_chain::local_chain::ChangeSet {
106-
blocks: core::iter::once((height, Some(hash)))
107-
.chain((height + 1..exp_hashes.len() as u32).map(|h| (h, None)))
108-
.collect(),
109-
}
105+
bdk_chain::local_chain::ChangeSet::from(
106+
core::iter::once((height, Some(hash)))
107+
.chain((height + 1..exp_hashes.len() as u32).map(|h| (h, None))),
108+
)
110109
} else {
111110
[(height, Some(hash))].into()
112111
},

crates/chain/src/indexed_tx_graph.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ impl<A, I> AsRef<TxGraph<A>> for IndexedTxGraph<A, I> {
482482
))
483483
)]
484484
#[must_use]
485+
#[non_exhaustive]
485486
pub struct ChangeSet<A, IA> {
486487
/// [`TxGraph`] changeset.
487488
pub tx_graph: tx_graph::ChangeSet<A>,

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,7 @@ impl<K: core::fmt::Debug> std::error::Error for InsertDescriptorError<K> {}
10211021
#[derive(Clone, Debug, Default, PartialEq)]
10221022
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10231023
#[must_use]
1024+
#[non_exhaustive]
10241025
pub struct ChangeSet {
10251026
/// Maps each `DescriptorId` to its last revealed derivation index.
10261027
pub last_revealed: BTreeMap<DescriptorId, u32>,
@@ -1031,6 +1032,19 @@ pub struct ChangeSet {
10311032
pub spk_cache: BTreeMap<DescriptorId, BTreeMap<u32, ScriptBuf>>,
10321033
}
10331034

1035+
impl ChangeSet {
1036+
/// Create a new empty `ChangeSet`.
1037+
pub fn new(
1038+
last_revealed: BTreeMap<DescriptorId, u32>,
1039+
spk_cache: BTreeMap<DescriptorId, BTreeMap<u32, ScriptBuf>>,
1040+
) -> Self {
1041+
Self {
1042+
last_revealed,
1043+
spk_cache,
1044+
}
1045+
}
1046+
}
1047+
10341048
impl Merge for ChangeSet {
10351049
/// Merge another [`ChangeSet`] into self.
10361050
fn merge(&mut self, other: Self) {

crates/chain/src/local_chain.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ impl LocalChain {
408408
/// The [`ChangeSet`] represents changes to [`LocalChain`].
409409
#[derive(Debug, Default, Clone, PartialEq)]
410410
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
411+
#[non_exhaustive]
411412
pub struct ChangeSet {
412413
/// Changes to the [`LocalChain`] blocks.
413414
///

crates/chain/src/tx_graph.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ impl<A: Anchor> From<TxUpdate<A>> for TxGraph<A> {
173173
///
174174
/// [module-level documentation]: crate::tx_graph
175175
#[derive(Clone, Debug, PartialEq)]
176+
#[non_exhaustive]
176177
pub struct TxGraph<A = ConfirmationBlockTime> {
177178
txs: HashMap<Txid, TxNodeInternal>,
178179
spends: BTreeMap<OutPoint, HashSet<Txid>>,
@@ -1415,6 +1416,7 @@ impl<A: Anchor> TxGraph<A> {
14151416
))
14161417
)]
14171418
#[must_use]
1419+
#[non_exhaustive]
14181420
pub struct ChangeSet<A = ()> {
14191421
/// Added transactions.
14201422
pub txs: BTreeSet<Arc<Transaction>>,

crates/chain/tests/test_indexed_tx_graph.rs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ mod common;
55

66
use std::{collections::BTreeSet, sync::Arc};
77

8+
use bdk_chain::indexed_tx_graph::ChangeSet;
89
use bdk_chain::{
910
indexed_tx_graph::{self, IndexedTxGraph},
1011
indexer::keychain_txout::KeychainTxOutIndex,
1112
local_chain::LocalChain,
12-
tx_graph, Balance, CanonicalizationParams, ChainPosition, ConfirmationBlockTime, DescriptorExt,
13+
Balance, CanonicalizationParams, ChainPosition, ConfirmationBlockTime, DescriptorExt,
1314
SpkIterator,
1415
};
1516
use bdk_testenv::{
@@ -74,45 +75,41 @@ fn insert_relevant_txs() {
7475

7576
let txs = [tx_c, tx_b, tx_a];
7677

77-
let changeset = indexed_tx_graph::ChangeSet {
78-
tx_graph: tx_graph::ChangeSet {
79-
txs: txs.iter().cloned().map(Arc::new).collect(),
80-
..Default::default()
81-
},
82-
indexer: keychain_txout::ChangeSet {
83-
last_revealed: [(descriptor.descriptor_id(), 9_u32)].into(),
84-
spk_cache: [(descriptor.descriptor_id(), {
85-
let index_after_spk_1 = 9 /* index of spk_1 */ + 1;
86-
SpkIterator::new_with_range(
87-
&descriptor,
88-
// This will also persist the staged spk cache inclusions from prev call to
89-
// `.insert_descriptor`.
90-
0..index_after_spk_1 + lookahead,
91-
)
92-
.collect()
93-
})]
94-
.into(),
95-
},
96-
};
78+
let mut changeset = ChangeSet::default();
79+
changeset
80+
.tx_graph
81+
.txs
82+
.extend(txs.iter().cloned().map(Arc::new));
83+
let mut indexer_changeset = keychain_txout::ChangeSet::default();
84+
indexer_changeset.last_revealed = [(descriptor.descriptor_id(), 9_u32)].into();
85+
indexer_changeset.spk_cache = [(descriptor.descriptor_id(), {
86+
let index_after_spk_1 = 9 /* index of spk_1 */ + 1;
87+
SpkIterator::new_with_range(
88+
&descriptor,
89+
// This will also persist the staged spk cache inclusions from prev call to
90+
// `.insert_descriptor`.
91+
0..index_after_spk_1 + lookahead,
92+
)
93+
.collect()
94+
})]
95+
.into();
96+
changeset.indexer = indexer_changeset.into();
9797

9898
assert_eq!(
9999
graph.batch_insert_relevant(txs.iter().cloned().map(|tx| (tx, None))),
100100
changeset,
101101
);
102102

103103
// The initial changeset will also contain info about the keychain we added
104-
let initial_changeset = indexed_tx_graph::ChangeSet {
105-
tx_graph: changeset.tx_graph,
106-
indexer: keychain_txout::ChangeSet {
107-
last_revealed: changeset.indexer.last_revealed,
108-
spk_cache: [(
109-
descriptor.descriptor_id(),
110-
SpkIterator::new_with_range(&descriptor, 0..=9 /* index of spk_1*/ + lookahead)
111-
.collect(),
112-
)]
113-
.into(),
114-
},
115-
};
104+
let mut initial_changeset = indexed_tx_graph::ChangeSet::default();
105+
initial_changeset.tx_graph = changeset.tx_graph;
106+
initial_changeset.indexer = keychain_txout::ChangeSet::default();
107+
initial_changeset.indexer.last_revealed = changeset.indexer.last_revealed;
108+
initial_changeset.indexer.spk_cache = [(
109+
descriptor.descriptor_id(),
110+
SpkIterator::new_with_range(&descriptor, 0..=9 /*index of spk_1 */ + lookahead).collect(),
111+
)]
112+
.into();
116113

117114
assert_eq!(graph.initial_changeset(), initial_changeset);
118115
}

crates/chain/tests/test_keychain_txout_index.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,12 @@ fn merge_changesets_check_last_revealed() {
8080
rhs_di.insert(descriptor_ids[1], 5); // value more than lhs desc 1
8181
lhs_di.insert(descriptor_ids[3], 4); // key doesn't exist in lhs
8282

83-
let mut lhs = ChangeSet {
84-
last_revealed: lhs_di,
85-
..Default::default()
86-
};
87-
let rhs = ChangeSet {
88-
last_revealed: rhs_di,
89-
..Default::default()
90-
};
83+
let mut lhs = ChangeSet::default();
84+
lhs.last_revealed = lhs_di;
85+
86+
let mut rhs = ChangeSet::default();
87+
rhs.last_revealed = rhs_di;
88+
9189
lhs.merge(rhs);
9290

9391
// Existing index doesn't update if the new index in `other` is lower than `self`.
@@ -129,12 +127,13 @@ fn test_set_all_derivation_indices() {
129127
),
130128
]
131129
.into();
130+
131+
let mut expected_changeset = ChangeSet::default();
132+
expected_changeset.last_revealed = last_revealed.clone();
133+
expected_changeset.spk_cache = spk_cache.clone();
132134
assert_eq!(
133135
txout_index.reveal_to_target_multi(&derive_to),
134-
ChangeSet {
135-
last_revealed: last_revealed.clone(),
136-
spk_cache: spk_cache.clone(),
137-
}
136+
expected_changeset
138137
);
139138
assert_eq!(txout_index.last_revealed_indices(), derive_to);
140139
assert_eq!(
@@ -629,16 +628,11 @@ fn lookahead_to_target() {
629628
#[test]
630629
fn applying_changesets_one_by_one_vs_aggregate_must_have_same_result() {
631630
let desc = parse_descriptor(DESCRIPTORS[0]);
632-
let changesets: &[ChangeSet] = &[
633-
ChangeSet {
634-
last_revealed: [(desc.descriptor_id(), 10)].into(),
635-
..Default::default()
636-
},
637-
ChangeSet {
638-
last_revealed: [(desc.descriptor_id(), 12)].into(),
639-
..Default::default()
640-
},
641-
];
631+
let mut changeset_1 = ChangeSet::default();
632+
changeset_1.last_revealed = [(desc.descriptor_id(), 10)].into();
633+
let mut changeset_2 = ChangeSet::default();
634+
changeset_2.last_revealed = [(desc.descriptor_id(), 12)].into();
635+
let changesets: &[ChangeSet] = &[changeset_1, changeset_2];
642636

643637
let mut indexer_a = KeychainTxOutIndex::<TestKeychain>::new(0, true);
644638
let _ = indexer_a

0 commit comments

Comments
 (0)