Skip to content

Commit 5f4c4aa

Browse files
author
+Sharon
committed
Add example_electrum_sync module with simplified Electrum sync API
- Added new `example_electrum_sync` crate module (lib.rs) implementing: - `ElectrumSyncManager` for long-lived Electrum connections - `SyncOptions` with builder-style convenience methods - One-liner sync API supporting fast sync and full scan - Internal helpers for building SyncRequest and FullScanRequest - Clean, documented, cache-preserving sync flow aligned with BDK rules - Updated `main.rs` to use the new manager: - Demonstrates fast sync, full scan, custom stop_gap, custom batch_size - Removed outdated `bdk-builder.rs` from the Electrum folder: - Functionality fully replaced by the new sync manager - Simplifies API surface and avoids conflicting builders
1 parent 67865cd commit 5f4c4aa

File tree

7 files changed

+217
-155
lines changed

7 files changed

+217
-155
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ members = [
1010
"crates/testenv",
1111
"examples/example_cli",
1212
"examples/example_electrum",
13+
"examples/example_electrum_sync",
1314
"examples/example_esplora",
1415
"examples/example_bitcoind_rpc_polling",
1516
]

crates/electrum/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ workspace = true
1515

1616
[dependencies]
1717
bdk_core = { path = "../core", version = "0.6.1" }
18-
bdk_chain = { path = "../chain" }
1918
electrum-client = { version = "0.24.0", features = [ "proxy" ] }
2019

2120
[dev-dependencies]

crates/electrum/src/bdk_builder.rs

Lines changed: 0 additions & 151 deletions
This file was deleted.

crates/electrum/src/lib.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@
1919
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
2020
#![warn(missing_docs)]
2121

22-
mod bdk_builder;
2322
mod bdk_electrum_client;
24-
25-
pub use bdk_builder::*;
2623
pub use bdk_electrum_client::*;
2724

