Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wallet examples #1442

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Fully working examples of how to use these components are in `/example-crates`:
- [`wallet_esplora_blocking`](./example-crates/wallet_esplora_blocking): Uses the `Wallet` to sync and spend using the Esplora blocking interface.
- [`wallet_esplora_async`](./example-crates/wallet_esplora_async): Uses the `Wallet` to sync and spend using the Esplora asynchronous interface.
- [`wallet_electrum`](./example-crates/wallet_electrum): Uses the `Wallet` to sync and spend using Electrum.
- [`wallet_rpc`](./example-crates/wallet_rpc): Uses the `Wallet` to sync and spend using Bitcoin RPC.

[`BDK 1.0 project page`]: https://github.com/orgs/bitcoindevkit/projects/14
[`rust-miniscript`]: https://github.com/rust-bitcoin/rust-miniscript
Expand Down
4 changes: 2 additions & 2 deletions example-crates/wallet_electrum/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "wallet_electrum_example"
name = "wallet_electrum"
ValuedMammal marked this conversation as resolved.
Show resolved Hide resolved
version = "0.2.0"
edition = "2021"

[dependencies]
bdk_wallet = { path = "../../crates/wallet", features = ["file_store"] }
bdk_wallet = { path = "../../crates/wallet", features = ["rusqlite"] }
bdk_electrum = { path = "../../crates/electrum" }
anyhow = "1"
52 changes: 22 additions & 30 deletions example-crates/wallet_electrum/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,42 @@
use bdk_wallet::file_store::Store;
use bdk_wallet::Wallet;
use std::io::Write;
use std::str::FromStr;

use bdk_electrum::electrum_client;
use bdk_electrum::BdkElectrumClient;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::bitcoin::{Address, Amount};
use bdk_wallet::chain::collections::HashSet;
use bdk_wallet::{KeychainKind, SignOptions};
use bdk_electrum::{electrum_client, BdkElectrumClient};
use bdk_wallet::{
bitcoin::{Amount, Network},
chain::collections::HashSet,
rusqlite::Connection,
KeychainKind, SignOptions, Wallet,
};

const DB_MAGIC: &str = "bdk_wallet_electrum_example";
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
const STOP_GAP: usize = 50;
const STOP_GAP: usize = 20;
const BATCH_SIZE: usize = 5;

const NETWORK: Network = Network::Testnet;
const NETWORK: Network = Network::Signet;
const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
const ELECTRUM_URL: &str = "ssl://electrum.blockstream.info:60002";
const ELECTRUM_URL: &str = "ssl://mempool.space:60602";

fn main() -> Result<(), anyhow::Error> {
let db_path = "bdk-electrum-example.db";

let mut db = Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), db_path)?;
let mut conn = Connection::open_in_memory().expect("must open connection");

let wallet_opt = Wallet::load()
.descriptors(EXTERNAL_DESC, INTERNAL_DESC)
.network(NETWORK)
.load_wallet(&mut db)?;
.load_wallet(&mut conn)?;
let mut wallet = match wallet_opt {
Some(wallet) => wallet,
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
.network(NETWORK)
.create_wallet(&mut db)?,
.create_wallet(&mut conn)?,
};

let address = wallet.next_unused_address(KeychainKind::External);
wallet.persist(&mut db)?;
wallet.persist(&mut conn)?;
println!("Generated Address: {}", address);

let balance = wallet.balance();
println!("Wallet balance before syncing: {} sats", balance.total());
println!("Wallet balance before syncing: {}", balance.total());

