use bdk_chain::{
- bitcoin::{OutPoint, ScriptBuf, Txid},
- collections::{HashMap, HashSet},
+ bitcoin::{OutPoint, ScriptBuf, Transaction, Txid},
+ collections::{BTreeMap, HashMap, HashSet},
local_chain::CheckPoint,
+ spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult, TxCache},
tx_graph::TxGraph,
- Anchor, BlockId, ConfirmationHeightAnchor, ConfirmationTimeHeightAnchor,
+ BlockId, ConfirmationHeightAnchor, ConfirmationTimeHeightAnchor,
};
+use core::str::FromStr;
use electrum_client::{ElectrumApi, Error, HeaderNotification};
-use std::{collections::BTreeMap, fmt::Debug, str::FromStr};
+use std::sync::Arc;
/// We include a chain suffix of a certain length for the purpose of robustness.
const CHAIN_SUFFIX_LENGTH: u32 = 8;
-/// Combination of chain and transactions updates from electrum
-///
-/// We have to update the chain and the txids at the same time since we anchor the txids to
-/// the same chain tip that we check before and after we gather the txids.
-#[derive(Debug)]
-pub struct ElectrumUpdate {
- /// Chain update
- pub chain_update: CheckPoint,
- /// Tracks electrum updates in TxGraph
- pub graph_update: TxGraph<ConfirmationTimeHeightAnchor>,
-}
-
/// Trait to extend [`electrum_client::Client`] functionality.
pub trait ElectrumExt {
/// Full scan the keychain scripts specified with the blockchain (via an Electrum client) and
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `batch_size` specifies the max number of script pubkeys to request for in a
/// single batch request.
- fn full_scan<K: Ord + Clone, A: Anchor>(
+ fn full_scan<K: Ord + Clone>(
&self,
- prev_tip: CheckPoint,
- keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
- full_txs: Option<&TxGraph<A>>,
+ request: FullScanRequest<K>,
stop_gap: usize,
batch_size: usize,
- ) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error>;
+ ) -> Result<FullScanResult<K, ConfirmationHeightAnchor>, Error>;
/// Sync a set of scripts with the blockchain (via an Electrum client) for the data specified
/// and returns updates for [`bdk_chain`] data structures.
/// may include scripts that have been used, use [`full_scan`] with the keychain.
///
/// [`full_scan`]: ElectrumExt::full_scan
- fn sync<A: Anchor>(
+ fn sync(
&self,
- prev_tip: CheckPoint,
- misc_spks: impl IntoIterator<Item = ScriptBuf>,
- full_txs: Option<&TxGraph<A>>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
+ request: SyncRequest,
batch_size: usize,
- ) -> Result<ElectrumUpdate, Error>;
+ ) -> Result<SyncResult<ConfirmationHeightAnchor>, Error>;
}
impl<E: ElectrumApi> ElectrumExt for E {
- fn full_scan<K: Ord + Clone, A: Anchor>(
+ fn full_scan<K: Ord + Clone>(
&self,
- prev_tip: CheckPoint,
- keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
- full_txs: Option<&TxGraph<A>>,
+ mut request: FullScanRequest<K>,
stop_gap: usize,
batch_size: usize,
- ) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error> {
- let mut request_spks = keychain_spks
- .into_iter()
- .map(|(k, s)| (k, s.into_iter()))
- .collect::<BTreeMap<K, _>>();
+ ) -> Result<FullScanResult<K, ConfirmationHeightAnchor>, Error> {
+ let mut request_spks = request.spks_by_keychain;
+
+ // We keep track of already-scanned spks just in case a reorg happens and we need to do a
+ // rescan. We need to keep track of this as iterators in `keychain_spks` are "unbounded" so
+ // cannot be collected. In addition, we keep track of whether an spk has an active tx
+ // history for determining the `last_active_index`.
+ // * key: (keychain, spk_index) that identifies the spk.
+ // * val: (script_pubkey, has_tx_history).
let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();
- let (electrum_update, keychain_update) = loop {
- let (tip, _) = construct_update_tip(self, prev_tip.clone())?;
- let mut tx_graph = TxGraph::<ConfirmationHeightAnchor>::default();
- if let Some(txs) = full_txs {
- let _ =
- tx_graph.apply_update(txs.clone().map_anchors(|a| ConfirmationHeightAnchor {
- anchor_block: a.anchor_block(),
- confirmation_height: a.confirmation_height_upper_bound(),
- }));
- }
+ let update = loop {
+ let (tip, _) = construct_update_tip(self, request.chain_tip.clone())?;
+ let mut graph_update = TxGraph::<ConfirmationHeightAnchor>::default();
let cps = tip
.iter()
.take(10)
scanned_spks.append(&mut populate_with_spks(
self,
&cps,
- &mut tx_graph,
+ &mut request.tx_cache,
+ &mut graph_update,
&mut scanned_spks
.iter()
.map(|(i, (spk, _))| (i.clone(), spk.clone())),
populate_with_spks(
self,
&cps,
- &mut tx_graph,
+ &mut request.tx_cache,
+ &mut graph_update,
keychain_spks,
stop_gap,
batch_size,
let chain_update = tip;
- let graph_update = into_confirmation_time_tx_graph(self, &tx_graph)?;
-
let keychain_update = request_spks
.into_keys()
.filter_map(|k| {
})
.collect::<BTreeMap<_, _>>();
- break (
- ElectrumUpdate {
- chain_update,
- graph_update,
- },
- keychain_update,
- );
+ break FullScanResult {
+ graph_update,
+ chain_update,
+ last_active_indices: keychain_update,
+ };
};
- Ok((electrum_update, keychain_update))
+ Ok(update)
}
- fn sync<A: Anchor>(
+ fn sync(
&self,
- prev_tip: CheckPoint,
- misc_spks: impl IntoIterator<Item = ScriptBuf>,
- full_txs: Option<&TxGraph<A>>,
- txids: impl IntoIterator<Item = Txid>,
- outpoints: impl IntoIterator<Item = OutPoint>,
+ request: SyncRequest,
batch_size: usize,
- ) -> Result<ElectrumUpdate, Error> {
- let spk_iter = misc_spks
- .into_iter()
- .enumerate()
- .map(|(i, spk)| (i as u32, spk));
-
- let (mut electrum_update, _) = self.full_scan(
- prev_tip.clone(),
- [((), spk_iter)].into(),
- full_txs,
- usize::MAX,
- batch_size,
- )?;
-
- let (tip, _) = construct_update_tip(self, prev_tip)?;
+ ) -> Result<SyncResult<ConfirmationHeightAnchor>, Error> {
+ let mut tx_cache = request.tx_cache.clone();
+
+ let full_scan_req = FullScanRequest::from_chain_tip(request.chain_tip.clone())
+ .cache_txs(request.tx_cache)
+ .set_spks_for_keychain((), request.spks.enumerate().map(|(i, spk)| (i as u32, spk)));
+ let full_scan_res = self.full_scan(full_scan_req, usize::MAX, batch_size)?;
+
+ let (tip, _) = construct_update_tip(self, request.chain_tip)?;
let cps = tip
.iter()
.take(10)
.collect::<BTreeMap<u32, CheckPoint>>();
let mut tx_graph = TxGraph::<ConfirmationHeightAnchor>::default();
- populate_with_txids(self, &cps, &mut tx_graph, txids)?;
- populate_with_outpoints(self, &cps, &mut tx_graph, outpoints)?;
- let _ = electrum_update
- .graph_update
- .apply_update(into_confirmation_time_tx_graph(self, &tx_graph)?);
+ populate_with_txids(self, &cps, &mut tx_cache, &mut tx_graph, request.txids)?;
+ populate_with_outpoints(self, &cps, &mut tx_cache, &mut tx_graph, request.outpoints)?;
- Ok(electrum_update)
+ Ok(SyncResult {
+ chain_update: full_scan_res.chain_update,
+ graph_update: full_scan_res.graph_update,
+ })
}
}
+/// Trait that extends [`SyncResult`] and [`FullScanResult`] functionality.
+///
+/// Currently, only a single method exists that converts the update [`TxGraph`] to have an anchor
+/// type of [`ConfirmationTimeHeightAnchor`].
+pub trait ElectrumResultExt {
+ /// New result type with a [`TxGraph`] that contains the [`ConfirmationTimeHeightAnchor`].
+ type NewResult;
+
+ /// Convert result type to have an update [`TxGraph`] that contains the [`ConfirmationTimeHeightAnchor`] .
+ fn try_into_confirmation_time_result(
+ self,
+ client: &impl ElectrumApi,
+ ) -> Result<Self::NewResult, Error>;
+}
+
+impl<K> ElectrumResultExt for FullScanResult<K, ConfirmationHeightAnchor> {
+ type NewResult = FullScanResult<K, ConfirmationTimeHeightAnchor>;
+
+ fn try_into_confirmation_time_result(
+ self,
+ client: &impl ElectrumApi,
+ ) -> Result<Self::NewResult, Error> {
+ Ok(FullScanResult::<K, ConfirmationTimeHeightAnchor> {
+ graph_update: try_into_confirmation_time_result(self.graph_update, client)?,
+ chain_update: self.chain_update,
+ last_active_indices: self.last_active_indices,
+ })
+ }
+}
+
+impl ElectrumResultExt for SyncResult<ConfirmationHeightAnchor> {
+ type NewResult = SyncResult<ConfirmationTimeHeightAnchor>;
+
+ fn try_into_confirmation_time_result(
+ self,
+ client: &impl ElectrumApi,
+ ) -> Result<Self::NewResult, Error> {
+ Ok(SyncResult {
+ graph_update: try_into_confirmation_time_result(self.graph_update, client)?,
+ chain_update: self.chain_update,
+ })
+ }
+}
+
+fn try_into_confirmation_time_result(
+ graph_update: TxGraph<ConfirmationHeightAnchor>,
+ client: &impl ElectrumApi,
+) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
+ let relevant_heights = graph_update
+ .all_anchors()
+ .iter()
+ .map(|(a, _)| a.confirmation_height)
+ .collect::<HashSet<_>>();
+
+ let height_to_time = relevant_heights
+ .clone()
+ .into_iter()
+ .zip(
+ client
+ .batch_block_header(relevant_heights)?
+ .into_iter()
+ .map(|bh| bh.time as u64),
+ )
+ .collect::<HashMap<u32, u64>>();
+
+ Ok(graph_update.map_anchors(|a| ConfirmationTimeHeightAnchor {
+ anchor_block: a.anchor_block,
+ confirmation_height: a.confirmation_height,
+ confirmation_time: height_to_time[&a.confirmation_height],
+ }))
+}
+
/// Return a [`CheckPoint`] of the latest tip, that connects with `prev_tip`.
fn construct_update_tip(
client: &impl ElectrumApi,
fn populate_with_outpoints(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,
+ tx_cache: &mut TxCache,
tx_graph: &mut TxGraph<ConfirmationHeightAnchor>,
outpoints: impl IntoIterator<Item = OutPoint>,
) -> Result<(), Error> {
let res_tx = match tx_graph.get_tx(res.tx_hash) {
Some(tx) => tx,
None => {
- let res_tx = client.transaction_get(&res.tx_hash)?;
- let _ = tx_graph.insert_tx(res_tx);
- tx_graph.get_tx(res.tx_hash).expect("just inserted")
+ let res_tx = fetch_tx(client, tx_cache, res.tx_hash)?;
+ let _ = tx_graph.insert_tx(Arc::clone(&res_tx));
+ res_tx
}
};
has_spending = res_tx
fn populate_with_txids(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,
- tx_graph: &mut TxGraph<ConfirmationHeightAnchor>,
+ tx_cache: &mut TxCache,
+ graph_update: &mut TxGraph<ConfirmationHeightAnchor>,
txids: impl IntoIterator<Item = Txid>,
) -> Result<(), Error> {
for txid in txids {
- let tx = match client.transaction_get(&txid) {
+ let tx = match fetch_tx(client, tx_cache, txid) {
Ok(tx) => tx,
Err(electrum_client::Error::Protocol(_)) => continue,
Err(other_err) => return Err(other_err),
None => continue,
};
- if tx_graph.get_tx(txid).is_none() {
- let _ = tx_graph.insert_tx(tx);
+ if graph_update.get_tx(txid).is_none() {
+ // TODO: We need to be able to insert an `Arc` of a transaction.
+ let _ = graph_update.insert_tx(tx);
}
if let Some(anchor) = anchor {
- let _ = tx_graph.insert_anchor(txid, anchor);
+ let _ = graph_update.insert_anchor(txid, anchor);
}
}
Ok(())
}
+fn fetch_tx<C: ElectrumApi>(
+ client: &C,
+ tx_cache: &mut TxCache,
+ txid: Txid,
+) -> Result<Arc<Transaction>, Error> {
+ use bdk_chain::collections::hash_map::Entry;
+ Ok(match tx_cache.entry(txid) {
+ Entry::Occupied(entry) => entry.get().clone(),
+ Entry::Vacant(entry) => entry
+ .insert(Arc::new(client.transaction_get(&txid)?))
+ .clone(),
+ })
+}
+
fn populate_with_spks<I: Ord + Clone>(
client: &impl ElectrumApi,
cps: &BTreeMap<u32, CheckPoint>,
- tx_graph: &mut TxGraph<ConfirmationHeightAnchor>,
+ tx_cache: &mut TxCache,
+ graph_update: &mut TxGraph<ConfirmationHeightAnchor>,
spks: &mut impl Iterator<Item = (I, ScriptBuf)>,
stop_gap: usize,
batch_size: usize,
unused_spk_count = 0;
}
- for tx in spk_history {
- let mut update = TxGraph::<ConfirmationHeightAnchor>::default();
-
- if tx_graph.get_tx(tx.tx_hash).is_none() {
- let full_tx = client.transaction_get(&tx.tx_hash)?;
- update = TxGraph::<ConfirmationHeightAnchor>::new([full_tx]);
+ for tx_res in spk_history {
+ let _ = graph_update.insert_tx(fetch_tx(client, tx_cache, tx_res.tx_hash)?);
+ if let Some(anchor) = determine_tx_anchor(cps, tx_res.height, tx_res.tx_hash) {
+ let _ = graph_update.insert_anchor(tx_res.tx_hash, anchor);
}
-
- if let Some(anchor) = determine_tx_anchor(cps, tx.height, tx.tx_hash) {
- let _ = update.insert_anchor(tx.tx_hash, anchor);
- }
-
- let _ = tx_graph.apply_update(update);
}
}
}
}
-
-fn into_confirmation_time_tx_graph(
- client: &impl ElectrumApi,
- tx_graph: &TxGraph<ConfirmationHeightAnchor>,
-) -> Result<TxGraph<ConfirmationTimeHeightAnchor>, Error> {
- let relevant_heights = tx_graph
- .all_anchors()
- .iter()
- .map(|(a, _)| a.confirmation_height)
- .collect::<HashSet<_>>();
-
- let height_to_time = relevant_heights
- .clone()
- .into_iter()
- .zip(
- client
- .batch_block_header(relevant_heights)?
- .into_iter()
- .map(|bh| bh.time as u64),
- )
- .collect::<HashMap<u32, u64>>();
-
- let new_graph = tx_graph
- .clone()
- .map_anchors(|a| ConfirmationTimeHeightAnchor {
- anchor_block: a.anchor_block,
- confirmation_height: a.confirmation_height,
- confirmation_time: height_to_time[&a.confirmation_height],
- });
- Ok(new_graph)
-}
bitcoin::{hashes::Hash, Address, Amount, ScriptBuf, WScriptHash},
keychain::Balance,
local_chain::LocalChain,
+ spk_client::SyncRequest,
ConfirmationTimeHeightAnchor, IndexedTxGraph, SpkTxOutIndex,
};
-use bdk_electrum::{ElectrumExt, ElectrumUpdate};
+use bdk_electrum::{ElectrumExt, ElectrumResultExt};
use bdk_testenv::{anyhow, anyhow::Result, bitcoincore_rpc::RpcApi, TestEnv};
fn get_balance(
// Sync up to tip.
env.wait_until_electrum_sees_block()?;
- let ElectrumUpdate {
- chain_update,
- graph_update,
- } = client.sync::<ConfirmationTimeHeightAnchor>(
- recv_chain.tip(),
- [spk_to_track],
- Some(recv_graph.graph()),
- None,
- None,
- 5,
- )?;
+ let update = client
+ .sync(
+ SyncRequest::from_chain_tip(recv_chain.tip())
+ .chain_spks(core::iter::once(spk_to_track)),
+ 5,
+ )?
+ .try_into_confirmation_time_result(&client)?;
let _ = recv_chain
- .apply_update(chain_update)
+ .apply_update(update.chain_update)
.map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?;
- let _ = recv_graph.apply_update(graph_update);
+ let _ = recv_graph.apply_update(update.graph_update);
// Check to see if tx is confirmed.
assert_eq!(
// Sync up to tip.
env.wait_until_electrum_sees_block()?;
- let ElectrumUpdate {
- chain_update,
- graph_update,
- } = client.sync::<ConfirmationTimeHeightAnchor>(
- recv_chain.tip(),
- [spk_to_track.clone()],
- Some(recv_graph.graph()),
- None,
- None,
- 5,
- )?;
+ let update = client
+ .sync(
+ SyncRequest::from_chain_tip(recv_chain.tip()).chain_spks([spk_to_track.clone()]),
+ 5,
+ )?
+ .try_into_confirmation_time_result(&client)?;
let _ = recv_chain
- .apply_update(chain_update)
+ .apply_update(update.chain_update)
.map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?;
- let _ = recv_graph.apply_update(graph_update.clone());
+ let _ = recv_graph.apply_update(update.graph_update.clone());
// Retain a snapshot of all anchors before reorg process.
- let initial_anchors = graph_update.all_anchors();
+ let initial_anchors = update.graph_update.all_anchors();
// Check if initial balance is correct.
assert_eq!(
env.reorg_empty_blocks(depth)?;
env.wait_until_electrum_sees_block()?;
- let ElectrumUpdate {
- chain_update,
- graph_update,
- } = client.sync::<ConfirmationTimeHeightAnchor>(
- recv_chain.tip(),
- [spk_to_track.clone()],
- Some(recv_graph.graph()),
- None,
- None,
- 5,
- )?;
+ let update = client
+ .sync(
+ SyncRequest::from_chain_tip(recv_chain.tip()).chain_spks([spk_to_track.clone()]),
+ 5,
+ )?
+ .try_into_confirmation_time_result(&client)?;
let _ = recv_chain
- .apply_update(chain_update)
+ .apply_update(update.chain_update)
.map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?;
// Check to see if a new anchor is added during current reorg.
- if !initial_anchors.is_superset(graph_update.all_anchors()) {
+ if !initial_anchors.is_superset(update.graph_update.all_anchors()) {
println!("New anchor added at reorg depth {}", depth);
}
- let _ = recv_graph.apply_update(graph_update);
+ let _ = recv_graph.apply_update(update.graph_update);
assert_eq!(
get_balance(&recv_chain, &recv_graph)?,
use std::{
- collections::BTreeMap,
io::{self, Write},
sync::Mutex,
};
use bdk_chain::{
- bitcoin::{constants::genesis_block, Address, Network, OutPoint, Txid},
+ bitcoin::{constants::genesis_block, Address, Network, Txid},
+ collections::BTreeSet,
indexed_tx_graph::{self, IndexedTxGraph},
keychain,
local_chain::{self, LocalChain},
+ spk_client::{FullScanRequest, SyncRequest},
Append, ConfirmationHeightAnchor,
};
use bdk_electrum::{
electrum_client::{self, Client, ElectrumApi},
- ElectrumExt, ElectrumUpdate,
+ ElectrumExt,
};
use example_cli::{
anyhow::{self, Context},
let client = electrum_cmd.electrum_args().client(args.network)?;
- let response = match electrum_cmd.clone() {
+ let (chain_update, mut graph_update, keychain_update) = match electrum_cmd.clone() {
ElectrumCommands::Scan {
stop_gap,
scan_options,
..
} => {
- let (keychain_spks, tip) = {
+ let request = {
let graph = &*graph.lock().unwrap();
let chain = &*chain.lock().unwrap();
- let keychain_spks = graph
- .index
- .all_unbounded_spk_iters()
- .into_iter()
- .map(|(keychain, iter)| {
- let mut first = true;
- let spk_iter = iter.inspect(move |(i, _)| {
- if first {
- eprint!("\nscanning {}: ", keychain);
- first = false;
+ FullScanRequest::from_chain_tip(chain.tip())
+ .cache_graph_txs(graph.graph())
+ .set_spks_for_keychain(
+ Keychain::External,
+ graph
+ .index
+ .unbounded_spk_iter(&Keychain::External)
+ .into_iter()
+ .flatten(),
+ )
+ .set_spks_for_keychain(
+ Keychain::Internal,
+ graph
+ .index
+ .unbounded_spk_iter(&Keychain::Internal)
+ .into_iter()
+ .flatten(),
+ )
+ .inspect_spks_for_all_keychains({
+ let mut once = BTreeSet::new();
+ move |k, spk_i, _| {
+ if once.insert(k) {
+ eprint!("\nScanning {}: ", k);
+ } else {
+ eprint!("{} ", spk_i);
}
-
- eprint!("{} ", i);
let _ = io::stdout().flush();
- });
- (keychain, spk_iter)
+ }
})
- .collect::<BTreeMap<_, _>>();
-
- let tip = chain.tip();
- (keychain_spks, tip)
};
- client
- .full_scan::<_, ConfirmationHeightAnchor>(
- tip,
- keychain_spks,
- Some(graph.lock().unwrap().graph()),
- stop_gap,
- scan_options.batch_size,
- )
- .context("scanning the blockchain")?
+ let res = client
+ .full_scan::<_>(request, stop_gap, scan_options.batch_size)
+ .context("scanning the blockchain")?;
+ (
+ res.chain_update,
+ res.graph_update,
+ Some(res.last_active_indices),
+ )
}
ElectrumCommands::Sync {
mut unused_spks,
// Get a short lock on the tracker to get the spks we're interested in
let graph = graph.lock().unwrap();
let chain = chain.lock().unwrap();
- let chain_tip = chain.tip().block_id();
if !(all_spks || unused_spks || utxos || unconfirmed) {
unused_spks = true;
unused_spks = false;
}
- let mut spks: Box<dyn Iterator<Item = bdk_chain::bitcoin::ScriptBuf>> =
- Box::new(core::iter::empty());
+ let chain_tip = chain.tip();
+ let mut request =
+ SyncRequest::from_chain_tip(chain_tip.clone()).cache_graph_txs(graph.graph());
+
if all_spks {
let all_spks = graph
.index
.revealed_spks(..)
.map(|(k, i, spk)| (k.to_owned(), i, spk.to_owned()))
.collect::<Vec<_>>();
- spks = Box::new(spks.chain(all_spks.into_iter().map(|(k, i, spk)| {
- eprintln!("scanning {}:{}", k, i);
+ request = request.chain_spks(all_spks.into_iter().map(|(k, spk_i, spk)| {
+ eprintln!("scanning {}: {}", k, spk_i);
spk
- })));
+ }));
}
if unused_spks {
let unused_spks = graph
.unused_spks()
.map(|(k, i, spk)| (k, i, spk.to_owned()))
.collect::<Vec<_>>();
- spks = Box::new(spks.chain(unused_spks.into_iter().map(|(k, i, spk)| {
- eprintln!(
- "Checking if address {} {}:{} has been used",
- Address::from_script(&spk, args.network).unwrap(),
- k,
- i,
- );
- spk
- })));
+ request =
+ request.chain_spks(unused_spks.into_iter().map(move |(k, spk_i, spk)| {
+ eprintln!(
+ "Checking if address {} {}:{} has been used",
+ Address::from_script(&spk, args.network).unwrap(),
+ k,
+ spk_i,
+ );
+ spk
+ }));
}
- let mut outpoints: Box<dyn Iterator<Item = OutPoint>> = Box::new(core::iter::empty());
-
if utxos {
let init_outpoints = graph.index.outpoints();
let utxos = graph
.graph()
- .filter_chain_unspents(&*chain, chain_tip, init_outpoints)
+ .filter_chain_unspents(&*chain, chain_tip.block_id(), init_outpoints)
.map(|(_, utxo)| utxo)
.collect::<Vec<_>>();
-
- outpoints = Box::new(
- utxos
- .into_iter()
- .inspect(|utxo| {
- eprintln!(
- "Checking if outpoint {} (value: {}) has been spent",
- utxo.outpoint, utxo.txout.value
- );
- })
- .map(|utxo| utxo.outpoint),
- );
+ request = request.chain_outpoints(utxos.into_iter().map(|utxo| {
+ eprintln!(
+ "Checking if outpoint {} (value: {}) has been spent",
+ utxo.outpoint, utxo.txout.value
+ );
+ utxo.outpoint
+ }));
};
- let mut txids: Box<dyn Iterator<Item = Txid>> = Box::new(core::iter::empty());
-
if unconfirmed {
let unconfirmed_txids = graph
.graph()
- .list_chain_txs(&*chain, chain_tip)
+ .list_chain_txs(&*chain, chain_tip.block_id())
.filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed())
.map(|canonical_tx| canonical_tx.tx_node.txid)
.collect::<Vec<Txid>>();
- txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| {
- eprintln!("Checking if {} is confirmed yet", txid);
- }));
+ request = request.chain_txids(
+ unconfirmed_txids
+ .into_iter()
+ .inspect(|txid| eprintln!("Checking if {} is confirmed yet", txid)),
+ );
}
- let electrum_update = client
- .sync::<ConfirmationHeightAnchor>(
- chain.tip(),
- spks,
- Some(graph.graph()),
- txids,
- outpoints,
- scan_options.batch_size,
- )
+ let res = client
+ .sync(request, scan_options.batch_size)
.context("scanning the blockchain")?;
// drop lock on graph and chain
drop((graph, chain));
- (electrum_update, BTreeMap::new())
+ (res.chain_update, res.graph_update, None)
}
};
- let (
- ElectrumUpdate {
- chain_update,
- mut graph_update,
- },
- keychain_update,
- ) = response;
-
let now = std::time::UNIX_EPOCH
.elapsed()
.expect("must get time")
let mut chain = chain.lock().unwrap();
let mut graph = graph.lock().unwrap();
- let chain = chain.apply_update(chain_update)?;
-
- let indexed_tx_graph = {
- let mut changeset =
- indexed_tx_graph::ChangeSet::<ConfirmationHeightAnchor, _>::default();
- let (_, indexer) = graph.index.reveal_to_target_multi(&keychain_update);
- changeset.append(indexed_tx_graph::ChangeSet {
- indexer,
- ..Default::default()
- });
- changeset.append(graph.apply_update(graph_update.map_anchors(|a| {
- ConfirmationHeightAnchor {
- anchor_block: a.anchor_block,
- confirmation_height: a.confirmation_height,
- }
- })));
- changeset
- };
-
- (chain, indexed_tx_graph)
+ let chain_changeset = chain.apply_update(chain_update)?;
+
+ let mut indexed_tx_graph_changeset =
+ indexed_tx_graph::ChangeSet::<ConfirmationHeightAnchor, _>::default();
+ if let Some(keychain_update) = keychain_update {
+ let (_, keychain_changeset) = graph.index.reveal_to_target_multi(&keychain_update);
+ indexed_tx_graph_changeset.append(keychain_changeset.into());
+ }
+ indexed_tx_graph_changeset.append(graph.apply_update(graph_update));
+
+ (chain_changeset, indexed_tx_graph_changeset)
};
let mut db = db.lock().unwrap();