2825
pub use bdk_core;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "example_electrum_sync"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
bdk_core = { path = "../../crates/core" }
8+
bdk_chain = { path = "../../crates/chain" }
9+
bdk_electrum = { path = "../../crates/electrum" }
10+
electrum-client = "0.24.0"
11+
anyhow = "1.0"
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//! Simple one-liner Electrum sync helper
2+
//!
3+
//! This provides a clean API for synchronizing wallets with Electrum servers
4+
//! while preserving cache and respecting BDK's architectural boundaries.
5+
6+
use bdk_chain::{keychain_txout::KeychainTxOutIndex, tx_graph::TxGraph};
7+
use bdk_core::{
8+
collections::BTreeMap,
9+
spk_client::{FullScanRequest, SyncRequest},
10+
CheckPoint,
11+
};
12+
use bdk_electrum::BdkElectrumClient;
13+
use electrum_client::Client;
14+
15+
/// Result type for Electrum synchronization
16+
pub type ElectrumSyncResult<K> = (Option<CheckPoint>, TxGraph, Option<BTreeMap<K, u32>>);
17+
18+
/// Simple configuration for Electrum synchronization
19+
#[derive(Debug, Clone, Copy)]
20+
pub struct SyncOptions {
21+
pub fast: bool,
22+
pub stop_gap: usize,
23+
pub batch_size: usize,
24+
pub fetch_prev: bool,
25+
}
26+
27+
impl Default for SyncOptions {
28+
fn default() -> Self {
29+
Self {
30+
fast: false,
31+
stop_gap: 25,
32+
batch_size: 30,
33+
fetch_prev: false,
34+
}
35+
}
36+
}
37+
38+
impl SyncOptions {
39+
/// Create options for fast sync
40+
pub fn fast_sync() -> Self {
41+
Self {
42+
fast: true,
43+
..Default::default()
44+
}
45+
}
46+
47+
/// Create options for full scan
48+
pub fn full_scan() -> Self {
49+
Self::default()
50+
}
51+
52+
pub fn with_stop_gap(mut self, stop_gap: usize) -> Self {
53+
self.stop_gap = stop_gap;
54+
self
55+
}
56+
57+
pub fn with_batch_size(mut self, batch_size: usize) -> Self {
58+
self.batch_size = batch_size;
59+
self
60+
}
61+
62+
pub fn with_fetch_prev(mut self, fetch_prev: bool) -> Self {
63+
self.fetch_prev = fetch_prev;
64+
self
65+
}
66+
}
67+
68+
/// Long-lived Electrum sync manager that preserves cache across operations
69+
///
70+
/// This struct holds a persistent connection to an Electrum server, maintaining
71+
/// transaction and header caches between sync operations for better performance.
72+
pub struct ElectrumSyncManager {
73+
client: BdkElectrumClient<Client>,
74+
}
75+
76+
impl ElectrumSyncManager {
77+
/// Create a new sync manager with the given Electrum server URL
78+
pub fn new(url: &str) -> Result<Self, electrum_client::Error> {
79+
let client = Client::new(url)?;
80+
Ok(Self {
81+
client: BdkElectrumClient::new(client),
82+
})
83+
}
84+
85+
/// One-liner synchronization - the main convenience method
86+
///
87+
/// # Example
88+
/// ```text
89+
/// let manager = ElectrumSyncManager::new("tcp://electrum.example.com:50001")?;
90+
/// let result = manager.sync(&wallet, SyncOptions::full_scan())?;
91+
/// ```
92+
pub fn sync<K>(
93+
&self,
94+
wallet: &KeychainTxOutIndex<K>,
95+
options: SyncOptions,
96+
) -> Result<ElectrumSyncResult<K>, electrum_client::Error>
97+
where
98+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
99+
{
100+
if options.fast {
101+
self.fast_sync(wallet, options.batch_size, options.fetch_prev)
102+
} else {
103+
self.full_scan(
104+
wallet,
105+
options.stop_gap,
106+
options.batch_size,
107+
options.fetch_prev,
108+
)
109+
}
110+
}
111+
112+
/// One-liner fast sync (only checks revealed addresses)
113+
pub fn fast_sync<K>(
114+
&self,
115+
wallet: &KeychainTxOutIndex<K>,
116+
batch_size: usize,
117+
fetch_prev: bool,
118+
) -> Result<ElectrumSyncResult<K>, electrum_client::Error>
119+
where
120+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
121+
{
122+
let request = Self::build_sync_request(wallet);
123+
let response = self.client.sync(request, batch_size, fetch_prev)?;
124+
125+
Ok((response.chain_update, response.tx_update.into(), None))
126+
}
127+
128+
/// One-liner full scan (scans until stop gap is reached)
129+
pub fn full_scan<K>(
130+
&self,
131+
wallet: &KeychainTxOutIndex<K>,
132+
stop_gap: usize,
133+
batch_size: usize,
134+
fetch_prev: bool,
135+
) -> Result<ElectrumSyncResult<K>, electrum_client::Error>
136+
where
137+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
138+
{
139+
let request = Self::build_full_scan_request(wallet);
140+
let response = self
141+
.client
142+
.full_scan(request, stop_gap, batch_size, fetch_prev)?;
143+
144+
Ok((
145+
response.chain_update,
146+
response.tx_update.into(),
147+
Some(response.last_active_indices),
148+
))
149+
}
150+
151+
/// Get a reference to the underlying client for advanced operations
152+
pub fn client(&self) -> &BdkElectrumClient<Client> {
153+
&self.client
154+
}
155+
156+
/// Build a sync request based on revealed addresses.
157+
fn build_sync_request<K>(wallet: &KeychainTxOutIndex<K>) -> SyncRequest<(K, u32)>
158+
where
159+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
160+
{
161+
let mut builder = SyncRequest::builder();
162+
163+
for keychain_id in wallet.keychains().map(|(k, _)| k) {
164+
for (index, spk) in wallet.revealed_keychain_spks(keychain_id.clone()) {
165+
builder = builder.spks_with_indexes([((keychain_id.clone(), index), spk)]);
166+
}
167+
}
168+
169+
builder.build()
170+
}
171+
172+
/// Build a full scan request using unbounded SPK iterators.
173+
fn build_full_scan_request<K>(wallet: &KeychainTxOutIndex<K>) -> FullScanRequest<K>
174+
where
175+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
176+
{
177+
let mut builder = FullScanRequest::builder();
178+
179+
// Add unbounded script iterators for each keychain
180+
for (keychain_id, spk_iter) in wallet.all_unbounded_spk_iters() {
181+
builder = builder.spks_for_keychain(keychain_id, spk_iter);
182+
}
183+
184+
builder.build()
185+
}
186+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use bdk_chain::keychain_txout::KeychainTxOutIndex;
2+
use example_electrum_sync::{ElectrumSyncManager, SyncOptions};
3+
4+
fn main() -> Result<(), Box<dyn std::error::Error>> {
5+
println!("Testing ElectrumSyncManager...");
6+
let wallet: KeychainTxOutIndex<()> = KeychainTxOutIndex::default();
7+
let manager = ElectrumSyncManager::new("ssl://electrum.blockstream.info:50001")?;
8+
9+
// Fast sync
10+
let _ = manager.sync(&wallet, SyncOptions::fast_sync())?;
11+
12+
// Full scan with custom stop gap
13+
let _ = manager.sync(&wallet, SyncOptions::full_scan().with_stop_gap(50))?;
14+
15+
// Full scan with custom batch size
16+
let _ = manager.sync(&wallet, SyncOptions::full_scan().with_batch_size(100))?;
17+
18+
Ok(())
19+
}

0 commit comments

Comments
 (0)