refactor: improve docs, cleanup unnecessary types and improve code

This commit is contained in:
Vladimir Fomene 2023-09-06 09:47:45 +03:00
parent 4104206980
commit d43ae0231f
No known key found for this signature in database
GPG Key ID: 4D8AB1DF0F7BFBC3
11 changed files with 175 additions and 169 deletions

View File

@ -25,7 +25,7 @@ use bdk_chain::{
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;
@ -95,73 +95,51 @@ pub struct Wallet<D = ()> {
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,
@ -170,21 +148,22 @@ impl<K, A> From<local_chain::ChangeSet> for WalletChangeSet<K, A> {
}
}
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)]
@ -332,7 +311,7 @@ impl<D> Wallet<D> {
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);

View File

@ -6,7 +6,7 @@ use alloc::vec::Vec;
use bitcoin::{OutPoint, Transaction, TxOut};
use crate::{
keychain,
keychain, local_chain,
tx_graph::{self, TxGraph},
Anchor, Append,
};
@ -225,7 +225,16 @@ impl<A, K> From<keychain::ChangeSet<K>> for ChangeSet<A, keychain::ChangeSet<K>>
}
}
/// 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;
@ -234,17 +243,6 @@ pub trait Indexer {
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.

View File

@ -91,11 +91,10 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
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 {
@ -175,8 +174,10 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// 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
///

View File

@ -100,11 +100,3 @@ pub mod collections {
/// 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)
}
}

View File

@ -53,35 +53,19 @@ impl<I> Default for SpkTxOutIndex<I> {
}
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.
@ -93,6 +77,38 @@ impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
}
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

View File

@ -14,19 +14,19 @@ use std::{
/// 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())
@ -36,15 +36,15 @@ impl<A: Anchor> IncompleteTxGraph<A> {
/// 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);
@ -55,22 +55,20 @@ impl<A: Anchor> IncompleteTxGraph<A> {
}
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();
@ -122,8 +120,20 @@ impl IncompleteTxGraph<ConfirmationHeightAnchor> {
}
}
/// 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.
///
@ -136,7 +146,6 @@ pub trait ElectrumExt<A> {
/// 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>,
@ -145,7 +154,7 @@ pub trait ElectrumExt<A> {
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.
///
@ -157,13 +166,13 @@ pub trait ElectrumExt<A> {
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,
@ -172,11 +181,11 @@ pub trait ElectrumExt<A> {
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>,
@ -185,14 +194,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
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()))
@ -202,9 +204,9 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
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)
@ -216,7 +218,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
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())),
@ -229,7 +231,7 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
populate_with_spks(
self,
&cps,
&mut graph_update,
&mut relevant_txids,
keychain_spks,
stop_gap,
batch_size,
@ -240,12 +242,12 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
}
}
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(),
)?;
@ -271,10 +273,16 @@ impl ElectrumExt<ConfirmationHeightAnchor> for Client {
})
.collect::<BTreeMap<_, _>>();
break (chain_update, graph_update, keychain_update);
break (
ElectrumUpdate {
chain_update,
relevant_txids,
},
keychain_update,
);
};
Ok(update)
Ok((electrum_update, keychain_update))
}
}
@ -398,7 +406,7 @@ fn determine_tx_anchor(
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();
@ -447,7 +455,7 @@ fn populate_with_outpoints(
};
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);
}
@ -459,7 +467,7 @@ fn populate_with_outpoints(
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 {
@ -484,7 +492,7 @@ fn populate_with_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);
}
@ -495,7 +503,7 @@ fn populate_with_txids(
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,
@ -528,7 +536,7 @@ fn populate_with_spks<I: Ord + Clone>(
}
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);
}

View File

@ -3,14 +3,14 @@
//! 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.
@ -18,7 +18,7 @@
//! 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

View File

@ -13,7 +13,7 @@ use bdk_chain::{
};
use bdk_electrum::{
electrum_client::{self, ElectrumApi},
ElectrumExt,
ElectrumExt, ElectrumUpdate,
};
use example_cli::{
anyhow::{self, Context},
@ -66,16 +66,16 @@ type ChangeSet = (
);
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",
@ -251,18 +251,24 @@ fn main() -> anyhow::Result<()> {
// 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
@ -270,7 +276,7 @@ fn main() -> anyhow::Result<()> {
.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();

View File

@ -7,11 +7,13 @@ use std::io::Write;
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>> {
@ -53,16 +55,20 @@ 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),

View File

@ -2,7 +2,7 @@ use std::{io::Write, str::FromStr};
use bdk::{
bitcoin::{Address, Network},
wallet::{AddressIndex, WalletUpdate},
wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraAsyncExt};
@ -58,7 +58,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.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),

View File

@ -7,7 +7,7 @@ use std::{io::Write, str::FromStr};
use bdk::{
bitcoin::{Address, Network},
wallet::{AddressIndex, WalletUpdate},
wallet::{AddressIndex, Update},
SignOptions, Wallet,
};
use bdk_esplora::{esplora_client, EsploraExt};
@ -57,7 +57,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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),