feat(esplora)!: remove EsploraExt::update_local_chain
Previously, we would update the `TxGraph` and `KeychainTxOutIndex` first, then create a second update for `LocalChain`. This required locking the receiving structures 3 times (instead of twice, which is optimal). This PR eliminates this requirement by making use of the new `query` method of `CheckPoint`. Examples are also updated to use the new API.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
collections::BTreeMap,
|
||||
io::{self, Write},
|
||||
sync::Mutex,
|
||||
};
|
||||
@@ -60,6 +60,7 @@ enum EsploraCommands {
|
||||
esplora_args: EsploraArgs,
|
||||
},
|
||||
}
|
||||
|
||||
impl EsploraCommands {
|
||||
fn esplora_args(&self) -> EsploraArgs {
|
||||
match self {
|
||||
@@ -149,20 +150,24 @@ fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
let client = esplora_cmd.esplora_args().client(args.network)?;
|
||||
// Prepare the `IndexedTxGraph` update based on whether we are scanning or syncing.
|
||||
// Prepare the `IndexedTxGraph` and `LocalChain` updates based on whether we are scanning or
|
||||
// syncing.
|
||||
//
|
||||
// Scanning: We are iterating through spks of all keychains and scanning for transactions for
|
||||
// each spk. We start with the lowest derivation index spk and stop scanning after `stop_gap`
|
||||
// number of consecutive spks have no transaction history. A Scan is done in situations of
|
||||
// wallet restoration. It is a special case. Applications should use "sync" style updates
|
||||
// after an initial scan.
|
||||
//
|
||||
// Syncing: We only check for specified spks, utxos and txids to update their confirmation
|
||||
// status or fetch missing transactions.
|
||||
let indexed_tx_graph_changeset = match &esplora_cmd {
|
||||
let (local_chain_changeset, indexed_tx_graph_changeset) = match &esplora_cmd {
|
||||
EsploraCommands::Scan {
|
||||
stop_gap,
|
||||
scan_options,
|
||||
..
|
||||
} => {
|
||||
let local_tip = chain.lock().expect("mutex must not be poisoned").tip();
|
||||
let keychain_spks = graph
|
||||
.lock()
|
||||
.expect("mutex must not be poisoned")
|
||||
@@ -189,23 +194,33 @@ fn main() -> anyhow::Result<()> {
|
||||
// is reached. It returns a `TxGraph` update (`graph_update`) and a structure that
|
||||
// represents the last active spk derivation indices of keychains
|
||||
// (`keychain_indices_update`).
|
||||
let (mut graph_update, last_active_indices) = client
|
||||
.full_scan(keychain_spks, *stop_gap, scan_options.parallel_requests)
|
||||
let mut update = client
|
||||
.full_scan(
|
||||
local_tip,
|
||||
keychain_spks,
|
||||
*stop_gap,
|
||||
scan_options.parallel_requests,
|
||||
)
|
||||
.context("scanning for transactions")?;
|
||||
|
||||
// We want to keep track of the latest time a transaction was seen unconfirmed.
|
||||
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||
let _ = graph_update.update_last_seen_unconfirmed(now);
|
||||
let _ = update.tx_graph.update_last_seen_unconfirmed(now);
|
||||
|
||||
let mut graph = graph.lock().expect("mutex must not be poisoned");
|
||||
let mut chain = chain.lock().expect("mutex must not be poisoned");
|
||||
// Because we did a stop gap based scan we are likely to have some updates to our
|
||||
// deriviation indices. Usually before a scan you are on a fresh wallet with no
|
||||
// addresses derived so we need to derive up to last active addresses the scan found
|
||||
// before adding the transactions.
|
||||
let (_, index_changeset) = graph.index.reveal_to_target_multi(&last_active_indices);
|
||||
let mut indexed_tx_graph_changeset = graph.apply_update(graph_update);
|
||||
indexed_tx_graph_changeset.append(index_changeset.into());
|
||||
indexed_tx_graph_changeset
|
||||
(chain.apply_update(update.local_chain)?, {
|
||||
let (_, index_changeset) = graph
|
||||
.index
|
||||
.reveal_to_target_multi(&update.last_active_indices);
|
||||
let mut indexed_tx_graph_changeset = graph.apply_update(update.tx_graph);
|
||||
indexed_tx_graph_changeset.append(index_changeset.into());
|
||||
indexed_tx_graph_changeset
|
||||
})
|
||||
}
|
||||
EsploraCommands::Sync {
|
||||
mut unused_spks,
|
||||
@@ -231,12 +246,13 @@ fn main() -> anyhow::Result<()> {
|
||||
let mut outpoints: Box<dyn Iterator<Item = OutPoint>> = Box::new(core::iter::empty());
|
||||
let mut txids: Box<dyn Iterator<Item = Txid>> = Box::new(core::iter::empty());
|
||||
|
||||
let local_tip = chain.lock().expect("mutex must not be poisoned").tip();
|
||||
|
||||
// Get a short lock on the structures to get spks, utxos, and txs that we are interested
|
||||
// in.
|
||||
{
|
||||
let graph = graph.lock().unwrap();
|
||||
let chain = chain.lock().unwrap();
|
||||
let chain_tip = chain.tip().block_id();
|
||||
|
||||
if *all_spks {
|
||||
let all_spks = graph
|
||||
@@ -276,7 +292,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let init_outpoints = graph.index.outpoints().iter().cloned();
|
||||
let utxos = graph
|
||||
.graph()
|
||||
.filter_chain_unspents(&*chain, chain_tip, init_outpoints)
|
||||
.filter_chain_unspents(&*chain, local_tip.block_id(), init_outpoints)
|
||||
.map(|(_, utxo)| utxo)
|
||||
.collect::<Vec<_>>();
|
||||
outpoints = Box::new(
|
||||
@@ -299,7 +315,7 @@ fn main() -> anyhow::Result<()> {
|
||||
// `EsploraExt::update_tx_graph_without_keychain`.
|
||||
let unconfirmed_txids = graph
|
||||
.graph()
|
||||
.list_chain_txs(&*chain, chain_tip)
|
||||
.list_chain_txs(&*chain, local_tip.block_id())
|
||||
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
|
||||
.map(|canonical_tx| canonical_tx.tx_node.txid)
|
||||
.collect::<Vec<Txid>>();
|
||||
@@ -311,48 +327,30 @@ fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut graph_update =
|
||||
client.sync(spks, txids, outpoints, scan_options.parallel_requests)?;
|
||||
let mut update = client.sync(
|
||||
local_tip,
|
||||
spks,
|
||||
txids,
|
||||
outpoints,
|
||||
scan_options.parallel_requests,
|
||||
)?;
|
||||
|
||||
// Update last seen unconfirmed
|
||||
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||
let _ = graph_update.update_last_seen_unconfirmed(now);
|
||||
let _ = update.tx_graph.update_last_seen_unconfirmed(now);
|
||||
|
||||
graph.lock().unwrap().apply_update(graph_update)
|
||||
(
|
||||
chain.lock().unwrap().apply_update(update.local_chain)?,
|
||||
graph.lock().unwrap().apply_update(update.tx_graph),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
println!();
|
||||
|
||||
// Now that we're done updating the `IndexedTxGraph`, it's time to update the `LocalChain`! We
|
||||
// want the `LocalChain` to have data about all the anchors in the `TxGraph` - for this reason,
|
||||
// we want retrieve the blocks at the heights of the newly added anchors that are missing from
|
||||
// our view of the chain.
|
||||
let (missing_block_heights, tip) = {
|
||||
let chain = &*chain.lock().unwrap();
|
||||
let missing_block_heights = indexed_tx_graph_changeset
|
||||
.graph
|
||||
.missing_heights_from(chain)
|
||||
.collect::<BTreeSet<_>>();
|
||||
let tip = chain.tip();
|
||||
(missing_block_heights, tip)
|
||||
};
|
||||
|
||||
println!("prev tip: {}", tip.height());
|
||||
println!("missing block heights: {:?}", missing_block_heights);
|
||||
|
||||
// Here, we actually fetch the missing blocks and create a `local_chain::Update`.
|
||||
let chain_changeset = {
|
||||
let chain_update = client
|
||||
.update_local_chain(tip, missing_block_heights)
|
||||
.context("scanning for blocks")?;
|
||||
println!("new tip: {}", chain_update.tip.height());
|
||||
chain.lock().unwrap().apply_update(chain_update)?
|
||||
};
|
||||
|
||||
// We persist the changes
|
||||
let mut db = db.lock().unwrap();
|
||||
db.stage((chain_changeset, indexed_tx_graph_changeset));
|
||||
db.stage((local_chain_changeset, indexed_tx_graph_changeset));
|
||||
db.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -53,18 +53,17 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||
(k, k_spks)
|
||||
})
|
||||
.collect();
|
||||
let (mut update_graph, last_active_indices) = client
|
||||
.full_scan(keychain_spks, STOP_GAP, PARALLEL_REQUESTS)
|
||||
.await?;
|
||||
|
||||
let mut update = client
|
||||
.full_scan(prev_tip, keychain_spks, STOP_GAP, PARALLEL_REQUESTS)
|
||||
.await?;
|
||||
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||
let _ = update_graph.update_last_seen_unconfirmed(now);
|
||||
let missing_heights = update_graph.missing_heights(wallet.local_chain());
|
||||
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
|
||||
let _ = update.tx_graph.update_last_seen_unconfirmed(now);
|
||||
|
||||
let update = Update {
|
||||
last_active_indices,
|
||||
graph: update_graph,
|
||||
chain: Some(chain_update),
|
||||
last_active_indices: update.last_active_indices,
|
||||
graph: update.tx_graph,
|
||||
chain: Some(update.local_chain),
|
||||
};
|
||||
wallet.apply_update(update)?;
|
||||
wallet.commit()?;
|
||||
|
||||
@@ -36,7 +36,6 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
let client =
|
||||
esplora_client::Builder::new("https://blockstream.info/testnet/api").build_blocking();
|
||||
|
||||
let prev_tip = wallet.latest_checkpoint();
|
||||
let keychain_spks = wallet
|
||||
.all_unbounded_spk_iters()
|
||||
.into_iter()
|
||||
@@ -53,20 +52,20 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (mut update_graph, last_active_indices) =
|
||||
client.full_scan(keychain_spks, STOP_GAP, PARALLEL_REQUESTS)?;
|
||||
|
||||
let mut update = client.full_scan(
|
||||
wallet.latest_checkpoint(),
|
||||
keychain_spks,
|
||||
STOP_GAP,
|
||||
PARALLEL_REQUESTS,
|
||||
)?;
|
||||
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
|
||||
let _ = update_graph.update_last_seen_unconfirmed(now);
|
||||
let missing_heights = update_graph.missing_heights(wallet.local_chain());
|
||||
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
|
||||
let update = Update {
|
||||
last_active_indices,
|
||||
graph: update_graph,
|
||||
chain: Some(chain_update),
|
||||
};
|
||||
let _ = update.tx_graph.update_last_seen_unconfirmed(now);
|
||||
|
||||
wallet.apply_update(update)?;
|
||||
wallet.apply_update(Update {
|
||||
last_active_indices: update.last_active_indices,
|
||||
graph: update.tx_graph,
|
||||
chain: Some(update.local_chain),
|
||||
})?;
|
||||
wallet.commit()?;
|
||||
println!();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user