print!("Syncing...");
let client = BdkElectrumClient::new(electrum_client::Client::new(ELECTRUM_URL)?);
Expand All @@ -54,9 +49,9 @@ fn main() -> Result<(), anyhow::Error> {
.start_full_scan()
.inspect_spks_for_all_keychains({
let mut once = HashSet::<KeychainKind>::new();
move |k, spk_i, _| {
if once.insert(k) {
print!("\nScanning keychain [{:?}]", k)
move |kind, spk_i, _| {
if once.insert(kind) {
print!("\nScanning keychain [{:?}] {:<3}", kind, spk_i)
} else {
print!(" {:<3}", spk_i)
}
Expand All @@ -72,25 +67,22 @@ fn main() -> Result<(), anyhow::Error> {
println!();

wallet.apply_update(update)?;
wallet.persist(&mut db)?;
wallet.persist(&mut conn)?;

let balance = wallet.balance();
println!("Wallet balance after syncing: {} sats", balance.total());
println!("Wallet balance after syncing: {}", balance.total());

if balance.total() < SEND_AMOUNT {
println!(
"Please send at least {} sats to the receiving address",
"Please send at least {} to the receiving address",
SEND_AMOUNT
);
std::process::exit(0);
}

let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?
.require_network(Network::Testnet)?;

let mut tx_builder = wallet.build_tx();
tx_builder
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
.add_recipient(address.script_pubkey(), SEND_AMOUNT)
.enable_rbf();

let mut psbt = tx_builder.finish()?;
Expand Down
48 changes: 30 additions & 18 deletions example-crates/wallet_esplora_async/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
use std::{collections::BTreeSet, io::Write};
use std::io::Write;

use anyhow::Ok;
use bdk_esplora::{esplora_client, EsploraAsyncExt};
use bdk_wallet::{
bitcoin::{Amount, Network},
bitcoin::{Amount, Network, Script},
rusqlite::Connection,
KeychainKind, SignOptions, Wallet,
};

const SEND_AMOUNT: Amount = Amount::from_sat(5000);
const STOP_GAP: usize = 5;
const PARALLEL_REQUESTS: usize = 5;
const STOP_GAP: usize = 20;
const PARALLEL_REQUESTS: usize = 3;

const DB_PATH: &str = "bdk-example-esplora-async.sqlite";
const NETWORK: Network = Network::Signet;
const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";

#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let mut conn = Connection::open(DB_PATH)?;
let mut conn = Connection::open_in_memory().expect("must open connection");

let wallet_opt = Wallet::load()
.descriptors(EXTERNAL_DESC, INTERNAL_DESC)
Expand All @@ -38,38 +37,51 @@ async fn main() -> Result<(), anyhow::Error> {
println!("Next unused address: ({}) {}", address.index, address);

let balance = wallet.balance();
println!("Wallet balance before syncing: {} sats", balance.total());
println!("Wallet balance before syncing: {}", balance.total());

print!("Syncing...");
let client = esplora_client::Builder::new(ESPLORA_URL).build_async()?;

let request = wallet.start_full_scan().inspect_spks_for_all_keychains({
let mut once = BTreeSet::<KeychainKind>::new();
move |keychain, spk_i, _| {
if once.insert(keychain) {
print!("\nScanning keychain [{:?}] ", keychain);
}
print!(" {:<3}", spk_i);
std::io::stdout().flush().expect("must flush")
fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static {
let mut once = Some(());
let mut stdout = std::io::stdout();
move |spk_i, _| {
match once.take() {
Some(_) => print!("\nScanning keychain [{:?}] {:<3}", kind, spk_i),
None => print!(" {:<3}", spk_i),
};
stdout.flush().expect("must flush");
}
});
}
let request = wallet
.start_full_scan()
.inspect_spks_for_keychain(
KeychainKind::External,
generate_inspect(KeychainKind::External),
)
.inspect_spks_for_keychain(
KeychainKind::Internal,
generate_inspect(KeychainKind::Internal),
);

let mut update = client
.full_scan(request, STOP_GAP, PARALLEL_REQUESTS)
.await?;
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
let _ = update.graph_update.update_last_seen_unconfirmed(now);

println!();

wallet.apply_update(update)?;
wallet.persist(&mut conn)?;
ValuedMammal marked this conversation as resolved.
Show resolved Hide resolved
println!();

let balance = wallet.balance();
println!("Wallet balance after syncing: {} sats", balance.total());
println!("Wallet balance after syncing: {}", balance.total());

if balance.total() < SEND_AMOUNT {
println!(
"Please send at least {} sats to the receiving address",
"Please send at least {} to the receiving address",
SEND_AMOUNT
);
std::process::exit(0);
Expand Down
67 changes: 36 additions & 31 deletions example-crates/wallet_esplora_blocking/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,82 @@
use std::{collections::BTreeSet, io::Write};
use std::io::Write;

use bdk_esplora::{esplora_client, EsploraExt};
use bdk_wallet::{
bitcoin::{Amount, Network},
file_store::Store,
bitcoin::{Amount, Network, Script},
rusqlite::Connection,
KeychainKind, SignOptions, Wallet,
};

const DB_MAGIC: &str = "bdk_wallet_esplora_example";
const DB_PATH: &str = "bdk-example-esplora-blocking.db";
const SEND_AMOUNT: Amount = Amount::from_sat(5000);
const STOP_GAP: usize = 5;
const PARALLEL_REQUESTS: usize = 5;
const STOP_GAP: usize = 20;
const PARALLEL_REQUESTS: usize = 3;

const NETWORK: Network = Network::Signet;
const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
const INTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)";
const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";

fn main() -> Result<(), anyhow::Error> {
let mut db = Store::<bdk_wallet::ChangeSet>::open_or_create_new(DB_MAGIC.as_bytes(), DB_PATH)?;
let mut conn = Connection::open_in_memory().expect("must open connection");

let wallet_opt = Wallet::load()
.descriptors(EXTERNAL_DESC, INTERNAL_DESC)
.network(NETWORK)
.load_wallet(&mut db)?;
.load_wallet(&mut conn)?;
let mut wallet = match wallet_opt {
Some(wallet) => wallet,
None => Wallet::create(EXTERNAL_DESC, INTERNAL_DESC)
.network(NETWORK)
.create_wallet(&mut db)?,
.create_wallet(&mut conn)?,
};

let address = wallet.next_unused_address(KeychainKind::External);
wallet.persist(&mut db)?;
println!(
"Next unused address: ({}) {}",
address.index, address.address
);
wallet.persist(&mut conn)?;
println!("Generated Address: {}", address);

let balance = wallet.balance();
println!("Wallet balance before syncing: {} sats", balance.total());
println!("Wallet balance before syncing: {}", balance.total());

print!("Syncing...");
let client = esplora_client::Builder::new(ESPLORA_URL).build_blocking();

let request = wallet.start_full_scan().inspect_spks_for_all_keychains({
let mut once = BTreeSet::<KeychainKind>::new();
move |keychain, spk_i, _| {
if once.insert(keychain) {
print!("\nScanning keychain [{:?}] ", keychain);
}
print!(" {:<3}", spk_i);
std::io::stdout().flush().expect("must flush")
fn generate_inspect(kind: KeychainKind) -> impl FnMut(u32, &Script) + Send + Sync + 'static {
let mut once = Some(());
let mut stdout = std::io::stdout();
move |spk_i, _| {
match once.take() {
Some(_) => print!("\nScanning keychain [{:?}] {:<3}", kind, spk_i),
None => print!(" {:<3}", spk_i),
};
stdout.flush().expect("must flush");
}
});
}
let request = wallet
.start_full_scan()
.inspect_spks_for_keychain(
KeychainKind::External,
generate_inspect(KeychainKind::External),
)
.inspect_spks_for_keychain(
KeychainKind::Internal,
generate_inspect(KeychainKind::Internal),
);

let mut update = client.full_scan(request, STOP_GAP, PARALLEL_REQUESTS)?;
let now = std::time::UNIX_EPOCH.elapsed().unwrap().as_secs();
let _ = update.graph_update.update_last_seen_unconfirmed(now);

wallet.apply_update(update)?;
if let Some(changeset) = wallet.take_staged() {
db.append_changeset(&changeset)?;
}
println!();

wallet.apply_update(update)?;
wallet.persist(&mut conn)?;

let balance = wallet.balance();
println!("Wallet balance after syncing: {} sats", balance.total());
println!("Wallet balance after syncing: {}", balance.total());

if balance.total() < SEND_AMOUNT {
println!(
"Please send at least {} sats to the receiving address",
"Please send at least {} to the receiving address",
SEND_AMOUNT
);
std::process::exit(0);
Expand Down
3 changes: 1 addition & 2 deletions example-crates/wallet_rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bdk_wallet = { path = "../../crates/wallet", features = ["file_store"] }
bdk_wallet = { path = "../../crates/wallet" }
bdk_bitcoind_rpc = { path = "../../crates/bitcoind_rpc" }

anyhow = "1"
clap = { version = "3.2.25", features = ["derive", "env"] }
ctrlc = "2.0.1"
11 changes: 4 additions & 7 deletions example-crates/wallet_rpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,18 @@ wallet_rpc 0.1.0
Bitcoind RPC example using `bdk_wallet::Wallet`

USAGE:
wallet_rpc [OPTIONS] <DESCRIPTOR> [CHANGE_DESCRIPTOR]
wallet_rpc [OPTIONS] <DESCRIPTOR> <CHANGE_DESCRIPTOR>

ARGS:
<DESCRIPTOR> Wallet descriptor [env: DESCRIPTOR=]
<CHANGE_DESCRIPTOR> Wallet change descriptor [env: CHANGE_DESCRIPTOR=]

OPTIONS:
--db-path <DB_PATH>
Where to store wallet data [env: BDK_DB_PATH=] [default: .bdk_wallet_rpc_example.db]

-h, --help
Print help information

--network <NETWORK>
Bitcoin network to connect to [env: BITCOIN_NETWORK=] [default: testnet]
Bitcoin network to connect to [env: BITCOIN_NETWORK=] [default: signet]

--rpc-cookie <RPC_COOKIE>
RPC auth cookie file [env: RPC_COOKIE=]
Expand All @@ -33,10 +30,10 @@ OPTIONS:
RPC auth username [env: RPC_USER=]

--start-height <START_HEIGHT>
Earliest block height to start sync from [env: START_HEIGHT=] [default: 481824]
Earliest block height to start sync from [env: START_HEIGHT=] [default: 100000]

--url <URL>
RPC URL [env: RPC_URL=] [default: 127.0.0.1:8332]
RPC URL [env: RPC_URL=] [default: 127.0.0.1:38332]

-V, --version
Print version information
Expand Down
Loading
Loading