keychain::{self, KeychainTxOutIndex},
local_chain::{self, CannotConnectError, CheckPoint, CheckPointIter, LocalChain},
tx_graph::{CanonicalTx, TxGraph},
- Anchor, Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
+ Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut,
IndexedTxGraph, Persist, PersistBackend,
};
use bitcoin::consensus::encode::serialize;
secp: SecpCtx,
}
-/// A structure to update [`Wallet`].
+/// An update to [`Wallet`].
///
/// It updates [`bdk_chain::keychain::KeychainTxOutIndex`], [`bdk_chain::TxGraph`] and [`local_chain::LocalChain`] atomically.
-#[derive(Debug, Clone)]
-pub struct WalletUpdate<K, A> {
+#[derive(Debug, Clone, Default)]
+pub struct Update {
/// Contains the last active derivation indices per keychain (`K`), which is used to update the
/// [`KeychainTxOutIndex`].
- pub last_active_indices: BTreeMap<K, u32>,
+ pub last_active_indices: BTreeMap<KeychainKind, u32>,
- /// Update for the [`TxGraph`].
- pub graph: TxGraph<A>,
+ /// Update for the wallet's internal [`TxGraph`].
+ pub graph: TxGraph<ConfirmationTimeAnchor>,
- /// Update for the [`LocalChain`].
+ /// Update for the wallet's internal [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
pub chain: Option<local_chain::Update>,
}
-impl<K, A> Default for WalletUpdate<K, A> {
- fn default() -> Self {
- Self {
- last_active_indices: BTreeMap::new(),
- graph: TxGraph::default(),
- chain: None,
- }
- }
-}
-
-/// A structure that records the corresponding changes as result of applying an [`WalletUpdate`].
-#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
-pub struct WalletChangeSet<K, A> {
+/// The changes made to a wallet by applying an [`Update`].
+#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, Default)]
+pub struct ChangeSet {
/// Changes to the [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
pub chain: local_chain::ChangeSet,
- /// ChangeSet to [`IndexedTxGraph`].
+ /// Changes to [`IndexedTxGraph`].
///
/// [`IndexedTxGraph`]: bdk_chain::indexed_tx_graph::IndexedTxGraph
- #[serde(bound(
- deserialize = "K: Ord + serde::Deserialize<'de>, A: Ord + serde::Deserialize<'de>",
- serialize = "K: Ord + serde::Serialize, A: Ord + serde::Serialize",
- ))]
- pub index_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>,
+ pub indexed_tx_graph:
+ indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>,
}
-impl<K, A> Default for WalletChangeSet<K, A> {
- fn default() -> Self {
- Self {
- chain: Default::default(),
- index_tx_graph: Default::default(),
- }
- }
-}
-
-impl<K: Ord, A: Anchor> Append for WalletChangeSet<K, A> {
+impl Append for ChangeSet {
fn append(&mut self, other: Self) {
Append::append(&mut self.chain, other.chain);
- Append::append(&mut self.index_tx_graph, other.index_tx_graph);
+ Append::append(&mut self.indexed_tx_graph, other.indexed_tx_graph);
}
fn is_empty(&self) -> bool {
- self.chain.is_empty() && self.index_tx_graph.is_empty()
+ self.chain.is_empty() && self.indexed_tx_graph.is_empty()
}
}
-impl<K, A> From<local_chain::ChangeSet> for WalletChangeSet<K, A> {
+impl From<local_chain::ChangeSet> for ChangeSet {
fn from(chain: local_chain::ChangeSet) -> Self {
Self {
chain,
}
}
-impl<K, A> From<indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>> for WalletChangeSet<K, A> {
- fn from(index_tx_graph: indexed_tx_graph::ChangeSet<A, keychain::ChangeSet<K>>) -> Self {
+impl From<indexed_tx_graph::ChangeSet<ConfirmationTimeAnchor, keychain::ChangeSet<KeychainKind>>>
+ for ChangeSet
+{
+ fn from(
+ indexed_tx_graph: indexed_tx_graph::ChangeSet<
+ ConfirmationTimeAnchor,
+ keychain::ChangeSet<KeychainKind>,
+ >,
+ ) -> Self {
Self {
- index_tx_graph,
+ indexed_tx_graph,
..Default::default()
}
}
}
-/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
-pub type Update = WalletUpdate<KeychainKind, ConfirmationTimeAnchor>;
-
-/// The changeset produced internally by [`Wallet`] when mutated.
-pub type ChangeSet = WalletChangeSet<KeychainKind, ConfirmationTimeAnchor>;
-
/// The address index selection strategy to use to derived an address from the wallet's external
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
#[derive(Debug)]
let changeset = db.load_from_persistence().map_err(NewError::Persist)?;
chain.apply_changeset(&changeset.chain);
- indexed_graph.apply_changeset(changeset.index_tx_graph);
+ indexed_graph.apply_changeset(changeset.indexed_tx_graph);
let persist = Persist::new(db);
use bitcoin::{OutPoint, Transaction, TxOut};
use crate::{
- keychain,
+ keychain, local_chain,
tx_graph::{self, TxGraph},
Anchor, Append,
};
}
}
-/// Represents a structure that can index transaction data.
+impl<A, IA> From<ChangeSet<A, IA>> for (local_chain::ChangeSet, ChangeSet<A, IA>) {
+ fn from(indexed_changeset: ChangeSet<A, IA>) -> Self {
+ (local_chain::ChangeSet::default(), indexed_changeset)
+ }
+}
+
+/// Utilities for indexing transaction data.
+///
+/// Types which implement this trait can be used to construct an [`IndexedTxGraph`].
+/// This trait's methods should rarely be called directly.
pub trait Indexer {
/// The resultant "changeset" when new transaction data is indexed.
type ChangeSet;
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet;
/// Scans a transaction for relevant outpoints, which are stored and indexed internally.
- ///
- /// If the matched script pubkey is part of the lookahead, the last stored index is updated for
- /// the script pubkey's keychain and the [`ChangeSet`] returned will reflect the
- /// change.
- ///
- /// Typically, this method is used in two situations:
- ///
- /// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
- /// your txouts.
- /// 2. When getting new data from the chain, you usually scan it before incorporating it into
- /// your chain state.
fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet;
/// Apply changeset to itself.
type ChangeSet = super::ChangeSet<K>;
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
- let mut changeset = super::ChangeSet::<K>::default();
- for (keychain, index) in self.inner.index_txout(outpoint, txout) {
- changeset.append(self.reveal_to_target(&keychain, index).1);
+ match self.inner.scan_txout(outpoint, txout).cloned() {
+ Some((keychain, index)) => self.reveal_to_target(&keychain, index).1,
+ None => super::ChangeSet::default(),
}
- changeset
}
fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::ChangeSet {
/// Set the lookahead count for `keychain`.
///
- /// The lookahead is the number of scripts to cache ahead of the last stored script index. This
- /// is useful during a scan via [`Indexer::index_tx`] or [`Indexer::index_txout`].
+ /// The lookahead is the number of scripts to cache ahead of the last revealed script index. This
+ /// is useful to find outputs you own when processing block data that lie beyond the last revealed
+ /// index. In certain situations, such as when performing an initial scan of the blockchain during
+ /// wallet import, it may be uncertain or unknown what the last revealed index is.
///
/// # Panics
///
/// How many confirmations are needed f or a coinbase output to be spent.
pub const COINBASE_MATURITY: u32 = 100;
-
-impl<A, IA> From<indexed_tx_graph::ChangeSet<A, IA>>
- for (local_chain::ChangeSet, indexed_tx_graph::ChangeSet<A, IA>)
-{
- fn from(indexed_changeset: indexed_tx_graph::ChangeSet<A, IA>) -> Self {
- (local_chain::ChangeSet::default(), indexed_changeset)
- }
-}
}
impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
- type ChangeSet = BTreeSet<I>;
+ type ChangeSet = ();
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
- let spk_i = self.spk_indices.get(&txout.script_pubkey);
- let mut scanned_indices = BTreeSet::new();
- if let Some(spk_i) = spk_i {
- self.txouts.insert(outpoint, (spk_i.clone(), txout.clone()));
- self.spk_txouts.insert((spk_i.clone(), outpoint));
- self.unused.remove(spk_i);
- scanned_indices.insert(spk_i.clone());
- }
- scanned_indices
+ self.scan_txout(outpoint, txout);
+ Default::default()
}
fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet {
- let mut scanned_indices = BTreeSet::new();
-
- for (i, txout) in tx.output.iter().enumerate() {
- let op = OutPoint::new(tx.txid(), i as u32);
- let mut txout_indices = self.index_txout(op, txout);
- scanned_indices.append(&mut txout_indices);
- }
-
- scanned_indices
+ self.scan(tx);
+ Default::default()
}
- fn initial_changeset(&self) -> Self::ChangeSet {
- self.spks.keys().cloned().collect()
- }
+ fn initial_changeset(&self) -> Self::ChangeSet {}
fn apply_changeset(&mut self, _changeset: Self::ChangeSet) {
// This applies nothing.
}
impl<I: Clone + Ord> SpkTxOutIndex<I> {
+ /// Scans a transaction's outputs for matching script pubkeys.
+ ///
+ /// Typically, this is used in two situations:
+ ///
+ /// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
+ /// your txouts.
+ /// 2. When getting new data from the chain, you usually scan it before incorporating it into your chain state.
+ pub fn scan(&mut self, tx: &Transaction) -> BTreeSet<I> {
+ let mut scanned_indices = BTreeSet::new();
+ let txid = tx.txid();
+ for (i, txout) in tx.output.iter().enumerate() {
+ let op = OutPoint::new(txid, i as u32);
+ if let Some(spk_i) = self.scan_txout(op, txout) {
+ scanned_indices.insert(spk_i.clone());
+ }
+ }
+
+ scanned_indices
+ }
+
+ /// Scan a single `TxOut` for a matching script pubkey and returns the index that matches the
+ /// script pubkey (if any).
+ pub fn scan_txout(&mut self, op: OutPoint, txout: &TxOut) -> Option<&I> {
+ let spk_i = self.spk_indices.get(&txout.script_pubkey);
+ if let Some(spk_i) = spk_i {
+ self.txouts.insert(op, (spk_i.clone(), txout.clone()));
+ self.spk_txouts.insert((spk_i.clone(), op));
+ self.unused.remove(spk_i);
+ }
+ spk_i
+ }
+
/// Get a reference to the set of indexed outpoints.
pub fn outpoints(&self) -> &BTreeSet<(I, OutPoint)> {
&self.spk_txouts
/// We assume that a block of this depth and deeper cannot be reorged.
const ASSUME_FINAL_DEPTH: u32 = 8;
-/// Represents a [`TxGraph`] update fetched from an Electrum server, but excludes full transactions.
+/// Represents updates fetched from an Electrum server, but excludes full transactions.
///
/// To provide a complete update to [`TxGraph`], you'll need to call [`Self::missing_full_txs`] to
-/// determine the full transactions missing from [`TxGraph`]. Then call [`Self::finalize`] to
+/// determine the full transactions missing from [`TxGraph`]. Then call [`Self::into_tx_graph`] to
/// fetch the full transactions from Electrum and finalize the update.
#[derive(Debug, Default, Clone)]
-pub struct IncompleteTxGraph<A>(HashMap<Txid, BTreeSet<A>>);
+pub struct RelevantTxids(HashMap<Txid, BTreeSet<ConfirmationHeightAnchor>>);
-impl<A: Anchor> IncompleteTxGraph<A> {
+impl RelevantTxids {
/// Determine the full transactions that are missing from `graph`.
///
- /// Refer to [`IncompleteTxGraph`] for more.
- pub fn missing_full_txs<A2>(&self, graph: &TxGraph<A2>) -> Vec<Txid> {
+ /// Refer to [`RelevantTxids`] for more details.
+ pub fn missing_full_txs<A: Anchor>(&self, graph: &TxGraph<A>) -> Vec<Txid> {
self.0
.keys()
.filter(move |&&txid| graph.as_ref().get_tx(txid).is_none())
/// Finalizes the [`TxGraph`] update by fetching `missing` txids from the `client`.
///
- /// Refer to [`IncompleteTxGraph`] for more.
- pub fn finalize(
+ /// Refer to [`RelevantTxids`] for more details.
+ pub fn into_tx_graph(
self,
client: &Client,
seen_at: Option<u64>,
missing: Vec<Txid>,
- ) -> Result<TxGraph<A>, Error> {
+ ) -> Result<TxGraph<ConfirmationHeightAnchor>, Error> {
let new_txs = client.batch_transaction_get(&missing)?;
- let mut graph = TxGraph::<A>::new(new_txs);
+ let mut graph = TxGraph::<ConfirmationHeightAnchor>::new(new_txs);
for (txid, anchors) in self.0 {
if let Some(seen_at) = seen_at {
let _ = graph.insert_seen_at(txid, seen_at);
}
Ok(graph)
}
-}
-impl IncompleteTxGraph<ConfirmationHeightAnchor> {
- /// Finalizes the [`IncompleteTxGraph`] with `new_txs` and anchors of type
+ /// Finalizes [`RelevantTxids`] with `new_txs` and anchors of type
/// [`ConfirmationTimeAnchor`].
///
/// **Note:** The confirmation time might not be precisely correct if there has been a reorg.
/// Electrum's API intends that we use the merkle proof API, we should change `bdk_electrum` to
/// use it.
- pub fn finalize_with_confirmation_time(
+ pub fn into_confirmation_time_tx_graph(
self,
client: &Client,
seen_at: Option<u64>,
missing: Vec<Txid>,
) -> Result<TxGraph<ConfirmationTimeAnchor>, Error> {
- let graph = self.finalize(client, seen_at, missing)?;
+ let graph = self.into_tx_graph(client, seen_at, missing)?;
let relevant_heights = {
let mut visited_heights = HashSet::new();
}
}
+/// 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: local_chain::Update,
+ /// Transaction updates from electrum
+ pub relevant_txids: RelevantTxids,
+}
+
/// Trait to extend [`Client`] functionality.
-pub trait ElectrumExt<A> {
+pub trait ElectrumExt {
/// Scan the blockchain (via electrum) for the data specified and returns updates for
/// [`bdk_chain`] data structures.
///
/// The 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.
- #[allow(clippy::type_complexity)]
fn scan<K: Ord + Clone>(
&self,
prev_tip: Option<CheckPoint>,
outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize,
batch_size: usize,
- ) -> Result<(local_chain::Update, IncompleteTxGraph<A>, BTreeMap<K, u32>), Error>;
+ ) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error>;
/// Convenience method to call [`scan`] without requiring a keychain.
///
txids: impl IntoIterator<Item = Txid>,
outpoints: impl IntoIterator<Item = OutPoint>,
batch_size: usize,
- ) -> Result<(local_chain::Update, IncompleteTxGraph<A>), Error> {
+ ) -> Result<ElectrumUpdate, Error> {
let spk_iter = misc_spks
.into_iter()
.enumerate()
.map(|(i, spk)| (i as u32, spk));
- let (chain, graph, _) = self.scan(
+ let (electrum_update, _) = self.scan(
prev_tip,
[((), spk_iter)].into(),
txids,
batch_size,
)?;
- Ok((chain, graph))
+ Ok(electrum_update)
}
}
-impl ElectrumExt<ConfirmationHeightAnchor> for Client {
+impl ElectrumExt for Client {
fn scan<K: Ord + Clone>(
&self,
prev_tip: Option<CheckPoint>,
outpoints: impl IntoIterator<Item = OutPoint>,
stop_gap: usize,
batch_size: usize,
- ) -> Result<
- (
- local_chain::Update,
- IncompleteTxGraph<ConfirmationHeightAnchor>,
- BTreeMap<K, u32>,
- ),
- Error,
- > {
+ ) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error> {
let mut request_spks = keychain_spks
.into_iter()
.map(|(k, s)| (k, s.into_iter()))
let txids = txids.into_iter().collect::<Vec<_>>();
let outpoints = outpoints.into_iter().collect::<Vec<_>>();
- let update = loop {
+ let (electrum_update, keychain_update) = loop {
let (tip, _) = construct_update_tip(self, prev_tip.clone())?;
- let mut graph_update = IncompleteTxGraph::<ConfirmationHeightAnchor>::default();
+ let mut relevant_txids = RelevantTxids::default();
let cps = tip
.iter()
.take(10)
scanned_spks.append(&mut populate_with_spks(
self,
&cps,
- &mut graph_update,
+ &mut relevant_txids,
&mut scanned_spks
.iter()
.map(|(i, (spk, _))| (i.clone(), spk.clone())),
populate_with_spks(
self,
&cps,
- &mut graph_update,
+ &mut relevant_txids,
keychain_spks,
stop_gap,
batch_size,
}
}
- populate_with_txids(self, &cps, &mut graph_update, &mut txids.iter().cloned())?;
+ populate_with_txids(self, &cps, &mut relevant_txids, &mut txids.iter().cloned())?;
let _txs = populate_with_outpoints(
self,
&cps,
- &mut graph_update,
+ &mut relevant_txids,
&mut outpoints.iter().cloned(),
)?;
})
.collect::<BTreeMap<_, _>>();
- break (chain_update, graph_update, keychain_update);
+ break (
+ ElectrumUpdate {
+ chain_update,
+ relevant_txids,
+ },
+ keychain_update,
+ );
};
- Ok(update)
+ Ok((electrum_update, keychain_update))
}
}
fn populate_with_outpoints(
client: &Client,
cps: &BTreeMap<u32, CheckPoint>,
- graph_update: &mut IncompleteTxGraph<ConfirmationHeightAnchor>,
+ relevant_txids: &mut RelevantTxids,
outpoints: &mut impl Iterator<Item = OutPoint>,
) -> Result<HashMap<Txid, Transaction>, Error> {
let mut full_txs = HashMap::new();
};
let anchor = determine_tx_anchor(cps, res.height, res.tx_hash);
- let tx_entry = graph_update.0.entry(res.tx_hash).or_default();
+ let tx_entry = relevant_txids.0.entry(res.tx_hash).or_default();
if let Some(anchor) = anchor {
tx_entry.insert(anchor);
}
fn populate_with_txids(
client: &Client,
cps: &BTreeMap<u32, CheckPoint>,
- graph_update: &mut IncompleteTxGraph<ConfirmationHeightAnchor>,
+ relevant_txids: &mut RelevantTxids,
txids: &mut impl Iterator<Item = Txid>,
) -> Result<(), Error> {
for txid in txids {
None => continue,
};
- let tx_entry = graph_update.0.entry(txid).or_default();
+ let tx_entry = relevant_txids.0.entry(txid).or_default();
if let Some(anchor) = anchor {
tx_entry.insert(anchor);
}
fn populate_with_spks<I: Ord + Clone>(
client: &Client,
cps: &BTreeMap<u32, CheckPoint>,
- graph_update: &mut IncompleteTxGraph<ConfirmationHeightAnchor>,
+ relevant_txids: &mut RelevantTxids,
spks: &mut impl Iterator<Item = (I, ScriptBuf)>,
stop_gap: usize,
batch_size: usize,
}
for tx in spk_history {
- let tx_entry = graph_update.0.entry(tx.tx_hash).or_default();
+ let tx_entry = relevant_txids.0.entry(tx.tx_hash).or_default();
if let Some(anchor) = determine_tx_anchor(cps, tx.height, tx.tx_hash) {
tx_entry.insert(anchor);
}
//! The star of the show is the [`ElectrumExt::scan`] method, which scans for relevant blockchain
//! data (via electrum) and outputs updates for [`bdk_chain`] structures as a tuple of form:
//!
-//! ([`bdk_chain::local_chain::Update`], [`IncompleteTxGraph`], `keychain_update`)
+//! ([`bdk_chain::local_chain::Update`], [`RelevantTxids`], `keychain_update`)
//!
-//! An [`IncompleteTxGraph`] only includes `txid`s and no full transactions. The caller is
+//! An [`RelevantTxids`] only includes `txid`s and no full transactions. The caller is
//! responsible for obtaining full transactions before applying. This can be done with
//! these steps:
//!
//! 1. Determine which full transactions are missing. The method [`missing_full_txs`] of
-//! [`IncompleteTxGraph`] can be used.
+//! [`RelevantTxids`] can be used.
//!
//! 2. Obtaining the full transactions. To do this via electrum, the method
//! [`batch_transaction_get`] can be used.
//! Refer to [`bdk_electrum_example`] for a complete example.
//!
//! [`ElectrumClient::scan`]: electrum_client::ElectrumClient::scan
-//! [`missing_full_txs`]: IncompleteTxGraph::missing_full_txs
+//! [`missing_full_txs`]: RelevantTxids::missing_full_txs
//! [`batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
//! [`bdk_electrum_example`]: https://github.com/LLFourn/bdk_core_staging/tree/master/bdk_electrum_example
};
use bdk_electrum::{
electrum_client::{self, ElectrumApi},
- ElectrumExt,
+ ElectrumExt, ElectrumUpdate,
};
use example_cli::{
anyhow::{self, Context},
);
fn main() -> anyhow::Result<()> {
- let (args, keymap, index, db, init_changeset) =
+ let (args, keymap, index, db, (disk_local_chain, disk_tx_graph)) =
example_cli::init::<ElectrumCommands, ChangeSet>(DB_MAGIC, DB_PATH)?;
let graph = Mutex::new({
let mut graph = IndexedTxGraph::new(index);
- graph.apply_changeset(init_changeset.1);
+ graph.apply_changeset(disk_tx_graph);
graph
});
- let chain = Mutex::new(LocalChain::from_changeset(init_changeset.0));
+ let chain = Mutex::new(LocalChain::from_changeset(disk_local_chain));
let electrum_url = match args.network {
Network::Bitcoin => "ssl://electrum.blockstream.info:50002",
// drop lock on graph and chain
drop((graph, chain));
- let (chain_update, graph_update) = client
+ let electrum_update = client
.scan_without_keychain(tip, spks, txids, outpoints, scan_options.batch_size)
.context("scanning the blockchain")?;
- (chain_update, graph_update, BTreeMap::new())
+ (electrum_update, BTreeMap::new())
}
};
- let (chain_update, incomplete_graph_update, keychain_update) = response;
+ let (
+ ElectrumUpdate {
+ chain_update,
+ relevant_txids,
+ },
+ keychain_update,
+ ) = response;
let missing_txids = {
let graph = &*graph.lock().unwrap();
- incomplete_graph_update.missing_full_txs(graph.graph())
+ relevant_txids.missing_full_txs(graph.graph())
};
let now = std::time::UNIX_EPOCH
.expect("must get time")
.as_secs();
- let graph_update = incomplete_graph_update.finalize(&client, Some(now), missing_txids)?;
+ let graph_update = relevant_txids.into_tx_graph(&client, Some(now), missing_txids)?;
let db_changeset = {
let mut chain = chain.lock().unwrap();
use std::str::FromStr;
use bdk::bitcoin::Address;
-use bdk::wallet::WalletUpdate;
+use bdk::wallet::Update;
use bdk::SignOptions;
use bdk::{bitcoin::Network, Wallet};
-use bdk_electrum::electrum_client::{self, ElectrumApi};
-use bdk_electrum::ElectrumExt;
+use bdk_electrum::{
+ electrum_client::{self, ElectrumApi},
+ ElectrumExt, ElectrumUpdate,
+};
use bdk_file_store::Store;
fn main() -> Result<(), Box<dyn std::error::Error>> {
})
.collect();
- let (chain_update, incomplete_graph_update, keychain_update) =
- client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
+ let (
+ ElectrumUpdate {
+ chain_update,
+ relevant_txids,
+ },
+ keychain_update,
+ ) = client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
println!();
- let missing = incomplete_graph_update.missing_full_txs(wallet.as_ref());
- let graph_update =
- incomplete_graph_update.finalize_with_confirmation_time(&client, None, missing)?;
+ let missing = relevant_txids.missing_full_txs(wallet.as_ref());
+ let graph_update = relevant_txids.into_confirmation_time_tx_graph(&client, None, missing)?;
- let wallet_update = WalletUpdate {
+ let wallet_update = Update {
last_active_indices: keychain_update,
graph: graph_update,
chain: Some(chain_update),
use bdk::{
bitcoin::{Address, Network},
- wallet::{AddressIndex, WalletUpdate},
+ wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
.await?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights).await?;
- let update = WalletUpdate {
+ let update = Update {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),
use bdk::{
bitcoin::{Address, Network},
- wallet::{AddressIndex, WalletUpdate},
+ wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraExt};
client.update_tx_graph(keychain_spks, None, None, STOP_GAP, PARALLEL_REQUESTS)?;
let missing_heights = wallet.tx_graph().missing_heights(wallet.local_chain());
let chain_update = client.update_local_chain(prev_tip, missing_heights)?;
- let update = WalletUpdate {
+ let update = Update {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),