From b3b273c0d63e257d27462593d9c1ac94d1c75e85 Mon Sep 17 00:00:00 2001 From: ShigrafS Date: Thu, 15 Jan 2026 23:23:27 +0530 Subject: [PATCH 1/4] feat: add one-liner wallet sync API with ElectrumSync Signed-off-by: ShigrafS --- crates/electrum/Cargo.toml | 3 +- crates/electrum/src/electrum_sync.rs | 151 ++++++++++++++++++ crates/electrum/src/lib.rs | 3 + .../src/bin/one_liner_sync.rs | 75 +++++++++ examples/one_liner_sync.rs | 76 +++++++++ 5 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 crates/electrum/src/electrum_sync.rs create mode 100644 examples/example_electrum/src/bin/one_liner_sync.rs create mode 100644 examples/one_liner_sync.rs diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 4c324a8e1..09c3ba12f 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -16,10 +16,11 @@ workspace = true [dependencies] bdk_core = { path = "../core", version = "0.6.1" } electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features = false } +bdk_chain = { path = "../chain", version = "0.23.2" } [dev-dependencies] bdk_testenv = { path = "../testenv" } -bdk_chain = { path = "../chain" } + criterion = { version = "0.7" } [features] diff --git a/crates/electrum/src/electrum_sync.rs b/crates/electrum/src/electrum_sync.rs new file mode 100644 index 000000000..43815d2ff --- /dev/null +++ b/crates/electrum/src/electrum_sync.rs @@ -0,0 +1,151 @@ +use bdk_chain::{ + + collections::BTreeMap, + keychain_txout::KeychainTxOutIndex, + local_chain::CheckPoint, + tx_graph::TxGraph, + +}; +use bdk_core::spk_client::{FullScanRequest, SyncRequest}; +use electrum_client::ElectrumApi; + +use crate::BdkElectrumClient; + +/// Configuration for the sync operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SyncOptions { + /// Whether to perform a fast sync (only revealed addresses) or full scan (lookahead until stop gap). + /// + /// - `true`: Sync only the scripts that are already revealed in the wallet. + /// - `false`: Scan for new scripts until `stop_gap` of empty histories is found. + pub fast: bool, + + /// The number of unused scripts to fetch before stopping (only used if `fast` is `false`). + pub stop_gap: usize, + + /// The number of scripts to query in a single batch. + pub batch_size: usize, + + /// Whether to fetch previous transaction outputs for fee calculation. + pub fetch_prev: bool, +} + +impl Default for SyncOptions { + fn default() -> Self { + Self { + fast: true, + stop_gap: 20, + batch_size: 10, + fetch_prev: false, + } + } +} + +impl SyncOptions { + /// Create options for a fast sync (revealed scripts only). + pub fn fast() -> Self { + Self { + fast: true, + ..Default::default() + } + } + + /// Create options for a full scan (unbounded discovery). + pub fn full_scan() -> Self { + Self { + fast: false, + ..Default::default() + } + } +} + +/// A helper struct to sync a wallet (KeychainTxOutIndex) with an Electrum server. +/// +/// This wrapper holds the [`BdkElectrumClient`] which maintains a cache of headers and transactions +/// to optimize repeated syncs. +pub struct ElectrumSync<'a, K, E> { + wallet: &'a KeychainTxOutIndex, + client: BdkElectrumClient, +} + +impl<'a, K, E> ElectrumSync<'a, K, E> +where + E: ElectrumApi, + K: Ord + Clone + core::fmt::Debug + Send + Sync, +{ + /// Create a new `ElectrumSync` helper. + /// + /// # Arguments + /// + /// * `wallet` - The wallet index to sync. + /// * `client` - The `BdkElectrumClient` to use for network requests. + pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { + Self { wallet, client } + } + + /// Access the underlying `BdkElectrumClient`. + pub fn client(&self) -> &BdkElectrumClient { + &self.client + } + + /// Perform the sync operation based on the provided `options`. + /// + /// Returns a tuple containing: + /// - `Option`: The updated chain tip. + /// - `TxGraph`: The graph of transactions found. + /// - `Option>`: The last active indices for each keychain (only for full scan). + pub fn sync( + &self, + options: SyncOptions, + ) -> Result< + ( + Option, + TxGraph, + Option>, + ), + electrum_client::Error, + > { + if options.fast { + let request = SyncRequest::builder() + .spks_with_indexes( + self.wallet + .revealed_spks(..) + .map(|(k, spk)| (k.1, spk.into())), + ) + .build(); + + // Note: sync() doesn't return last active indices in the same way full_scan does contextually, + // but we can infer or simpler just return None for fast sync. + let response = self + .client + .sync(request, options.batch_size, options.fetch_prev)?; + + Ok(( + response.chain_update, + response.tx_update.into(), + None, + )) + } else { + let mut builder = FullScanRequest::builder(); + + for (keychain, spks) in self.wallet.all_unbounded_spk_iters() { + builder = builder.spks_for_keychain(keychain, spks); + } + + let request = builder.build(); + + let response = self.client.full_scan( + request, + options.stop_gap, + options.batch_size, + options.fetch_prev, + )?; + + Ok(( + response.chain_update, + response.tx_update.into(), + Some(response.last_active_indices), + )) + } + } +} diff --git a/crates/electrum/src/lib.rs b/crates/electrum/src/lib.rs index 9c1d9f452..c413a0f6f 100644 --- a/crates/electrum/src/lib.rs +++ b/crates/electrum/src/lib.rs @@ -22,5 +22,8 @@ mod bdk_electrum_client; pub use bdk_electrum_client::*; +mod electrum_sync; +pub use electrum_sync::*; + pub use bdk_core; pub use electrum_client; diff --git a/examples/example_electrum/src/bin/one_liner_sync.rs b/examples/example_electrum/src/bin/one_liner_sync.rs new file mode 100644 index 000000000..453cbc1d1 --- /dev/null +++ b/examples/example_electrum/src/bin/one_liner_sync.rs @@ -0,0 +1,75 @@ +//! Example of a one-liner wallet sync using ElectrumSync. +//! +//! This example demonstrates how to: +//! 1. Create a wallet (KeychainTxOutIndex). +//! 2. Create an Electrum client. +//! 3. Use `ElectrumSync` for a "one-liner" sync. +//! +//! Note: This example requires an actual Electrum server URL to run successfully. +//! By default it tries to connect to a public testnet server. + +use bdk_chain::{ + bitcoin::{Network, Network::Testnet}, + keychain_txout::KeychainTxOutIndex, // Correct import path +}; +use bdk_electrum::{ + electrum_client::{self, ElectrumApi}, + BdkElectrumClient, ElectrumSync, SyncOptions, +}; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum MyKeychain { + External, + Internal, +} + +fn main() -> Result<(), Box> { + const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet + + // 1. Setup Wallet: KeychainTxOutIndex + let mut wallet_index = KeychainTxOutIndex::::new(20, true); + + // Add descriptors (using some public descriptor for demo purposes) + // Descriptor: tr([73c5da0a/86'/1'/0']tpubDC.../0/*) (External) + // This is just a dummy descriptor for compilation, won't find real funds on testnet unless the xpub is valid/funded + let external_descriptor = "tr([73c5da0a/86'/1'/0']tpubDCDkM3bAi3d7KqW8G9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V-testnet/0/*)"; + // Note: Parsing descriptors requires more boilerplate in real code (miniscript, secp256k1), + // omitted here for brevity if just checking API structure. + // BUT we need it to compile. So let's use a simpler known descriptor if possible, or just mock usage. + + // For the sake of this example being purely about API structure, we will skip actual descriptor parsing + // unless we need to query specifically. In a real app you'd insert descriptors here. + println!("Wallet index initialized."); + + // 2. Setup Electrum Client + let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; + // Wrap it in BdkElectrumClient (preserves cache) + let bdk_client = BdkElectrumClient::new(electrum_client); + + // 3. One-Liner Sync + // We create the helper. + let syncer = ElectrumSync::new(&wallet_index, bdk_client); + + println!("Starting full scan..."); + + // Perform a full scan (discovers scripts) + let result = syncer.sync(SyncOptions::full_scan())?; + + // Ideally we would apply the result to the wallet here. + // wallet_index.apply_changeset(result.2.unwrap()); // Applying last active indices + // wallet_index.apply_update(result.1); // Applying tx graph + + println!("Sync finished!"); + println!("New tip: {:?}", result.0); + println!("Found transactions: {}", result.1.full_txs().count()); + + // 4. Repeated Sync (Fast) + // Suppose we just want to check revealed addresses for new txs (faster). + println!("Starting fast sync..."); + let fast_result = syncer.sync(SyncOptions::fast())?; + + println!("Fast sync finished!"); + + Ok(()) +} diff --git a/examples/one_liner_sync.rs b/examples/one_liner_sync.rs new file mode 100644 index 000000000..94a23063f --- /dev/null +++ b/examples/one_liner_sync.rs @@ -0,0 +1,76 @@ +//! Example of a one-liner wallet sync using ElectrumSync. +//! +//! This example demonstrates how to: +//! 1. Create a wallet (KeychainTxOutIndex). +//! 2. Create an Electrum client. +//! 3. Use `ElectrumSync` for a "one-liner" sync. +//! +//! Note: This example requires an actual Electrum server URL to run successfully. +//! By default it tries to connect to a public testnet server. + +use bdk_chain::{ + bitcoin::{Network, Network::Testnet}, + indexer::KeychainTxOutIndex, +}; +use bdk_electrum::{ + electrum_client::{self, ElectrumApi}, + BdkElectrumClient, ElectrumSync, SyncOptions, +}; +use std::collections::BTreeMap; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum MyKeychain { + External, + Internal, +} + +fn main() -> Result<(), Box> { + const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet + + // 1. Setup Wallet: KeychainTxOutIndex + let mut wallet_index = KeychainTxOutIndex::::new(20, true); + + // Add descriptors (using some public descriptor for demo purposes) + // Descriptor: tr([73c5da0a/86'/1'/0']tpubDC.../0/*) (External) + // This is just a dummy descriptor for compilation, won't find real funds on testnet unless the xpub is valid/funded + let external_descriptor = "tr([73c5da0a/86'/1'/0']tpubDCDkM3bAi3d7KqW8G9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V-testnet/0/*)"; + // Note: Parsing descriptors requires more boilerplate in real code (miniscript, secp256k1), + // omitted here for brevity if just checking API structure. + // BUT we need it to compile. So let's use a simpler known descriptor if possible, or just mock usage. + + // For the sake of this example being purely about API structure, we will skip actual descriptor parsing + // unless we need to query specifically. In a real app you'd insert descriptors here. + println!("Wallet index initialized."); + + // 2. Setup Electrum Client + let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; + // Wrap it in BdkElectrumClient (preserves cache) + let bdk_client = BdkElectrumClient::new(electrum_client); + + // 3. One-Liner Sync + // We create the helper. + let syncer = ElectrumSync::new(&wallet_index, bdk_client); + + println!("Starting full scan..."); + + // Perform a full scan (discovers scripts) + let result = syncer.sync(SyncOptions::full_scan())?; + + // Ideally we would apply the result to the wallet here. + // wallet_index.apply_update(result.1, result.2); // Conceptual, depends on specific Wallet/Index API for application. + // KeychainTxOutIndex doesn't directly take TxGraph updates, usually a `Wallet` struct does. + // But this shows we got the data. + + println!("Sync finished!"); + println!("New tip: {:?}", result.0); + println!("Found transactions: {}", result.1.full_txs().count()); + + // 4. Repeated Sync (Fast) + // Suppose we just want to check revealed addresses for new txs (faster). + println!("Starting fast sync..."); + let fast_result = syncer.sync(SyncOptions::fast())?; + + println!("Fast sync finished!"); + + Ok(()) +} From 7c36ddae71a41e6655b2487ac5ce9875fbb95949 Mon Sep 17 00:00:00 2001 From: ShigrafS Date: Fri, 16 Jan 2026 11:41:53 +0530 Subject: [PATCH 2/4] feat: add one-liner wallet sync API with ElectrumSync and SyncOptions Signed-off-by: ShigrafS --- Cargo.toml | 1 + crates/electrum/Cargo.toml | 8 +- crates/electrum/src/electrum_sync.rs | 151 ------------------ crates/electrum/src/lib.rs | 3 +- .../electrum/tests/test_api_compatibility.rs | 64 ++++++++ examples/example_one_liner/Cargo.toml | 11 ++ examples/example_one_liner/src/main.rs | 151 ++++++++++++++++++ 7 files changed, 234 insertions(+), 155 deletions(-) delete mode 100644 crates/electrum/src/electrum_sync.rs create mode 100644 crates/electrum/tests/test_api_compatibility.rs create mode 100644 examples/example_one_liner/Cargo.toml create mode 100644 examples/example_one_liner/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index d505c1a0a..358556f06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "examples/example_electrum", "examples/example_esplora", "examples/example_bitcoind_rpc_polling", + "examples/example_one_liner", ] [workspace.package] diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 09c3ba12f..6d2a19d8a 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -16,10 +16,14 @@ workspace = true [dependencies] bdk_core = { path = "../core", version = "0.6.1" } electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features = false } -bdk_chain = { path = "../chain", version = "0.23.2" } + [dev-dependencies] -bdk_testenv = { path = "../testenv" } + +bdk_chain = { path = "../chain" } +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" criterion = { version = "0.7" } diff --git a/crates/electrum/src/electrum_sync.rs b/crates/electrum/src/electrum_sync.rs deleted file mode 100644 index 43815d2ff..000000000 --- a/crates/electrum/src/electrum_sync.rs +++ /dev/null @@ -1,151 +0,0 @@ -use bdk_chain::{ - - collections::BTreeMap, - keychain_txout::KeychainTxOutIndex, - local_chain::CheckPoint, - tx_graph::TxGraph, - -}; -use bdk_core::spk_client::{FullScanRequest, SyncRequest}; -use electrum_client::ElectrumApi; - -use crate::BdkElectrumClient; - -/// Configuration for the sync operation. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SyncOptions { - /// Whether to perform a fast sync (only revealed addresses) or full scan (lookahead until stop gap). - /// - /// - `true`: Sync only the scripts that are already revealed in the wallet. - /// - `false`: Scan for new scripts until `stop_gap` of empty histories is found. - pub fast: bool, - - /// The number of unused scripts to fetch before stopping (only used if `fast` is `false`). - pub stop_gap: usize, - - /// The number of scripts to query in a single batch. - pub batch_size: usize, - - /// Whether to fetch previous transaction outputs for fee calculation. - pub fetch_prev: bool, -} - -impl Default for SyncOptions { - fn default() -> Self { - Self { - fast: true, - stop_gap: 20, - batch_size: 10, - fetch_prev: false, - } - } -} - -impl SyncOptions { - /// Create options for a fast sync (revealed scripts only). - pub fn fast() -> Self { - Self { - fast: true, - ..Default::default() - } - } - - /// Create options for a full scan (unbounded discovery). - pub fn full_scan() -> Self { - Self { - fast: false, - ..Default::default() - } - } -} - -/// A helper struct to sync a wallet (KeychainTxOutIndex) with an Electrum server. -/// -/// This wrapper holds the [`BdkElectrumClient`] which maintains a cache of headers and transactions -/// to optimize repeated syncs. -pub struct ElectrumSync<'a, K, E> { - wallet: &'a KeychainTxOutIndex, - client: BdkElectrumClient, -} - -impl<'a, K, E> ElectrumSync<'a, K, E> -where - E: ElectrumApi, - K: Ord + Clone + core::fmt::Debug + Send + Sync, -{ - /// Create a new `ElectrumSync` helper. - /// - /// # Arguments - /// - /// * `wallet` - The wallet index to sync. - /// * `client` - The `BdkElectrumClient` to use for network requests. - pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { - Self { wallet, client } - } - - /// Access the underlying `BdkElectrumClient`. - pub fn client(&self) -> &BdkElectrumClient { - &self.client - } - - /// Perform the sync operation based on the provided `options`. - /// - /// Returns a tuple containing: - /// - `Option`: The updated chain tip. - /// - `TxGraph`: The graph of transactions found. - /// - `Option>`: The last active indices for each keychain (only for full scan). - pub fn sync( - &self, - options: SyncOptions, - ) -> Result< - ( - Option, - TxGraph, - Option>, - ), - electrum_client::Error, - > { - if options.fast { - let request = SyncRequest::builder() - .spks_with_indexes( - self.wallet - .revealed_spks(..) - .map(|(k, spk)| (k.1, spk.into())), - ) - .build(); - - // Note: sync() doesn't return last active indices in the same way full_scan does contextually, - // but we can infer or simpler just return None for fast sync. - let response = self - .client - .sync(request, options.batch_size, options.fetch_prev)?; - - Ok(( - response.chain_update, - response.tx_update.into(), - None, - )) - } else { - let mut builder = FullScanRequest::builder(); - - for (keychain, spks) in self.wallet.all_unbounded_spk_iters() { - builder = builder.spks_for_keychain(keychain, spks); - } - - let request = builder.build(); - - let response = self.client.full_scan( - request, - options.stop_gap, - options.batch_size, - options.fetch_prev, - )?; - - Ok(( - response.chain_update, - response.tx_update.into(), - Some(response.last_active_indices), - )) - } - } -} diff --git a/crates/electrum/src/lib.rs b/crates/electrum/src/lib.rs index c413a0f6f..1bcd5097e 100644 --- a/crates/electrum/src/lib.rs +++ b/crates/electrum/src/lib.rs @@ -22,8 +22,7 @@ mod bdk_electrum_client; pub use bdk_electrum_client::*; -mod electrum_sync; -pub use electrum_sync::*; + pub use bdk_core; pub use electrum_client; diff --git a/crates/electrum/tests/test_api_compatibility.rs b/crates/electrum/tests/test_api_compatibility.rs new file mode 100644 index 000000000..7b1bdab1e --- /dev/null +++ b/crates/electrum/tests/test_api_compatibility.rs @@ -0,0 +1,64 @@ +//! Test ensuring that the API allows manual construction of SyncRequest +//! and execution via BdkElectrumClient. +//! +//! Strategy: Use a real `electrum_client::Client` pointing to a non-existent server. +//! This validates that the types (`SyncRequest`, `BdkElectrumClient`) are compatible +//! and compile together. Runtime failure is expected and asserted. + +use bdk_chain::{ + local_chain::LocalChain, + spk_client::SyncRequest, + keychain_txout::KeychainTxOutIndex, + IndexedTxGraph, +}; +use bdk_electrum::BdkElectrumClient; +use bdk_electrum::electrum_client; +use bdk_electrum::bitcoin::{ + Address, BlockHash, + hashes::Hash, +}; +use std::str::FromStr; + +#[test] +fn test_manual_sync_request_construction_with_dummy_client() { + // 1. Setup Dummy Client + // We use a real client but point to an invalid address. + // This allows us to compile check the wiring without mocking the huge trait. + let dummy_url = "ssl://127.0.0.1:0"; // Invalid port/host + // If creation fails (e.g. invalid URL format), we panic, which is fine (test fails). + // If creation succeeds, we get a client that will fail on IO. + let electrum_client = match electrum_client::Client::new(dummy_url) { + Ok(c) => c, + Err(_) => return, // Could not create client, skips test (or panic?) + // If we can't create it, we can't test wiring. But verify compilation is the main goal. + }; + + let client = BdkElectrumClient::new(electrum_client); + + // 2. Setup Wallet (Local components) + let (mut chain, _) = LocalChain::from_genesis(BlockHash::all_zeros()); + let mut graph = IndexedTxGraph::::new(KeychainTxOutIndex::::default()); + + // 3. Define a script to track + let descriptor_str = "wpkh(022e3e56c52b21c640798e6e5d2633008432a2657e057f5c907a48d844208a0d0a)"; + let descriptor = bdk_chain::miniscript::Descriptor::from_str(descriptor_str).expect("parse"); + + // Insert into keychain + let _ = graph.index.insert_descriptor(0, descriptor); + graph.index.reveal_to_target(0, 5); + + // 4. Construct SyncRequest Manually + // This part validates the API Types compatibility. + let request = SyncRequest::builder() + .chain_tip(chain.tip()) + .spks_with_indexes(graph.index.revealed_spks(..).map(|(k, s)| (k.1, s.into()))) + .build(); + + // 5. Execute Sync + // This should fail with an error (likely IO or ConnectionRefused), but COMPILATION must succeed. + let result = client.sync(request, 10, false); + + // 6. Assertions + // We expect an error. If by miracle it succeeds (no), valid. + assert!(result.is_err(), "Sync should fail due to dummy URL, but it must compile and run"); +} diff --git a/examples/example_one_liner/Cargo.toml b/examples/example_one_liner/Cargo.toml new file mode 100644 index 000000000..33b6aea61 --- /dev/null +++ b/examples/example_one_liner/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "example_one_liner" +version = "0.1.0" +edition = "2021" + +[dependencies] +bdk_chain = { path = "../../crates/chain", features = ["serde"] } +bdk_electrum = { path = "../../crates/electrum" } +bdk_core = { path = "../../crates/core" } +electrum-client = { version = "0.24.0", features = ["proxy"], default-features = false } +serde = { version = "1.0", features = ["derive"] } diff --git a/examples/example_one_liner/src/main.rs b/examples/example_one_liner/src/main.rs new file mode 100644 index 000000000..ed11aa51f --- /dev/null +++ b/examples/example_one_liner/src/main.rs @@ -0,0 +1,151 @@ +use bdk_chain::{ + bitcoin::Network, + keychain_txout::KeychainTxOutIndex, + tx_graph::TxGraph, + collections::BTreeMap, + CheckPoint, +}; +use bdk_core::{ + spk_client::{FullScanRequest, SyncRequest}, + ConfirmationBlockTime, +}; +use bdk_electrum::{ + electrum_client::{self, ElectrumApi}, + BdkElectrumClient, +}; + +// ----------------------------------------------------------------------------- +// ONE-LINER SYNC HELPER (Proposed API Pattern) +// ----------------------------------------------------------------------------- + +/// Configuration for the sync operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SyncOptions { + pub fast: bool, + pub stop_gap: usize, + pub batch_size: usize, + pub fetch_prev: bool, +} + +impl Default for SyncOptions { + fn default() -> Self { + Self { + fast: true, + stop_gap: 20, + batch_size: 10, + fetch_prev: false, + } + } +} + +impl SyncOptions { + pub fn fast() -> Self { + Self { fast: true, ..Default::default() } + } + pub fn full_scan() -> Self { + Self { fast: false, ..Default::default() } + } +} + +pub struct ElectrumSync<'a, K, E> { + wallet: &'a KeychainTxOutIndex, + client: BdkElectrumClient, +} + +impl<'a, K, E> ElectrumSync<'a, K, E> +where + E: ElectrumApi, + K: Ord + Clone + core::fmt::Debug + Send + Sync, +{ + pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { + Self { wallet, client } + } + + pub fn sync( + &self, + options: SyncOptions, + ) -> Result< + ( + Option, + TxGraph, + Option>, + ), + electrum_client::Error, + > { + if options.fast { + let request = SyncRequest::builder() + .spks_with_indexes( + self.wallet + .revealed_spks(..) + .map(|(k, spk)| (k.1, spk.into())), + ) + .build(); + + let response = self + .client + .sync(request, options.batch_size, options.fetch_prev)?; + + Ok(( + response.chain_update, + response.tx_update.into(), + None, + )) + } else { + let mut builder = FullScanRequest::builder(); + + for (keychain, spks) in self.wallet.all_unbounded_spk_iters() { + builder = builder.spks_for_keychain(keychain, spks); + } + + let request = builder.build(); + + let response = self.client.full_scan( + request, + options.stop_gap, + options.batch_size, + options.fetch_prev, + )?; + + Ok(( + response.chain_update, + response.tx_update.into(), + Some(response.last_active_indices), + )) + } + } +} + +// ----------------------------------------------------------------------------- +// EXAMPLE USAGE +// ----------------------------------------------------------------------------- + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum MyKeychain { + External, + Internal, +} + +fn main() -> Result<(), Box> { + const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet + + let mut wallet_index = KeychainTxOutIndex::::new(20, true); + println!("Wallet index initialized."); + + // This descriptor is specific to Testnet. + // In a real example we might parse it, but for now we just initialize index. + + let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; + let bdk_client = BdkElectrumClient::new(electrum_client); + + let syncer = ElectrumSync::new(&wallet_index, bdk_client); + + println!("Starting full scan..."); + let result = syncer.sync(SyncOptions::full_scan())?; + println!("Sync finished! Found {} txs.", result.1.full_txs().count()); + + println!("Starting fast sync..."); + let _ = syncer.sync(SyncOptions::fast())?; + println!("Fast sync finished!"); + + Ok(()) +} From 7764161384388fb7b86870fd28418097cfd7ef64 Mon Sep 17 00:00:00 2001 From: ShigrafS Date: Thu, 5 Feb 2026 17:14:29 +0530 Subject: [PATCH 3/4] refactor(electrum): reduce scope to example-only one-liner sync Reverts library API changes and consolidates one-liner sync logic into a single self-contained example in examples/example_electrum. Changes: - Revert public API additions in bdk_electrum - Remove test_api_compatibility - Remove separate example_one_liner crate - Add ElectrumSync helper to examples/example_electrum/src/bin/one_liner_sync.rs Signed-off-by: ShigrafS --- Cargo.toml | 2 +- crates/electrum/Cargo.toml | 4 +- crates/electrum/src/lib.rs | 3 +- .../electrum/tests/test_api_compatibility.rs | 64 ------- examples/example_electrum/Cargo.toml | 2 + .../src/bin/one_liner_sync.rs | 156 ++++++++++++++---- examples/example_one_liner/Cargo.toml | 11 -- examples/example_one_liner/src/main.rs | 151 ----------------- examples/one_liner_sync.rs | 76 --------- 9 files changed, 130 insertions(+), 339 deletions(-) delete mode 100644 crates/electrum/tests/test_api_compatibility.rs delete mode 100644 examples/example_one_liner/Cargo.toml delete mode 100644 examples/example_one_liner/src/main.rs delete mode 100644 examples/one_liner_sync.rs diff --git a/Cargo.toml b/Cargo.toml index 358556f06..7bbf03820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ "examples/example_electrum", "examples/example_esplora", "examples/example_bitcoind_rpc_polling", - "examples/example_one_liner", + ] [workspace.package] diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 6d2a19d8a..83efa5466 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -21,9 +21,7 @@ electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features [dev-dependencies] bdk_chain = { path = "../chain" } -anyhow = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" + criterion = { version = "0.7" } diff --git a/crates/electrum/src/lib.rs b/crates/electrum/src/lib.rs index 1bcd5097e..563799b69 100644 --- a/crates/electrum/src/lib.rs +++ b/crates/electrum/src/lib.rs @@ -24,5 +24,4 @@ pub use bdk_electrum_client::*; -pub use bdk_core; -pub use electrum_client; + diff --git a/crates/electrum/tests/test_api_compatibility.rs b/crates/electrum/tests/test_api_compatibility.rs deleted file mode 100644 index 7b1bdab1e..000000000 --- a/crates/electrum/tests/test_api_compatibility.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! Test ensuring that the API allows manual construction of SyncRequest -//! and execution via BdkElectrumClient. -//! -//! Strategy: Use a real `electrum_client::Client` pointing to a non-existent server. -//! This validates that the types (`SyncRequest`, `BdkElectrumClient`) are compatible -//! and compile together. Runtime failure is expected and asserted. - -use bdk_chain::{ - local_chain::LocalChain, - spk_client::SyncRequest, - keychain_txout::KeychainTxOutIndex, - IndexedTxGraph, -}; -use bdk_electrum::BdkElectrumClient; -use bdk_electrum::electrum_client; -use bdk_electrum::bitcoin::{ - Address, BlockHash, - hashes::Hash, -}; -use std::str::FromStr; - -#[test] -fn test_manual_sync_request_construction_with_dummy_client() { - // 1. Setup Dummy Client - // We use a real client but point to an invalid address. - // This allows us to compile check the wiring without mocking the huge trait. - let dummy_url = "ssl://127.0.0.1:0"; // Invalid port/host - // If creation fails (e.g. invalid URL format), we panic, which is fine (test fails). - // If creation succeeds, we get a client that will fail on IO. - let electrum_client = match electrum_client::Client::new(dummy_url) { - Ok(c) => c, - Err(_) => return, // Could not create client, skips test (or panic?) - // If we can't create it, we can't test wiring. But verify compilation is the main goal. - }; - - let client = BdkElectrumClient::new(electrum_client); - - // 2. Setup Wallet (Local components) - let (mut chain, _) = LocalChain::from_genesis(BlockHash::all_zeros()); - let mut graph = IndexedTxGraph::::new(KeychainTxOutIndex::::default()); - - // 3. Define a script to track - let descriptor_str = "wpkh(022e3e56c52b21c640798e6e5d2633008432a2657e057f5c907a48d844208a0d0a)"; - let descriptor = bdk_chain::miniscript::Descriptor::from_str(descriptor_str).expect("parse"); - - // Insert into keychain - let _ = graph.index.insert_descriptor(0, descriptor); - graph.index.reveal_to_target(0, 5); - - // 4. Construct SyncRequest Manually - // This part validates the API Types compatibility. - let request = SyncRequest::builder() - .chain_tip(chain.tip()) - .spks_with_indexes(graph.index.revealed_spks(..).map(|(k, s)| (k.1, s.into()))) - .build(); - - // 5. Execute Sync - // This should fail with an error (likely IO or ConnectionRefused), but COMPILATION must succeed. - let result = client.sync(request, 10, false); - - // 6. Assertions - // We expect an error. If by miracle it succeeds (no), valid. - assert!(result.is_err(), "Sync should fail due to dummy URL, but it must compile and run"); -} diff --git a/examples/example_electrum/Cargo.toml b/examples/example_electrum/Cargo.toml index 9dcd54000..4bd492906 100644 --- a/examples/example_electrum/Cargo.toml +++ b/examples/example_electrum/Cargo.toml @@ -8,4 +8,6 @@ edition = "2021" [dependencies] bdk_chain = { path = "../../crates/chain", features = ["serde"] } bdk_electrum = { path = "../../crates/electrum" } +electrum-client = { version = "0.24.0", features = ["proxy"], default-features = false } +bdk_core = { path = "../../crates/core" } example_cli = { path = "../example_cli" } diff --git a/examples/example_electrum/src/bin/one_liner_sync.rs b/examples/example_electrum/src/bin/one_liner_sync.rs index 453cbc1d1..59c97d300 100644 --- a/examples/example_electrum/src/bin/one_liner_sync.rs +++ b/examples/example_electrum/src/bin/one_liner_sync.rs @@ -1,22 +1,126 @@ -//! Example of a one-liner wallet sync using ElectrumSync. -//! -//! This example demonstrates how to: -//! 1. Create a wallet (KeychainTxOutIndex). -//! 2. Create an Electrum client. -//! 3. Use `ElectrumSync` for a "one-liner" sync. -//! -//! Note: This example requires an actual Electrum server URL to run successfully. -//! By default it tries to connect to a public testnet server. - use bdk_chain::{ - bitcoin::{Network, Network::Testnet}, - keychain_txout::KeychainTxOutIndex, // Correct import path + bitcoin::Network, + keychain_txout::KeychainTxOutIndex, + tx_graph::TxGraph, + collections::BTreeMap, + CheckPoint, }; -use bdk_electrum::{ - electrum_client::{self, ElectrumApi}, - BdkElectrumClient, ElectrumSync, SyncOptions, +use bdk_core::{ + spk_client::{FullScanRequest, SyncRequest}, + ConfirmationBlockTime, }; -use std::collections::BTreeMap; +use bdk_electrum::BdkElectrumClient; +use electrum_client::{self, ElectrumApi}; + +// ----------------------------------------------------------------------------- +// ONE-LINER SYNC HELPER (Proposed API Pattern) +// ----------------------------------------------------------------------------- + +// NOTE: This helper is intentionally defined in the example. +// It demonstrates how an application may compose existing BDK APIs. +// This is NOT part of the bdk_electrum public API. + +/// Configuration for the sync operation. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SyncOptions { + pub fast: bool, + pub stop_gap: usize, + pub batch_size: usize, + pub fetch_prev: bool, +} + +impl Default for SyncOptions { + fn default() -> Self { + Self { + fast: true, + stop_gap: 20, + batch_size: 10, + fetch_prev: false, + } + } +} + +impl SyncOptions { + pub fn fast() -> Self { + Self { fast: true, ..Default::default() } + } + + pub fn full_scan() -> Self { + Self { fast: false, ..Default::default() } + } +} + +pub struct ElectrumSync<'a, K, E> { + wallet: &'a KeychainTxOutIndex, + client: BdkElectrumClient, +} + +impl<'a, K, E> ElectrumSync<'a, K, E> +where + E: ElectrumApi, + K: Ord + Clone + core::fmt::Debug + Send + Sync, +{ + pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { + Self { wallet, client } + } + + pub fn sync( + &self, + options: SyncOptions, + ) -> Result< + ( + Option, + TxGraph, + Option>, + ), + electrum_client::Error, + > { + if options.fast { + let request = SyncRequest::builder() + .spks_with_indexes( + self.wallet + .revealed_spks(..) + .map(|(k, spk)| (k.1, spk.into())), + ) + .build(); + + let response = self + .client + .sync(request, options.batch_size, options.fetch_prev)?; + + Ok(( + response.chain_update, + response.tx_update.into(), + None, + )) + } else { + let mut builder = FullScanRequest::builder(); + + for (keychain, spks) in self.wallet.all_unbounded_spk_iters() { + builder = builder.spks_for_keychain(keychain, spks); + } + + let request = builder.build(); + + let response = self.client.full_scan( + request, + options.stop_gap, + options.batch_size, + options.fetch_prev, + )?; + + Ok(( + response.chain_update, + response.tx_update.into(), + Some(response.last_active_indices), + )) + } + } +} + +// ----------------------------------------------------------------------------- +// EXAMPLE USAGE +// ----------------------------------------------------------------------------- #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] enum MyKeychain { @@ -27,21 +131,12 @@ enum MyKeychain { fn main() -> Result<(), Box> { const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet - // 1. Setup Wallet: KeychainTxOutIndex let mut wallet_index = KeychainTxOutIndex::::new(20, true); - - // Add descriptors (using some public descriptor for demo purposes) - // Descriptor: tr([73c5da0a/86'/1'/0']tpubDC.../0/*) (External) - // This is just a dummy descriptor for compilation, won't find real funds on testnet unless the xpub is valid/funded - let external_descriptor = "tr([73c5da0a/86'/1'/0']tpubDCDkM3bAi3d7KqW8G9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V-testnet/0/*)"; - // Note: Parsing descriptors requires more boilerplate in real code (miniscript, secp256k1), - // omitted here for brevity if just checking API structure. - // BUT we need it to compile. So let's use a simpler known descriptor if possible, or just mock usage. - - // For the sake of this example being purely about API structure, we will skip actual descriptor parsing - // unless we need to query specifically. In a real app you'd insert descriptors here. println!("Wallet index initialized."); + // This descriptor is specific to Testnet. + // In a real example we might parse it, but for now we just initialize index. + // 2. Setup Electrum Client let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; // Wrap it in BdkElectrumClient (preserves cache) @@ -57,8 +152,7 @@ fn main() -> Result<(), Box> { let result = syncer.sync(SyncOptions::full_scan())?; // Ideally we would apply the result to the wallet here. - // wallet_index.apply_changeset(result.2.unwrap()); // Applying last active indices - // wallet_index.apply_update(result.1); // Applying tx graph + // wallet_index.apply_update(result.1, result.2); // Conceptual, depends on specific Wallet/Index API for application. println!("Sync finished!"); println!("New tip: {:?}", result.0); @@ -67,7 +161,7 @@ fn main() -> Result<(), Box> { // 4. Repeated Sync (Fast) // Suppose we just want to check revealed addresses for new txs (faster). println!("Starting fast sync..."); - let fast_result = syncer.sync(SyncOptions::fast())?; + let _fast_result = syncer.sync(SyncOptions::fast())?; println!("Fast sync finished!"); diff --git a/examples/example_one_liner/Cargo.toml b/examples/example_one_liner/Cargo.toml deleted file mode 100644 index 33b6aea61..000000000 --- a/examples/example_one_liner/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "example_one_liner" -version = "0.1.0" -edition = "2021" - -[dependencies] -bdk_chain = { path = "../../crates/chain", features = ["serde"] } -bdk_electrum = { path = "../../crates/electrum" } -bdk_core = { path = "../../crates/core" } -electrum-client = { version = "0.24.0", features = ["proxy"], default-features = false } -serde = { version = "1.0", features = ["derive"] } diff --git a/examples/example_one_liner/src/main.rs b/examples/example_one_liner/src/main.rs deleted file mode 100644 index ed11aa51f..000000000 --- a/examples/example_one_liner/src/main.rs +++ /dev/null @@ -1,151 +0,0 @@ -use bdk_chain::{ - bitcoin::Network, - keychain_txout::KeychainTxOutIndex, - tx_graph::TxGraph, - collections::BTreeMap, - CheckPoint, -}; -use bdk_core::{ - spk_client::{FullScanRequest, SyncRequest}, - ConfirmationBlockTime, -}; -use bdk_electrum::{ - electrum_client::{self, ElectrumApi}, - BdkElectrumClient, -}; - -// ----------------------------------------------------------------------------- -// ONE-LINER SYNC HELPER (Proposed API Pattern) -// ----------------------------------------------------------------------------- - -/// Configuration for the sync operation. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SyncOptions { - pub fast: bool, - pub stop_gap: usize, - pub batch_size: usize, - pub fetch_prev: bool, -} - -impl Default for SyncOptions { - fn default() -> Self { - Self { - fast: true, - stop_gap: 20, - batch_size: 10, - fetch_prev: false, - } - } -} - -impl SyncOptions { - pub fn fast() -> Self { - Self { fast: true, ..Default::default() } - } - pub fn full_scan() -> Self { - Self { fast: false, ..Default::default() } - } -} - -pub struct ElectrumSync<'a, K, E> { - wallet: &'a KeychainTxOutIndex, - client: BdkElectrumClient, -} - -impl<'a, K, E> ElectrumSync<'a, K, E> -where - E: ElectrumApi, - K: Ord + Clone + core::fmt::Debug + Send + Sync, -{ - pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { - Self { wallet, client } - } - - pub fn sync( - &self, - options: SyncOptions, - ) -> Result< - ( - Option, - TxGraph, - Option>, - ), - electrum_client::Error, - > { - if options.fast { - let request = SyncRequest::builder() - .spks_with_indexes( - self.wallet - .revealed_spks(..) - .map(|(k, spk)| (k.1, spk.into())), - ) - .build(); - - let response = self - .client - .sync(request, options.batch_size, options.fetch_prev)?; - - Ok(( - response.chain_update, - response.tx_update.into(), - None, - )) - } else { - let mut builder = FullScanRequest::builder(); - - for (keychain, spks) in self.wallet.all_unbounded_spk_iters() { - builder = builder.spks_for_keychain(keychain, spks); - } - - let request = builder.build(); - - let response = self.client.full_scan( - request, - options.stop_gap, - options.batch_size, - options.fetch_prev, - )?; - - Ok(( - response.chain_update, - response.tx_update.into(), - Some(response.last_active_indices), - )) - } - } -} - -// ----------------------------------------------------------------------------- -// EXAMPLE USAGE -// ----------------------------------------------------------------------------- - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -enum MyKeychain { - External, - Internal, -} - -fn main() -> Result<(), Box> { - const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet - - let mut wallet_index = KeychainTxOutIndex::::new(20, true); - println!("Wallet index initialized."); - - // This descriptor is specific to Testnet. - // In a real example we might parse it, but for now we just initialize index. - - let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; - let bdk_client = BdkElectrumClient::new(electrum_client); - - let syncer = ElectrumSync::new(&wallet_index, bdk_client); - - println!("Starting full scan..."); - let result = syncer.sync(SyncOptions::full_scan())?; - println!("Sync finished! Found {} txs.", result.1.full_txs().count()); - - println!("Starting fast sync..."); - let _ = syncer.sync(SyncOptions::fast())?; - println!("Fast sync finished!"); - - Ok(()) -} diff --git a/examples/one_liner_sync.rs b/examples/one_liner_sync.rs deleted file mode 100644 index 94a23063f..000000000 --- a/examples/one_liner_sync.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Example of a one-liner wallet sync using ElectrumSync. -//! -//! This example demonstrates how to: -//! 1. Create a wallet (KeychainTxOutIndex). -//! 2. Create an Electrum client. -//! 3. Use `ElectrumSync` for a "one-liner" sync. -//! -//! Note: This example requires an actual Electrum server URL to run successfully. -//! By default it tries to connect to a public testnet server. - -use bdk_chain::{ - bitcoin::{Network, Network::Testnet}, - indexer::KeychainTxOutIndex, -}; -use bdk_electrum::{ - electrum_client::{self, ElectrumApi}, - BdkElectrumClient, ElectrumSync, SyncOptions, -}; -use std::collections::BTreeMap; - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -enum MyKeychain { - External, - Internal, -} - -fn main() -> Result<(), Box> { - const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002"; // Testnet - - // 1. Setup Wallet: KeychainTxOutIndex - let mut wallet_index = KeychainTxOutIndex::::new(20, true); - - // Add descriptors (using some public descriptor for demo purposes) - // Descriptor: tr([73c5da0a/86'/1'/0']tpubDC.../0/*) (External) - // This is just a dummy descriptor for compilation, won't find real funds on testnet unless the xpub is valid/funded - let external_descriptor = "tr([73c5da0a/86'/1'/0']tpubDCDkM3bAi3d7KqW8G9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V9w8V-testnet/0/*)"; - // Note: Parsing descriptors requires more boilerplate in real code (miniscript, secp256k1), - // omitted here for brevity if just checking API structure. - // BUT we need it to compile. So let's use a simpler known descriptor if possible, or just mock usage. - - // For the sake of this example being purely about API structure, we will skip actual descriptor parsing - // unless we need to query specifically. In a real app you'd insert descriptors here. - println!("Wallet index initialized."); - - // 2. Setup Electrum Client - let electrum_client = electrum_client::Client::new(ELECTRUM_URL)?; - // Wrap it in BdkElectrumClient (preserves cache) - let bdk_client = BdkElectrumClient::new(electrum_client); - - // 3. One-Liner Sync - // We create the helper. - let syncer = ElectrumSync::new(&wallet_index, bdk_client); - - println!("Starting full scan..."); - - // Perform a full scan (discovers scripts) - let result = syncer.sync(SyncOptions::full_scan())?; - - // Ideally we would apply the result to the wallet here. - // wallet_index.apply_update(result.1, result.2); // Conceptual, depends on specific Wallet/Index API for application. - // KeychainTxOutIndex doesn't directly take TxGraph updates, usually a `Wallet` struct does. - // But this shows we got the data. - - println!("Sync finished!"); - println!("New tip: {:?}", result.0); - println!("Found transactions: {}", result.1.full_txs().count()); - - // 4. Repeated Sync (Fast) - // Suppose we just want to check revealed addresses for new txs (faster). - println!("Starting fast sync..."); - let fast_result = syncer.sync(SyncOptions::fast())?; - - println!("Fast sync finished!"); - - Ok(()) -} From c0f916320dcbc687f639d251b1cb396ce286f5b1 Mon Sep 17 00:00:00 2001 From: ShigrafS Date: Thu, 5 Feb 2026 17:21:05 +0530 Subject: [PATCH 4/4] fix(examples): restrict visibility and add comments to one-liner sync example This commit also reverts accidental changes to crates/electrum/src/lib.rs and crates/electrum/Cargo.toml to ensure no library API changes are introduced. Signed-off-by: ShigrafS --- Cargo.toml | 1 - crates/electrum/Cargo.toml | 5 +-- crates/electrum/src/lib.rs | 5 ++- .../src/bin/one_liner_sync.rs | 36 +++++++++++++------ 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7bbf03820..d505c1a0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ "examples/example_electrum", "examples/example_esplora", "examples/example_bitcoind_rpc_polling", - ] [workspace.package] diff --git a/crates/electrum/Cargo.toml b/crates/electrum/Cargo.toml index 83efa5466..4c324a8e1 100644 --- a/crates/electrum/Cargo.toml +++ b/crates/electrum/Cargo.toml @@ -17,12 +17,9 @@ workspace = true bdk_core = { path = "../core", version = "0.6.1" } electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features = false } - [dev-dependencies] - +bdk_testenv = { path = "../testenv" } bdk_chain = { path = "../chain" } - - criterion = { version = "0.7" } [features] diff --git a/crates/electrum/src/lib.rs b/crates/electrum/src/lib.rs index 563799b69..9c1d9f452 100644 --- a/crates/electrum/src/lib.rs +++ b/crates/electrum/src/lib.rs @@ -22,6 +22,5 @@ mod bdk_electrum_client; pub use bdk_electrum_client::*; - - - +pub use bdk_core; +pub use electrum_client; diff --git a/examples/example_electrum/src/bin/one_liner_sync.rs b/examples/example_electrum/src/bin/one_liner_sync.rs index 59c97d300..9654f6f25 100644 --- a/examples/example_electrum/src/bin/one_liner_sync.rs +++ b/examples/example_electrum/src/bin/one_liner_sync.rs @@ -1,5 +1,19 @@ +//! Example of a one-liner wallet sync using ElectrumSync. +//! +//! This example demonstrates how an application can build a "one-liner" Electrum sync +//! by composing existing BDK APIs. +//! +//! No new API is introduced in bdk_electrum. +//! +//! This example demonstrates how to: +//! 1. Create a wallet (KeychainTxOutIndex). +//! 2. Create an Electrum client. +//! 3. Use `ElectrumSync` for a "one-liner" sync. +//! +//! Note: This example requires an actual Electrum server URL to run successfully. +//! By default it tries to connect to a public testnet server. + use bdk_chain::{ - bitcoin::Network, keychain_txout::KeychainTxOutIndex, tx_graph::TxGraph, collections::BTreeMap, @@ -22,11 +36,11 @@ use electrum_client::{self, ElectrumApi}; /// Configuration for the sync operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SyncOptions { - pub fast: bool, - pub stop_gap: usize, - pub batch_size: usize, - pub fetch_prev: bool, +struct SyncOptions { + fast: bool, + stop_gap: usize, + batch_size: usize, + fetch_prev: bool, } impl Default for SyncOptions { @@ -41,16 +55,16 @@ impl Default for SyncOptions { } impl SyncOptions { - pub fn fast() -> Self { + fn fast() -> Self { Self { fast: true, ..Default::default() } } - pub fn full_scan() -> Self { + fn full_scan() -> Self { Self { fast: false, ..Default::default() } } } -pub struct ElectrumSync<'a, K, E> { +struct ElectrumSync<'a, K, E> { wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient, } @@ -60,11 +74,11 @@ where E: ElectrumApi, K: Ord + Clone + core::fmt::Debug + Send + Sync, { - pub fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { + fn new(wallet: &'a KeychainTxOutIndex, client: BdkElectrumClient) -> Self { Self { wallet, client } } - pub fn sync( + fn sync( &self, options: SyncOptions, ) -> Result<