Skip to content

Commit dfde574

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 dfde574

File tree

7 files changed

+219
-155
lines changed

7 files changed

+219
-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: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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(wallet, options.stop_gap, options.batch_size, options.fetch_prev)
104+
}
105+
}
106+
107+
/// One-liner fast sync (only checks revealed addresses)
108+
pub fn fast_sync<K>(
109+
&self,
110+
wallet: &KeychainTxOutIndex<K>,
111+
batch_size: usize,
112+
fetch_prev: bool,
113+
) -> Result<ElectrumSyncResult<K>, electrum_client::Error>
114+
where
115+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
116+
{
117+
let request = Self::build_sync_request(wallet);
118+
let response = self.client.sync(request, batch_size, fetch_prev)?;
119+
120+
Ok((
121+
response.chain_update,
122+
response.tx_update.into(),
123+
None,
124+
))
125+
}
126+
127+
/// One-liner full scan (scans until stop gap is reached)
128+
pub fn full_scan<K>(
129+
&self,
130+
wallet: &KeychainTxOutIndex<K>,
131+
stop_gap: usize,
132+
batch_size: usize,
133+
fetch_prev: bool,
134+
) -> Result<ElectrumSyncResult<K>, electrum_client::Error>
135+
where
136+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
137+
{
138+
let request = Self::build_full_scan_request(wallet);
139+
let response = self.client.full_scan(
140+
request,
141+
stop_gap,
142+
batch_size,
143+
fetch_prev,
144+
)?;
145+
146+
Ok((
147+
response.chain_update,
148+
response.tx_update.into(),
149+
Some(response.last_active_indices),
150+
))
151+
}
152+
153+
/// Get a reference to the underlying client for advanced operations
154+
pub fn client(&self) -> &BdkElectrumClient<Client> {
155+
&self.client
156+
}
157+
158+
/// Build a sync request based on revealed addresses.
159+
fn build_sync_request<K>(wallet: &KeychainTxOutIndex<K>) -> SyncRequest<(K, u32)>
160+
where
161+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
162+
{
163+
let mut builder = SyncRequest::builder();
164+
165+
for keychain_id in wallet.keychains().map(|(k, _)| k) {
166+
for (index, spk) in wallet.revealed_keychain_spks(keychain_id.clone()) {
167+
builder = builder.spks_with_indexes([((keychain_id.clone(), index), spk)]);
168+
}
169+
}
170+
171+
builder.build()
172+
}
173+
174+
/// Build a full scan request using unbounded SPK iterators.
175+
fn build_full_scan_request<K>(wallet: &KeychainTxOutIndex<K>) -> FullScanRequest<K>
176+
where
177+
K: Ord + Clone + Send + Sync + std::fmt::Debug + 'static,
178+
{
179+
let mut builder = FullScanRequest::builder();
180+
181+
// Add unbounded script iterators for each keychain
182+
for (keychain_id, spk_iter) in wallet.all_unbounded_spk_iters() {
183+
builder = builder.spks_for_keychain(keychain_id, spk_iter);
184+
}
185+
186+
builder.build()
187+
}
188+
}

0 commit comments

Comments
 (0)