Skip to content

Commit e677824

Browse files
committed
fix: Simplify esplora cli example
LLFourn addressing his comments on bitcoindevkit#1040
1 parent edd3744 commit e677824

File tree

2 files changed

+60
-53
lines changed

2 files changed

+60
-53
lines changed

crates/chain/src/tx_graph.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,33 @@ impl<A> ChangeSet<A> {
10481048
})
10491049
.chain(self.txouts.iter().map(|(op, txout)| (*op, txout)))
10501050
}
1051+
1052+
/// Iterates over the heights of that the new transaction anchors in this changeset.
1053+
///
1054+
/// This is useful if you want to find which heights you need to fetch data about in order to
1055+
/// confirm or exclude these anchors.
1056+
///
1057+
/// See also: [`Self::missing_heights`]
1058+
pub fn relevant_heights(&self) -> impl Iterator<Item=u32> + '_ where A: Anchor {
1059+
let mut dedup = None;
1060+
self.anchors.iter().map(|(a, _)| a.anchor_block().height).filter(move |height| {
1061+
let duplicate = dedup == Some(*height);
1062+
dedup = Some(*height);
1063+
!duplicate
1064+
})
1065+
}
1066+
1067+
1068+
/// Returns an iterator for the [`relevant_heights`] in this changeset that are not included in
1069+
/// `local_chain`. This tells you which heights you need to include in `local_chain` in order
1070+
/// for it to conclusively act as a [`ChainOracle`] for the transaction anchors this changeset
1071+
/// will add.
1072+
///
1073+
/// [`ChainOracle`]: crate::ChainOracle
1074+
/// [`relevant_heights`]: Self::relevant_heights
1075+
pub fn missing_heights_from<'a>(&'a self, local_chain: &'a LocalChain) -> impl Iterator<Item=u32> + 'a where A: Anchor {
1076+
self.relevant_heights().filter(move |height| !local_chain.blocks().contains_key(height))
1077+
}
10511078
}
10521079

