Skip to content

Commit 67ee040

Browse files
committed
docs(wallet): add sync to bdk_wallet examples
- add partial syncing to electrum, esplora_async and esplora_blocking examples - add applying evicted txns to wallet in electrum, esplora_async and esplora_blocking examples
1 parent c39ce79 commit 67ee040

File tree

3 files changed

+225
-10
lines changed
  • examples
    • example_wallet_electrum/src
    • example_wallet_esplora_async/src
    • example_wallet_esplora_blocking/src

3 files changed

+225
-10
lines changed

examples/example_wallet_electrum/src/main.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bdk_wallet::bitcoin::Txid;
12
use bdk_wallet::file_store::Store;
23
use bdk_wallet::Wallet;
34
use std::io::Write;
@@ -44,7 +45,7 @@ fn main() -> Result<(), anyhow::Error> {
4445
let balance = wallet.balance();
4546
println!("Wallet balance before syncing: {}", balance.total());
4647

47-
print!("Syncing...");
48+
println!("=== Performing Full Sync ===");
4849
let client = BdkElectrumClient::new(electrum_client::Client::new(ELECTRUM_URL)?);
4950

5051
// Populate the electrum client's transaction cache so it doesn't redownload transaction we
@@ -71,7 +72,12 @@ fn main() -> Result<(), anyhow::Error> {
7172
wallet.persist(&mut db)?;
7273

7374
let balance = wallet.balance();
74-
println!("Wallet balance after syncing: {}", balance.total());
75+
println!("Wallet balance after full sync: {}", balance.total());
76+
println!(
77+
"Wallet has {} transactions and {} utxos after full sync",
78+
wallet.transactions().count(),
79+
wallet.list_unspent().count()
80+
);
7581

7682
if balance.total() < SEND_AMOUNT {
7783
println!("Please send at least {SEND_AMOUNT} to the receiving address");
@@ -89,5 +95,73 @@ fn main() -> Result<(), anyhow::Error> {
8995
client.transaction_broadcast(&tx)?;
9096
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
9197

98+
let unconfirmed_txids: HashSet<Txid> = wallet
99+
.transactions()
100+
.filter(|tx| tx.chain_position.is_unconfirmed())
101+
.map(|tx| tx.tx_node.txid)
102+
.collect();
103+
104+
client.populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx));
105+
106+
println!("\n=== Performing Partial Sync ===\n");
107+
print!("SCANNING: ");
108+
let mut last_printed = 0;
109+
let sync_request = wallet
110+
.start_sync_with_revealed_spks()
111+
.inspect(move |_, sync_progress| {
112+
let progress_percent =
113+
(100 * sync_progress.consumed()) as f32 / sync_progress.total() as f32;
114+
let progress_percent = progress_percent.round() as u32;
115+
if progress_percent.is_multiple_of(5) && progress_percent > last_printed {
116+
print!("{}% ", progress_percent);
117+
std::io::stdout().flush().expect("must flush");
118+
last_printed = progress_percent;
119+
}
120+
});
121+
client.populate_tx_cache(wallet.tx_graph().full_txs().map(|tx_node| tx_node.tx));
122+
let sync_update = client.sync(sync_request, BATCH_SIZE, false)?;
123+
println!();
124+
125+
let mut evicted_txs = Vec::new();
126+
for txid in unconfirmed_txids {
127+
let tx_node = wallet
128+
.tx_graph()
129+
.full_txs()
130+
.find(|full_tx| full_tx.txid == txid);
131+
let wallet_tx = wallet.get_tx(txid);
132+
133+
let is_evicted = match wallet_tx {
134+
Some(wallet_tx) => {
135+
!wallet_tx.chain_position.is_unconfirmed()
136+
&& !wallet_tx.chain_position.is_confirmed()
137+
}
138+
None => true,
139+
};
140+
141+
if is_evicted {
142+
if let Some(full_tx) = tx_node {
143+
evicted_txs.push((full_tx.txid, full_tx.last_seen.unwrap_or(0)));
144+
} else {
145+
evicted_txs.push((txid, 0));
146+
}
147+
}
148+
}
149+
150+
if !evicted_txs.is_empty() {
151+
wallet.apply_evicted_txs(evicted_txs.clone());
152+
println!("Applied {} evicted transactions", evicted_txs.len());
153+
}
154+
155+
wallet.apply_update(sync_update)?;
156+
wallet.persist(&mut db)?;
157+
158+
let balance_after_sync = wallet.balance();
159+
println!("Wallet balance after sync: {}", balance_after_sync.total());
160+
println!(
161+
"Wallet has {} transactions and {} utxos after partial sync",
162+
wallet.transactions().count(),
163+
wallet.list_unspent().count()
164+
);
165+
92166
Ok(())
93167
}

examples/example_wallet_esplora_async/src/main.rs

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use std::{collections::BTreeSet, io::Write};
2-
31
use anyhow::Ok;
42
use bdk_esplora::{esplora_client, EsploraAsyncExt};
53
use bdk_wallet::{
6-
bitcoin::{Amount, Network},
4+
bitcoin::{Amount, Network, Txid},
75
rusqlite::Connection,
86
KeychainKind, SignOptions, Wallet,
97
};
8+
use std::{
9+
collections::{BTreeSet, HashSet},
10+
io::Write,
11+
};
1012

1113
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
1214
const STOP_GAP: usize = 5;
@@ -42,7 +44,7 @@ async fn main() -> Result<(), anyhow::Error> {
4244
let balance = wallet.balance();
4345
println!("Wallet balance before syncing: {}", balance.total());
4446

45-
print!("Syncing...");
47+
println!("=== Performing Full Sync ===");
4648
let client = esplora_client::Builder::new(ESPLORA_URL).build_async()?;
4749

4850
let request = wallet.start_full_scan().inspect({
@@ -66,7 +68,12 @@ async fn main() -> Result<(), anyhow::Error> {
6668
println!();
6769

6870
let balance = wallet.balance();
69-
println!("Wallet balance after syncing: {}", balance.total());
71+
println!("Wallet balance after full sync: {}", balance.total());
72+
println!(
73+
"Wallet has {} transactions and {} utxos after full sync",
74+
wallet.transactions().count(),
75+
wallet.list_unspent().count()
76+
);
7077

7178
if balance.total() < SEND_AMOUNT {
7279
println!("Please send at least {SEND_AMOUNT} to the receiving address");
@@ -84,5 +91,71 @@ async fn main() -> Result<(), anyhow::Error> {
8491
client.broadcast(&tx).await?;
8592
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
8693

94+
let unconfirmed_txids: HashSet<Txid> = wallet
95+
.transactions()
96+
.filter(|tx| tx.chain_position.is_unconfirmed())
97+
.map(|tx| tx.tx_node.txid)
98+
.collect();
99+
100+
println!("\n=== Performing Partial Sync ===\n");
101+
print!("SCANNING: ");
102+
let mut printed = 0;
103+
let sync_request = wallet
104+
.start_sync_with_revealed_spks()
105+
.inspect(move |_, sync_progress| {
106+
let progress_percent =
107+
(100 * sync_progress.consumed()) as f32 / sync_progress.total() as f32;
108+
let progress_percent = progress_percent.round() as u32;
109+
if progress_percent.is_multiple_of(5) && progress_percent > printed {
110+
print!("{}% ", progress_percent);
111+
std::io::stdout().flush().expect("must flush");
112+
printed = progress_percent;
113+
}
114+
});
115+
let sync_update = client.sync(sync_request, PARALLEL_REQUESTS).await?;
116+
println!();
117+
118+
let mut evicted_txs = Vec::new();
119+
for txid in unconfirmed_txids {
120+
let tx_node = wallet
121+
.tx_graph()
122+
.full_txs()
123+
.find(|full_tx| full_tx.txid == txid);
124+
let wallet_tx = wallet.get_tx(txid);
125+
126+
let is_evicted = match wallet_tx {
127+
Some(wallet_tx) => {
128+
!wallet_tx.chain_position.is_unconfirmed()
129+
&& !wallet_tx.chain_position.is_confirmed()
130+
}
131+
None => true,
132+
};
133+
134+
if is_evicted {
135+
if let Some(full_tx) = tx_node {
136+
evicted_txs.push((full_tx.txid, full_tx.last_seen.unwrap_or(0)));
137+
} else {
138+
evicted_txs.push((txid, 0));
139+
}
140+
}
141+
}
142+
143+
if !evicted_txs.is_empty() {
144+
let evicted_count = evicted_txs.len();
145+
wallet.apply_evicted_txs(evicted_txs);
146+
println!("Applied {evicted_count} evicted transactions");
147+
}
148+
149+
wallet.apply_update(sync_update)?;
150+
wallet.persist(&mut conn)?;
151+
152+
let balance_after_sync = wallet.balance();
153+
println!("Wallet balance after sync: {}", balance_after_sync.total());
154+
println!(
155+
"Wallet has {} transactions and {} utxos after partial sync",
156+
wallet.transactions().count(),
157+
wallet.list_unspent().count()
158+
);
159+
87160
Ok(())
88161
}

examples/example_wallet_esplora_blocking/src/main.rs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use std::{collections::BTreeSet, io::Write};
2-
31
use bdk_esplora::{esplora_client, EsploraExt};
42
use bdk_wallet::{
5-
bitcoin::{Amount, Network},
3+
bitcoin::{Amount, Network, Txid},
64
file_store::Store,
75
KeychainKind, SignOptions, Wallet,
86
};
7+
use std::{
8+
collections::{BTreeSet, HashSet},
9+
io::Write,
10+
};
911

1012
const DB_MAGIC: &str = "bdk_wallet_esplora_example";
1113
const DB_PATH: &str = "bdk-example-esplora-blocking.db";
@@ -84,5 +86,71 @@ fn main() -> Result<(), anyhow::Error> {
8486
client.broadcast(&tx)?;
8587
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
8688

89+
let unconfirmed_txids: HashSet<Txid> = wallet
90+
.transactions()
91+
.filter(|tx| tx.chain_position.is_unconfirmed())
92+
.map(|tx| tx.tx_node.txid)
93+
.collect();
94+
95+
println!("\n=== Performing Partial Sync ===\n");
96+
print!("SCANNING: ");
97+
let mut printed = 0;
98+
let sync_request = wallet
99+
.start_sync_with_revealed_spks()
100+
.inspect(move |_, sync_progress| {
101+
let progress_percent =
102+
(100 * sync_progress.consumed()) as f32 / sync_progress.total() as f32;
103+
let progress_percent = progress_percent.round() as u32;
104+
if progress_percent.is_multiple_of(5) && progress_percent > printed {
105+
print!("{progress_percent}% ");
106+
std::io::stdout().flush().expect("must flush");
107+
printed = progress_percent;
108+
}
109+
});
110+
let sync_update = client.sync(sync_request, PARALLEL_REQUESTS)?;
111+
println!();
112+
113+
let mut evicted_txs = Vec::new();
114+
for txid in unconfirmed_txids {
115+
let tx_node = wallet
116+
.tx_graph()
117+
.full_txs()
118+
.find(|full_tx| full_tx.txid == txid);
119+
let wallet_tx = wallet.get_tx(txid);
120+
121+
let is_evicted = match wallet_tx {
122+
Some(wallet_tx) => {
123+
!wallet_tx.chain_position.is_unconfirmed()
124+
&& !wallet_tx.chain_position.is_confirmed()
125+
}
126+
None => true,
127+
};
128+
129+
if is_evicted {
130+
if let Some(full_tx) = tx_node {
131+
evicted_txs.push((full_tx.txid, full_tx.last_seen.unwrap_or(0)));
132+
} else {
133+
evicted_txs.push((txid, 0));
134+
}
135+
}
136+
}
137+
138+
if !evicted_txs.is_empty() {
139+
let evicted_count = evicted_txs.len();
140+
wallet.apply_evicted_txs(evicted_txs);
141+
println!("Applied {evicted_count} evicted transactions");
142+
}
143+
144+
wallet.apply_update(sync_update)?;
145+
wallet.persist(&mut db)?;
146+
147+
let balance_after_sync = wallet.balance();
148+
println!("Wallet balance after sync: {}", balance_after_sync.total());
149+
println!(
150+
"Wallet has {} transactions and {} utxos after partial sync",
151+
wallet.transactions().count(),
152+
wallet.list_unspent().count()
153+
);
154+
87155
Ok(())
88156
}

0 commit comments

Comments
 (0)