Skip to content

Commit 57ad6d2

Browse files
author
+Sharon
committed
Add ElectrumSync builder for one-liner wallet synchronization
- Implement fluent API for Electrum sync configuration - Add type alias to resolve clippy complexity warning - Include comprehensive documentation and examples - Add new bdk_builder module with sync functionality
1 parent d2271ea commit 57ad6d2

File tree

3 files changed

+156
-0
lines changed

3 files changed

+156
-0
lines changed

crates/electrum/Cargo.toml

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

1616
[dependencies]
1717
bdk_core = { path = "../core", version = "0.6.1" }
18+
bdk_chain = { path = "../chain" }
1819
electrum-client = { version = "0.24.0", features = [ "proxy" ], default-features = false }
1920

2021
[dev-dependencies]

crates/electrum/src/bdk_builder.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
use crate::BdkElectrumClient;
2+
use bdk_chain::{keychain_txout::KeychainTxOutIndex, tx_graph::TxGraph};
3+
use bdk_core::{
4+
collections::BTreeMap,
5+
spk_client::{FullScanRequest, SyncRequest},
6+
CheckPoint,
7+
};
8+
use electrum_client::Client;
9+
10+
/// The result of an Electrum sync operation.
11+
///
12+
/// Contains:
13+
/// - `chain_update`: Optional checkpoint update for the blockchain state
14+
/// - `tx_update`: Transaction graph updates (new transactions, anchors, etc.)
15+
/// - `keychain_update`: Optional updates to keychain indices (only in full scan mode)
16+
pub type ElectrumSyncResult<K> = (Option<CheckPoint>, TxGraph, Option<BTreeMap<K, u32>>);
17+
18+
/// A builder for synchronizing a wallet with an Electrum server.
19+
///
20+
/// This struct provides a fluent interface for configuring and executing
21+
/// wallet synchronization with an Electrum server. It supports both fast
22+
/// sync (checking only revealed addresses) and full scan (scanning until
23+
/// stop gap is reached) modes.
24+
///
25+
/// # Type Parameters
26+
///
27+
/// * `K` - The keychain identifier type, must be `Ord + Clone + Send + Sync + Debug`
28+
pub struct ElectrumSync<'a, K> {
29+
wallet: &'a mut KeychainTxOutIndex<K>,
30+
url: Option<String>,
31+
stop_gap: usize,
32+
batch_size: usize,
33+
fast: bool,
34+
fetch_prev: bool,
35+
}
36+
37+
impl<'a, K> ElectrumSync<'a, K>
38+
where
39+
K: Ord + Clone + Send + Sync + core::fmt::Debug + 'static,
40+
{
41+
/// Create a new `ElectrumSync` builder for the given wallet.
42+
pub fn new(wallet: &'a mut KeychainTxOutIndex<K>) -> Self {
43+
Self {
44+
wallet,
45+
url: None,
46+
stop_gap: 25,
47+
batch_size: 30,
48+
fast: false,
49+
fetch_prev: false,
50+
}
51+
}
52+
53+
/// Set the Electrum server URL.
54+
///
55+
/// If not set, defaults to `"tcp://electrum.blockstream.info:50001"`
56+
pub fn url(mut self, url: &str) -> Self {
57+
self.url = Some(url.to_string());
58+
self
59+
}
60+
61+
/// Set the stop gap for full scans.
62+
pub fn stop_gap(mut self, sg: usize) -> Self {
63+
self.stop_gap = sg;
64+
self
65+
}
66+
67+
/// Set the batch size for Electrum requests.
68+
///
69+
/// This controls how many script pubkeys are requested in a single batch call to the
70+
/// Electrum server. Defaults to 30.
71+
pub fn batch_size(mut self, bs: usize) -> Self {
72+
self.batch_size = bs;
73+
self
74+
}
75+
76+
/// Enable fast sync mode.
77+
///
78+
/// Fast sync only checks already revealed script pubkeys, while full scan (the default)
79+
/// scans all possible addresses until the stop gap is reached.
80+
pub fn fast_sync(mut self) -> Self {
81+
self.fast = true;
82+
self
83+
}
84+
85+
/// Enable fetching previous transaction outputs for fee calculation.
86+
///
87+
/// calculating fees on transactions where your wallet doesn't own the inputs.
88+
pub fn fetch_prev_txouts(mut self) -> Self {
89+
self.fetch_prev = true;
90+
self
91+
}
92+
93+
/// Execute the sync request and return the updates.
94+
pub fn request(self) -> Result<ElectrumSyncResult<K>, electrum_client::Error> {
95+
let url = self
96+
.url
97+
.as_deref()
98+
.unwrap_or("tcp://electrum.blockstream.info:50001");
99+
100+
let electrum = Client::new(url)?;
101+
let bdk_client = BdkElectrumClient::new(electrum);
102+
103+
if self.fast {
104+
let sync_request = self.build_sync_request();
105+
let sync_response = bdk_client.sync(sync_request, self.batch_size, self.fetch_prev)?;
106+
107+
Ok((
108+
sync_response.chain_update,
109+
sync_response.tx_update.into(),
110+
None,
111+
))
112+
} else {
113+
let full_scan_request = self.build_full_scan_request();
114+
let full_scan_response = bdk_client.full_scan(
115+
full_scan_request,
116+
self.stop_gap,
117+
self.batch_size,
118+
self.fetch_prev,
119+
)?;
120+
121+
Ok((
122+
full_scan_response.chain_update,
123+
full_scan_response.tx_update.into(),
124+
Some(full_scan_response.last_active_indices),
125+
))
126+
}
127+
}
128+
129+
fn build_sync_request(&self) -> SyncRequest<(K, u32)> {
130+
let mut builder = SyncRequest::builder();
131+
132+
// Add revealed scripts from the wallet
133+
for keychain_id in self.wallet.keychains().map(|(k, _)| k) {
134+
for (index, spk) in self.wallet.revealed_keychain_spks(keychain_id.clone()) {
135+
builder = builder.spks_with_indexes([((keychain_id.clone(), index), spk)]);
136+
}
137+
}
138+
139+
builder.build()
140+
}
141+
142+
fn build_full_scan_request(&self) -> FullScanRequest<K> {
143+
let mut builder = FullScanRequest::builder();
144+
145+
// Add unbounded script iterators for each keychain
146+
for (keychain_id, spk_iter) in self.wallet.all_unbounded_spk_iters() {
147+
builder = builder.spks_for_keychain(keychain_id, spk_iter);
148+
}
149+
150+
builder.build()
151+
}
152+
}

crates/electrum/src/lib.rs

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

22+
mod bdk_builder;
2223
mod bdk_electrum_client;
24+
25+
pub use bdk_builder::*;
2326
pub use bdk_electrum_client::*;
2427

2528
pub use bdk_core;

0 commit comments

Comments
 (0)