10531080
impl<A: Ord> Append for ChangeSet<A> {

example-crates/example_esplora/src/main.rs

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::{
2-
collections::BTreeMap,
2+
collections::{BTreeMap, BTreeSet},
33
io::{self, Write},
44
sync::Mutex,
55
};
66

77
use bdk_chain::{
88
bitcoin::{Address, Network, OutPoint, ScriptBuf, Txid},
9-
indexed_tx_graph::{self, IndexedTxGraph},
9+
indexed_tx_graph::IndexedTxGraph,
1010
keychain::WalletChangeSet,
1111
local_chain::{CheckPoint, LocalChain},
1212
Append, ConfirmationTimeAnchor,
@@ -114,17 +114,15 @@ fn main() -> anyhow::Result<()> {
114114
}
115115
};
116116

117-
// This is where we will accumulate changes of `IndexedTxGraph` and `LocalChain` and persist
118-
// these changes as a batch at the end.
119-
let mut changeset = WalletChangeSet::<Keychain, ConfirmationTimeAnchor>::default();
120-
121117
// Prepare the `IndexedTxGraph` update based on whether we are scanning or syncing.
122118
// Scanning: We are iterating through spks of all keychains and scanning for transactions for
123119
// each spk. We start with the lowest derivation index spk and stop scanning after `stop_gap`
124-
// number of consecutive spks have no transaction history.
120+
// number of consecutive spks have no transaction history. A Scan is done in situations of
121+
// wallet restoration. It is a special case. Applications should use "sync" style updates
122+
// after an initial scan.
125123
// Syncing: We only check for specified spks, utxos and txids to update their confirmation
126124
// status or fetch missing transactions.
127-
let graph_update = match &esplora_cmd {
125+
let indexed_tx_graph_changeset = match &esplora_cmd {
128126
EsploraCommands::Scan {
129127
stop_gap,
130128
scan_options,
@@ -155,7 +153,7 @@ fn main() -> anyhow::Result<()> {
155153
// is reached. It returns a `TxGraph` update (`graph_update`) and a structure that
156154
// represents the last active spk derivation indices of keychains
157155
// (`keychain_indices_update`).
158-
let (graph_update, keychain_indices_update) = client
156+
let (graph_update, last_active_indices) = client
159157
.update_tx_graph(
160158
keychain_spks,
161159
core::iter::empty(),
@@ -165,18 +163,15 @@ fn main() -> anyhow::Result<()> {
165163
)
166164
.context("scanning for transactions")?;
167165

168-
// Update the index in `IndexedTxGraph` with `keychain_indices_update`. The resultant
169-
// changes are appended to `changeset`.
170-
changeset.append({
171-
let (_, index_additions) = graph
172-
.lock()
173-
.expect("mutex must not be poisoned")
174-
.index
175-
.reveal_to_target_multi(&keychain_indices_update);
176-
WalletChangeSet::from(indexed_tx_graph::ChangeSet::from(index_additions))
177-
});
178-
179-
graph_update
166+
let mut graph = graph.lock().expect("mutex must not be poisoned");
167+
// Because we did a stop gap based scan we are likely to have some updates to our
168+
// deriviation indices. Usually before a scan you are on a fresh wallet with no
169+
// addresses derived so we need to derive up to last active addresses the scan found
170+
// before adding the transactions.
171+
let (_, index_changeset) = graph.index.reveal_to_target_multi(&last_active_indices);
172+
let mut indexed_tx_graph_changeset = graph.apply_update(graph_update);
173+
indexed_tx_graph_changeset.append(index_changeset.into());
174+
indexed_tx_graph_changeset
180175
}
181176
EsploraCommands::Sync {
182177
mut unused_spks,
@@ -281,42 +276,31 @@ fn main() -> anyhow::Result<()> {
281276
}
282277
}
283278

284-
client.update_tx_graph_without_keychain(
279+
let graph_update = client.update_tx_graph_without_keychain(
285280
spks,
286281
txids,
287282
outpoints,
288283
scan_options.parallel_requests,
289-
)?
284+
)?;
285+
286+
graph.lock().unwrap().apply_update(graph_update)
290287
}
291288
};
292289

293290
println!();
294291

295-
// We apply the `TxGraph` update, and append the resultant changes to `changeset`
296-
// (for persistance)
297-
changeset.append({
298-
let indexed_graph_additions = graph.lock().unwrap().apply_update(graph_update);
299-
WalletChangeSet::from(indexed_graph_additions)
300-
});
301-
302-
// Now that we're done updating the `TxGraph`, it's time to update the `LocalChain`!
303-
// We want the `LocalChain` to have data about all the anchors in the `TxGraph` - for this
304-
// reason, we want retrieve the heights of the newly added anchors, and fetch the corresponding
305-
// blocks.
306-
307-
// We get the heights of all the anchors introduced by the changeset, and the local chain tip.
308-
// Note that the latter is only used for logging.
292+
// Now that we're done updating the `IndexedTxGraph`, it's time to update the `LocalChain`! We
293+
// want the `LocalChain` to have data about all the anchors in the `TxGraph` - for this reason,
294+
// we want retrieve the blocks at the heights of the newly added anchors that are missing from
295+
// our view of the chain.
309296
let (missing_block_heights, tip) = {
310297
let chain = &*chain.lock().unwrap();
311-
let heights_to_fetch = changeset
312-
.indexed_tx_graph
298+
let missing_block_heights = indexed_tx_graph_changeset
313299
.graph
314-
.anchors
315-
.iter()
316-
.map(|(a, _)| a.confirmation_height)
317-
.collect::<Vec<_>>();
300+
.missing_heights_from(chain)
301+
.collect::<BTreeSet<_>>();
318302
let tip = chain.tip();
319-
(heights_to_fetch, tip)
303+
(missing_block_heights, tip)
320304
};
321305

322306
println!("prev tip: {}", tip.as_ref().map_or(0, CheckPoint::height));
@@ -329,16 +313,12 @@ fn main() -> anyhow::Result<()> {
329313

330314
println!("new tip: {}", chain_update.tip.height());
331315

332-
// We apply the `LocalChain` update, and append the resultant changes to `changeset`
333-
// (for persistance).
334-
changeset.append({
335-
let chain_additions = chain.lock().unwrap().apply_update(chain_update)?;
336-
WalletChangeSet::from(chain_additions)
337-
});
338-
339-
// We persist `changeset`.
316+
// We persist the changes
340317
let mut db = db.lock().unwrap();
341-
db.stage(changeset);
318+
db.stage(WalletChangeSet {
319+
chain: chain.lock().unwrap().apply_update(chain_update)?,
320+
indexed_tx_graph: indexed_tx_graph_changeset,
321+
});
342322
db.commit()?;
343323
Ok(())
344324
}

0 commit comments

Comments
 (0)