Skip to content

Commit f4f6dc8

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 4d3116a commit f4f6dc8

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!(
@@ -92,5 +98,73 @@ fn main() -> Result<(), anyhow::Error> {
9298
client.transaction_broadcast(&tx)?;
9399
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
94100

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

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!(
@@ -87,5 +94,71 @@ async fn main() -> Result<(), anyhow::Error> {
8794
client.broadcast(&tx).await?;
8895
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
8996

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

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";
@@ -87,5 +89,71 @@ fn main() -> Result<(), anyhow::Error> {
8789
client.broadcast(&tx)?;
8890
println!("Tx broadcasted! Txid: {}", tx.compute_txid());
8991

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

0 commit comments

Comments
 (0)