refactor(electrum_ext): rename scan_without_keychain to sync and scan to full_scan
removed txids and outpoints params from full_scan
This commit is contained in:
parent
7eff024213
commit
95d34854f4
@ -1,3 +1,7 @@
|
||||
# BDK Electrum
|
||||
|
||||
BDK Electrum client library for updating the keychain tracker.
|
||||
BDK Electrum extends [`electrum-client`] to update [`bdk_chain`] structures
|
||||
from an Electrum server.
|
||||
|
||||
[`electrum-client`]: https://docs.rs/electrum-client/
|
||||
[`bdk_chain`]: https://docs.rs/bdk-chain/
|
||||
|
@ -134,64 +134,54 @@ pub struct ElectrumUpdate {
|
||||
|
||||
/// Trait to extend [`Client`] functionality.
|
||||
pub trait ElectrumExt {
|
||||
/// Scan the blockchain (via electrum) for the data specified and returns updates for
|
||||
/// [`bdk_chain`] data structures.
|
||||
/// Full scan the keychain scripts specified with the blockchain (via an Electrum client) and
|
||||
/// returns updates for [`bdk_chain`] data structures.
|
||||
///
|
||||
/// - `prev_tip`: the most recent blockchain tip present locally
|
||||
/// - `keychain_spks`: keychains that we want to scan transactions for
|
||||
/// - `txids`: transactions for which we want updated [`Anchor`]s
|
||||
/// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
|
||||
/// want to included in the update
|
||||
///
|
||||
/// The scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
|
||||
/// 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 scan<K: Ord + Clone>(
|
||||
fn full_scan<K: Ord + Clone>(
|
||||
&self,
|
||||
prev_tip: CheckPoint,
|
||||
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
|
||||
txids: impl IntoIterator<Item = Txid>,
|
||||
outpoints: impl IntoIterator<Item = OutPoint>,
|
||||
stop_gap: usize,
|
||||
batch_size: usize,
|
||||
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error>;
|
||||
|
||||
/// Convenience method to call [`scan`] without requiring a keychain.
|
||||
/// Sync a set of scripts with the blockchain (via an Electrum client) for the data specified
|
||||
/// and returns updates for [`bdk_chain`] data structures.
|
||||
///
|
||||
/// [`scan`]: ElectrumExt::scan
|
||||
fn scan_without_keychain(
|
||||
/// - `prev_tip`: the most recent blockchain tip present locally
|
||||
/// - `misc_spks`: an iterator of scripts we want to sync transactions for
|
||||
/// - `txids`: transactions for which we want updated [`Anchor`]s
|
||||
/// - `outpoints`: transactions associated with these outpoints (residing, spending) that we
|
||||
/// want to include in the update
|
||||
///
|
||||
/// `batch_size` specifies the max number of script pubkeys to request for in a single batch
|
||||
/// request.
|
||||
///
|
||||
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
|
||||
/// may include scripts that have been used, use [`full_scan`] with the keychain.
|
||||
///
|
||||
/// [`full_scan`]: ElectrumExt::full_scan
|
||||
fn sync(
|
||||
&self,
|
||||
prev_tip: CheckPoint,
|
||||
misc_spks: impl IntoIterator<Item = ScriptBuf>,
|
||||
txids: impl IntoIterator<Item = Txid>,
|
||||
outpoints: impl IntoIterator<Item = OutPoint>,
|
||||
batch_size: usize,
|
||||
) -> Result<ElectrumUpdate, Error> {
|
||||
let spk_iter = misc_spks
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, spk)| (i as u32, spk));
|
||||
|
||||
let (electrum_update, _) = self.scan(
|
||||
prev_tip,
|
||||
[((), spk_iter)].into(),
|
||||
txids,
|
||||
outpoints,
|
||||
usize::MAX,
|
||||
batch_size,
|
||||
)?;
|
||||
|
||||
Ok(electrum_update)
|
||||
}
|
||||
) -> Result<ElectrumUpdate, Error>;
|
||||
}
|
||||
|
||||
impl ElectrumExt for Client {
|
||||
fn scan<K: Ord + Clone>(
|
||||
fn full_scan<K: Ord + Clone>(
|
||||
&self,
|
||||
prev_tip: CheckPoint,
|
||||
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
|
||||
txids: impl IntoIterator<Item = Txid>,
|
||||
outpoints: impl IntoIterator<Item = OutPoint>,
|
||||
stop_gap: usize,
|
||||
batch_size: usize,
|
||||
) -> Result<(ElectrumUpdate, BTreeMap<K, u32>), Error> {
|
||||
@ -201,9 +191,6 @@ impl ElectrumExt for Client {
|
||||
.collect::<BTreeMap<K, _>>();
|
||||
let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();
|
||||
|
||||
let txids = txids.into_iter().collect::<Vec<_>>();
|
||||
let outpoints = outpoints.into_iter().collect::<Vec<_>>();
|
||||
|
||||
let (electrum_update, keychain_update) = loop {
|
||||
let (tip, _) = construct_update_tip(self, prev_tip.clone())?;
|
||||
let mut relevant_txids = RelevantTxids::default();
|
||||
@ -242,15 +229,6 @@ impl ElectrumExt for Client {
|
||||
}
|
||||
}
|
||||
|
||||
populate_with_txids(self, &cps, &mut relevant_txids, &mut txids.iter().cloned())?;
|
||||
|
||||
let _txs = populate_with_outpoints(
|
||||
self,
|
||||
&cps,
|
||||
&mut relevant_txids,
|
||||
&mut outpoints.iter().cloned(),
|
||||
)?;
|
||||
|
||||
// check for reorgs during scan process
|
||||
let server_blockhash = self.block_header(tip.height() as usize)?.block_hash();
|
||||
if tip.hash() != server_blockhash {
|
||||
@ -284,6 +262,41 @@ impl ElectrumExt for Client {
|
||||
|
||||
Ok((electrum_update, keychain_update))
|
||||
}
|
||||
|
||||
fn sync(
|
||||
&self,
|
||||
prev_tip: CheckPoint,
|
||||
misc_spks: impl IntoIterator<Item = ScriptBuf>,
|
||||
txids: impl IntoIterator<Item = Txid>,
|
||||
outpoints: impl IntoIterator<Item = OutPoint>,
|
||||
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(),
|
||||
usize::MAX,
|
||||
batch_size,
|
||||
)?;
|
||||
|
||||
let (tip, _) = construct_update_tip(self, prev_tip)?;
|
||||
let cps = tip
|
||||
.iter()
|
||||
.take(10)
|
||||
.map(|cp| (cp.height(), cp))
|
||||
.collect::<BTreeMap<u32, CheckPoint>>();
|
||||
|
||||
populate_with_txids(self, &cps, &mut electrum_update.relevant_txids, txids)?;
|
||||
|
||||
let _txs =
|
||||
populate_with_outpoints(self, &cps, &mut electrum_update.relevant_txids, outpoints)?;
|
||||
|
||||
Ok(electrum_update)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [`CheckPoint`] of the latest tip, that connects with `prev_tip`.
|
||||
@ -405,7 +418,7 @@ fn populate_with_outpoints(
|
||||
client: &Client,
|
||||
cps: &BTreeMap<u32, CheckPoint>,
|
||||
relevant_txids: &mut RelevantTxids,
|
||||
outpoints: &mut impl Iterator<Item = OutPoint>,
|
||||
outpoints: impl IntoIterator<Item = OutPoint>,
|
||||
) -> Result<HashMap<Txid, Transaction>, Error> {
|
||||
let mut full_txs = HashMap::new();
|
||||
for outpoint in outpoints {
|
||||
@ -466,7 +479,7 @@ fn populate_with_txids(
|
||||
client: &Client,
|
||||
cps: &BTreeMap<u32, CheckPoint>,
|
||||
relevant_txids: &mut RelevantTxids,
|
||||
txids: &mut impl Iterator<Item = Txid>,
|
||||
txids: impl IntoIterator<Item = Txid>,
|
||||
) -> Result<(), Error> {
|
||||
for txid in txids {
|
||||
let tx = match client.transaction_get(&txid) {
|
||||
|
@ -1,26 +1,26 @@
|
||||
//! This crate is used for updating structures of the [`bdk_chain`] crate with data from electrum.
|
||||
//! This crate is used for updating structures of [`bdk_chain`] with data from an Electrum server.
|
||||
//!
|
||||
//! 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:
|
||||
//! The two primary methods are [`ElectrumExt::sync`] and [`ElectrumExt::full_scan`]. In most cases
|
||||
//! [`ElectrumExt::sync`] is used to sync the transaction histories of scripts that the application
|
||||
//! cares about, for example the scripts for all the receive addresses of a Wallet's keychain that it
|
||||
//! has shown a user. [`ElectrumExt::full_scan`] is meant to be used when importing or restoring a
|
||||
//! keychain where the range of possibly used scripts is not known. In this case it is necessary to
|
||||
//! scan all keychain scripts until a number (the "stop gap") of unused scripts is discovered. For a
|
||||
//! sync or full scan the user receives relevant blockchain data and output updates for
|
||||
//! [`bdk_chain`] including [`RelevantTxids`].
|
||||
//!
|
||||
//! ([`bdk_chain::local_chain::Update`], [`RelevantTxids`], `keychain_update`)
|
||||
//! The [`RelevantTxids`] only includes `txid`s and not full transactions. The caller is responsible
|
||||
//! for obtaining full transactions before applying new data to their [`bdk_chain`]. This can be
|
||||
//! done with these steps:
|
||||
//!
|
||||
//! 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. Use [`RelevantTxids::missing_full_txs`].
|
||||
//!
|
||||
//! 1. Determine which full transactions are missing. The method [`missing_full_txs`] of
|
||||
//! [`RelevantTxids`] can be used.
|
||||
//! 2. Obtaining the full transactions. To do this via electrum use [`ElectrumApi::batch_transaction_get`].
|
||||
//!
|
||||
//! 2. Obtaining the full transactions. To do this via electrum, the method
|
||||
//! [`batch_transaction_get`] can be used.
|
||||
//! Refer to [`example_electrum`] for a complete example.
|
||||
//!
|
||||
//! Refer to [`bdk_electrum_example`] for a complete example.
|
||||
//!
|
||||
//! [`ElectrumClient::scan`]: electrum_client::ElectrumClient::scan
|
||||
//! [`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
|
||||
//! [`ElectrumApi::batch_transaction_get`]: electrum_client::ElectrumApi::batch_transaction_get
|
||||
//! [`example_electrum`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_electrum
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
|
@ -172,14 +172,7 @@ fn main() -> anyhow::Result<()> {
|
||||
};
|
||||
|
||||
client
|
||||
.scan(
|
||||
tip,
|
||||
keychain_spks,
|
||||
core::iter::empty(),
|
||||
core::iter::empty(),
|
||||
stop_gap,
|
||||
scan_options.batch_size,
|
||||
)
|
||||
.full_scan(tip, keychain_spks, stop_gap, scan_options.batch_size)
|
||||
.context("scanning the blockchain")?
|
||||
}
|
||||
ElectrumCommands::Sync {
|
||||
@ -279,7 +272,7 @@ fn main() -> anyhow::Result<()> {
|
||||
drop((graph, chain));
|
||||
|
||||
let electrum_update = client
|
||||
.scan_without_keychain(tip, spks, txids, outpoints, scan_options.batch_size)
|
||||
.sync(tip, spks, txids, outpoints, scan_options.batch_size)
|
||||
.context("scanning the blockchain")?;
|
||||
(electrum_update, BTreeMap::new())
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ fn main() -> Result<(), anyhow::Error> {
|
||||
relevant_txids,
|
||||
},
|
||||
keychain_update,
|
||||
) = client.scan(prev_tip, keychain_spks, None, None, STOP_GAP, BATCH_SIZE)?;
|
||||
) = client.full_scan(prev_tip, keychain_spks, STOP_GAP, BATCH_SIZE)?;
|
||||
|
||||
println!();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user