diff --git a/crates/electrum/src/electrum_ext.rs b/crates/electrum/src/electrum_ext.rs index 4c6786557c..806cabeb2d 100644 --- a/crates/electrum/src/electrum_ext.rs +++ b/crates/electrum/src/electrum_ext.rs @@ -29,7 +29,7 @@ pub trait ElectrumExt { request: FullScanRequest, stop_gap: usize, batch_size: usize, - ) -> Result, Error>; + ) -> Result, 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. @@ -44,11 +44,7 @@ pub trait ElectrumExt { /// may include scripts that have been used, use [`full_scan`] with the keychain. /// /// [`full_scan`]: ElectrumExt::full_scan - fn sync( - &self, - request: SyncRequest, - batch_size: usize, - ) -> Result, Error>; + fn sync(&self, request: SyncRequest, batch_size: usize) -> Result; } impl ElectrumExt for E { @@ -57,7 +53,7 @@ impl ElectrumExt for E { mut request: FullScanRequest, stop_gap: usize, batch_size: usize, - ) -> Result, Error> { + ) -> Result, 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 @@ -134,20 +130,18 @@ impl ElectrumExt for E { }; }; - Ok(update) + Ok(ElectrumFullScanResult(update)) } - fn sync( - &self, - request: SyncRequest, - batch_size: usize, - ) -> Result, Error> { + fn sync(&self, request: SyncRequest, batch_size: usize) -> Result { 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 mut full_scan_res = self.full_scan(full_scan_req, usize::MAX, batch_size)?; + let mut full_scan_res = self + .full_scan(full_scan_req, usize::MAX, batch_size)? + .with_confirmation_height_anchor(); let (tip, _) = construct_update_tip(self, request.chain_tip)?; let cps = tip @@ -171,53 +165,64 @@ impl ElectrumExt for E { request.outpoints, )?; - Ok(SyncResult { + Ok(ElectrumSyncResult(SyncResult { chain_update: full_scan_res.chain_update, graph_update: full_scan_res.graph_update, - }) + })) } } -/// Trait that extends [`SyncResult`] and [`FullScanResult`] functionality. +/// The result of [`ElectrumExt::full_scan`]. /// -/// 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; -} - -impl ElectrumResultExt for FullScanResult { - type NewResult = FullScanResult; +/// This can be transformed into a [`FullScanResult`] with either [`ConfirmationHeightAnchor`] or +/// [`ConfirmationTimeHeightAnchor`] anchor types. +pub struct ElectrumFullScanResult(FullScanResult); + +impl ElectrumFullScanResult { + /// Return [`FullScanResult`] with [`ConfirmationHeightAnchor`]. + pub fn with_confirmation_height_anchor(self) -> FullScanResult { + self.0 + } - fn try_into_confirmation_time_result( + /// Return [`FullScanResult`] with [`ConfirmationTimeHeightAnchor`]. + /// + /// This requires additional calls to the Electrum server. + pub fn with_confirmation_time_height_anchor( self, client: &impl ElectrumApi, - ) -> Result { - Ok(FullScanResult:: { - graph_update: try_into_confirmation_time_result(self.graph_update, client)?, - chain_update: self.chain_update, - last_active_indices: self.last_active_indices, + ) -> Result, Error> { + let res = self.0; + Ok(FullScanResult { + graph_update: try_into_confirmation_time_result(res.graph_update, client)?, + chain_update: res.chain_update, + last_active_indices: res.last_active_indices, }) } } -impl ElectrumResultExt for SyncResult { - type NewResult = SyncResult; +/// The result of [`ElectrumExt::sync`]. +/// +/// This can be transformed into a [`SyncResult`] with either [`ConfirmationHeightAnchor`] or +/// [`ConfirmationTimeHeightAnchor`] anchor types. +pub struct ElectrumSyncResult(SyncResult); + +impl ElectrumSyncResult { + /// Return [`SyncResult`] with [`ConfirmationHeightAnchor`]. + pub fn with_confirmation_height_anchor(self) -> SyncResult { + self.0 + } - fn try_into_confirmation_time_result( + /// Return [`SyncResult`] with [`ConfirmationTimeHeightAnchor`]. + /// + /// This requires additional calls to the Electrum server. + pub fn with_confirmation_time_height_anchor( self, client: &impl ElectrumApi, - ) -> Result { + ) -> Result, Error> { + let res = self.0; Ok(SyncResult { - graph_update: try_into_confirmation_time_result(self.graph_update, client)?, - chain_update: self.chain_update, + graph_update: try_into_confirmation_time_result(res.graph_update, client)?, + chain_update: res.chain_update, }) } } diff --git a/crates/electrum/tests/test_electrum.rs b/crates/electrum/tests/test_electrum.rs index aa4b87933c..9905ab9cc2 100644 --- a/crates/electrum/tests/test_electrum.rs +++ b/crates/electrum/tests/test_electrum.rs @@ -5,7 +5,7 @@ use bdk_chain::{ spk_client::SyncRequest, ConfirmationTimeHeightAnchor, IndexedTxGraph, SpkTxOutIndex, }; -use bdk_electrum::{ElectrumExt, ElectrumResultExt}; +use bdk_electrum::ElectrumExt; use bdk_testenv::{anyhow, anyhow::Result, bitcoincore_rpc::RpcApi, TestEnv}; fn get_balance( @@ -67,7 +67,7 @@ fn scan_detects_confirmed_tx() -> Result<()> { .chain_spks(core::iter::once(spk_to_track)), 5, )? - .try_into_confirmation_time_result(&client)?; + .with_confirmation_time_height_anchor(&client)?; let _ = recv_chain .apply_update(update.chain_update) @@ -133,7 +133,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> Result<()> { SyncRequest::from_chain_tip(recv_chain.tip()).chain_spks([spk_to_track.clone()]), 5, )? - .try_into_confirmation_time_result(&client)?; + .with_confirmation_time_height_anchor(&client)?; let _ = recv_chain .apply_update(update.chain_update) @@ -163,7 +163,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> Result<()> { SyncRequest::from_chain_tip(recv_chain.tip()).chain_spks([spk_to_track.clone()]), 5, )? - .try_into_confirmation_time_result(&client)?; + .with_confirmation_time_height_anchor(&client)?; let _ = recv_chain .apply_update(update.chain_update) diff --git a/example-crates/example_electrum/src/main.rs b/example-crates/example_electrum/src/main.rs index 664d84d6a4..a58750c42d 100644 --- a/example-crates/example_electrum/src/main.rs +++ b/example-crates/example_electrum/src/main.rs @@ -183,7 +183,8 @@ fn main() -> anyhow::Result<()> { let res = client .full_scan::<_>(request, stop_gap, scan_options.batch_size) - .context("scanning the blockchain")?; + .context("scanning the blockchain")? + .with_confirmation_height_anchor(); ( res.chain_update, res.graph_update, @@ -303,7 +304,8 @@ fn main() -> anyhow::Result<()> { let res = client .sync(request, scan_options.batch_size) - .context("scanning the blockchain")?; + .context("scanning the blockchain")? + .with_confirmation_height_anchor(); // drop lock on graph and chain drop((graph, chain)); diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs index e1fe009847..a9b194ce80 100644 --- a/example-crates/wallet_electrum/src/main.rs +++ b/example-crates/wallet_electrum/src/main.rs @@ -10,7 +10,6 @@ use bdk::bitcoin::{Address, Amount}; use bdk::chain::collections::HashSet; use bdk::{bitcoin::Network, Wallet}; use bdk::{KeychainKind, SignOptions}; -use bdk_electrum::ElectrumResultExt; use bdk_electrum::{ electrum_client::{self, ElectrumApi}, ElectrumExt, @@ -55,7 +54,7 @@ fn main() -> Result<(), anyhow::Error> { let mut update = client .full_scan(request, STOP_GAP, BATCH_SIZE)? - .try_into_confirmation_time_result(&client)?; + .with_confirmation_time_height_anchor(&client)?; let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(); let _ = update.graph_update.update_last_seen_unconfirmed(now);