From: LLFourn Date: Thu, 2 Mar 2023 05:59:11 +0000 (+1100) Subject: Rename the stub wallet examples X-Git-Tag: v1.0.0-alpha.0~6^2~32 X-Git-Url: http://internal-gitweb-vhost/%22https:/parse/scripts/database/-script/-debug/struct.DecoderReader.html?a=commitdiff_plain;h=b5559767db7e8f389415dfe135907063e21598b8;p=bdk Rename the stub wallet examples --- diff --git a/Cargo.toml b/Cargo.toml index 7f97bf6a..78adeb45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ members = [ "crates/chain", "crates/file_store", "crates/electrum", - "example-crates/esplora-wallet", - "example-crates/electrum-wallet", - "example-crates/keychain_tracker_electrum_example", - "example-crates/keychain_tracker_esplora_example", + "example-crates/keychain_tracker_electrum", + "example-crates/keychain_tracker_esplora", "example-crates/keychain_tracker_example_cli", + "example-crates/wallet_electrum", + "example-crates/wallet_esplora", "nursery/tmp_plan", "nursery/coin_select" ] diff --git a/example-crates/electrum-wallet/Cargo.toml b/example-crates/electrum-wallet/Cargo.toml deleted file mode 100644 index 5e230054..00000000 --- a/example-crates/electrum-wallet/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "electrum-wallet-example" -version = "0.1.0" -edition = "2021" - -[dependencies] -bdk = { path = "../../crates/bdk" } diff --git a/example-crates/electrum-wallet/src/main.rs b/example-crates/electrum-wallet/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/example-crates/electrum-wallet/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/example-crates/esplora-wallet/Cargo.toml b/example-crates/esplora-wallet/Cargo.toml deleted file mode 100644 index 944f09b3..00000000 --- a/example-crates/esplora-wallet/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "bdk-esplora-wallet-example" -version = "0.1.0" -edition = "2021" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bdk = { path = "../../crates/bdk" } diff --git a/example-crates/esplora-wallet/src/main.rs b/example-crates/esplora-wallet/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/example-crates/esplora-wallet/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/example-crates/keychain_tracker_electrum/.gitignore b/example-crates/keychain_tracker_electrum/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/example-crates/keychain_tracker_electrum/.gitignore @@ -0,0 +1 @@ +/target diff --git a/example-crates/keychain_tracker_electrum/Cargo.toml b/example-crates/keychain_tracker_electrum/Cargo.toml new file mode 100644 index 00000000..4eceaa70 --- /dev/null +++ b/example-crates/keychain_tracker_electrum/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "keychain_tracker_electrum_example" +version = "0.1.0" +edition = "2021" + +[dependencies] +bdk_chain = { path = "../../crates/chain", version = "0.3", features = ["serde"] } +bdk_electrum = { path = "../../crates/electrum" } +keychain_tracker_example_cli = { path = "../keychain_tracker_example_cli"} diff --git a/example-crates/keychain_tracker_electrum/README.md b/example-crates/keychain_tracker_electrum/README.md new file mode 100644 index 00000000..b8bdea21 --- /dev/null +++ b/example-crates/keychain_tracker_electrum/README.md @@ -0,0 +1,6 @@ +# Keychain Tracker with electrum + +This example shows how you use the `KeychainTracker` from `bdk_chain` to create a simple command +line wallet. + + diff --git a/example-crates/keychain_tracker_electrum/src/main.rs b/example-crates/keychain_tracker_electrum/src/main.rs new file mode 100644 index 00000000..0fe27fd0 --- /dev/null +++ b/example-crates/keychain_tracker_electrum/src/main.rs @@ -0,0 +1,248 @@ +use bdk_chain::bitcoin::{Address, OutPoint, Txid}; +use bdk_electrum::bdk_chain::{self, bitcoin::Network, TxHeight}; +use bdk_electrum::{ + electrum_client::{self, ElectrumApi}, + ElectrumExt, ElectrumUpdate, +}; +use keychain_tracker_example_cli::{ + self as cli, + anyhow::{self, Context}, + clap::{self, Parser, Subcommand}, +}; +use std::{collections::BTreeMap, fmt::Debug, io, io::Write}; + +#[derive(Subcommand, Debug, Clone)] +enum ElectrumCommands { + /// Scans the addresses in the wallet using esplora API. + Scan { + /// When a gap this large has been found for a keychain it will stop. + #[clap(long, default_value = "5")] + stop_gap: usize, + #[clap(flatten)] + scan_options: ScanOptions, + }, + /// Scans particular addresses using esplora API + Sync { + /// Scan all the unused addresses + #[clap(long)] + unused_spks: bool, + /// Scan every address that you have derived + #[clap(long)] + all_spks: bool, + /// Scan unspent outpoints for spends or changes to confirmation status of residing tx + #[clap(long)] + utxos: bool, + /// Scan unconfirmed transactions for updates + #[clap(long)] + unconfirmed: bool, + #[clap(flatten)] + scan_options: ScanOptions, + }, +} + +#[derive(Parser, Debug, Clone, PartialEq)] +pub struct ScanOptions { + /// Set batch size for each script_history call to electrum client + #[clap(long, default_value = "25")] + pub batch_size: usize, +} + +fn main() -> anyhow::Result<()> { + let (args, keymap, mut tracker, mut db) = cli::init::()?; + + let electrum_url = match args.network { + Network::Bitcoin => "ssl://electrum.blockstream.info:50002", + Network::Testnet => "ssl://electrum.blockstream.info:60002", + Network::Regtest => "tcp://localhost:60401", + Network::Signet => "tcp://signet-electrumx.wakiyamap.dev:50001", + }; + let config = electrum_client::Config::builder() + .validate_domain(match args.network { + Network::Bitcoin => true, + _ => false, + }) + .build(); + + let client = electrum_client::Client::from_config(electrum_url, config)?; + + let electrum_cmd = match args.command { + cli::Commands::ChainSpecific(electrum_cmd) => electrum_cmd, + general_command => { + return cli::handle_commands( + general_command, + |transaction| { + let _txid = client.transaction_broadcast(transaction)?; + Ok(()) + }, + &mut tracker, + &mut db, + args.network, + &keymap, + ) + } + }; + + let response = match electrum_cmd { + ElectrumCommands::Scan { + stop_gap, + scan_options: scan_option, + } => { + let (spk_iterators, local_chain) = { + // Get a short lock on the tracker to get the spks iterators + // and local chain state + let tracker = &*tracker.lock().unwrap(); + let spk_iterators = tracker + .txout_index + .spks_of_all_keychains() + .into_iter() + .map(|(keychain, iter)| { + let mut first = true; + let spk_iter = iter.inspect(move |(i, _)| { + if first { + eprint!("\nscanning {}: ", keychain); + first = false; + } + + eprint!("{} ", i); + let _ = io::stdout().flush(); + }); + (keychain, spk_iter) + }) + .collect::>(); + let local_chain = tracker.chain().checkpoints().clone(); + (spk_iterators, local_chain) + }; + + // we scan the spks **without** a lock on the tracker + client.scan( + &local_chain, + spk_iterators, + core::iter::empty(), + core::iter::empty(), + stop_gap, + scan_option.batch_size, + )? + } + ElectrumCommands::Sync { + mut unused_spks, + mut utxos, + mut unconfirmed, + all_spks, + scan_options, + } => { + // Get a short lock on the tracker to get the spks we're interested in + let tracker = tracker.lock().unwrap(); + + if !(all_spks || unused_spks || utxos || unconfirmed) { + unused_spks = true; + unconfirmed = true; + utxos = true; + } else if all_spks { + unused_spks = false; + } + + let mut spks: Box> = + Box::new(core::iter::empty()); + if all_spks { + let all_spks = tracker + .txout_index + .all_spks() + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + spks = Box::new(spks.chain(all_spks.into_iter().map(|(index, script)| { + eprintln!("scanning {:?}", index); + script + }))); + } + if unused_spks { + let unused_spks = tracker + .txout_index + .unused_spks(..) + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| { + eprintln!( + "Checking if address {} {:?} has been used", + Address::from_script(&script, args.network).unwrap(), + index + ); + + script + }))); + } + + let mut outpoints: Box> = Box::new(core::iter::empty()); + + if utxos { + let utxos = tracker + .full_utxos() + .map(|(_, utxo)| utxo) + .collect::>(); + outpoints = Box::new( + utxos + .into_iter() + .inspect(|utxo| { + eprintln!( + "Checking if outpoint {} (value: {}) has been spent", + utxo.outpoint, utxo.txout.value + ); + }) + .map(|utxo| utxo.outpoint), + ); + }; + + let mut txids: Box> = Box::new(core::iter::empty()); + + if unconfirmed { + let unconfirmed_txids = tracker + .chain() + .range_txids_by_height(TxHeight::Unconfirmed..) + .map(|(_, txid)| *txid) + .collect::>(); + + txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| { + eprintln!("Checking if {} is confirmed yet", txid); + })); + } + + let local_chain = tracker.chain().checkpoints().clone(); + // drop lock on tracker + drop(tracker); + + // we scan the spks **without** a lock on the tracker + ElectrumUpdate { + chain_update: client + .scan_without_keychain( + &local_chain, + spks, + txids, + outpoints, + scan_options.batch_size, + ) + .context("scanning the blockchain")?, + ..Default::default() + } + } + }; + + let missing_txids = response.missing_full_txs(&*tracker.lock().unwrap()); + + // fetch the missing full transactions **without** a lock on the tracker + let new_txs = client + .batch_transaction_get(missing_txids) + .context("fetching full transactions")?; + + { + // Get a final short lock to apply the changes + let mut tracker = tracker.lock().unwrap(); + let changeset = { + let scan = response.into_keychain_scan(new_txs, &*tracker)?; + tracker.determine_changeset(&scan)? + }; + db.lock().unwrap().append_changeset(&changeset)?; + tracker.apply_changeset(changeset); + }; + + Ok(()) +} diff --git a/example-crates/keychain_tracker_electrum_example/.gitignore b/example-crates/keychain_tracker_electrum_example/.gitignore deleted file mode 100644 index ea8c4bf7..00000000 --- a/example-crates/keychain_tracker_electrum_example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/target diff --git a/example-crates/keychain_tracker_electrum_example/Cargo.toml b/example-crates/keychain_tracker_electrum_example/Cargo.toml deleted file mode 100644 index 4eceaa70..00000000 --- a/example-crates/keychain_tracker_electrum_example/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "keychain_tracker_electrum_example" -version = "0.1.0" -edition = "2021" - -[dependencies] -bdk_chain = { path = "../../crates/chain", version = "0.3", features = ["serde"] } -bdk_electrum = { path = "../../crates/electrum" } -keychain_tracker_example_cli = { path = "../keychain_tracker_example_cli"} diff --git a/example-crates/keychain_tracker_electrum_example/README.md b/example-crates/keychain_tracker_electrum_example/README.md deleted file mode 100644 index b8bdea21..00000000 --- a/example-crates/keychain_tracker_electrum_example/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Keychain Tracker with electrum - -This example shows how you use the `KeychainTracker` from `bdk_chain` to create a simple command -line wallet. - - diff --git a/example-crates/keychain_tracker_electrum_example/src/main.rs b/example-crates/keychain_tracker_electrum_example/src/main.rs deleted file mode 100644 index 0fe27fd0..00000000 --- a/example-crates/keychain_tracker_electrum_example/src/main.rs +++ /dev/null @@ -1,248 +0,0 @@ -use bdk_chain::bitcoin::{Address, OutPoint, Txid}; -use bdk_electrum::bdk_chain::{self, bitcoin::Network, TxHeight}; -use bdk_electrum::{ - electrum_client::{self, ElectrumApi}, - ElectrumExt, ElectrumUpdate, -}; -use keychain_tracker_example_cli::{ - self as cli, - anyhow::{self, Context}, - clap::{self, Parser, Subcommand}, -}; -use std::{collections::BTreeMap, fmt::Debug, io, io::Write}; - -#[derive(Subcommand, Debug, Clone)] -enum ElectrumCommands { - /// Scans the addresses in the wallet using esplora API. - Scan { - /// When a gap this large has been found for a keychain it will stop. - #[clap(long, default_value = "5")] - stop_gap: usize, - #[clap(flatten)] - scan_options: ScanOptions, - }, - /// Scans particular addresses using esplora API - Sync { - /// Scan all the unused addresses - #[clap(long)] - unused_spks: bool, - /// Scan every address that you have derived - #[clap(long)] - all_spks: bool, - /// Scan unspent outpoints for spends or changes to confirmation status of residing tx - #[clap(long)] - utxos: bool, - /// Scan unconfirmed transactions for updates - #[clap(long)] - unconfirmed: bool, - #[clap(flatten)] - scan_options: ScanOptions, - }, -} - -#[derive(Parser, Debug, Clone, PartialEq)] -pub struct ScanOptions { - /// Set batch size for each script_history call to electrum client - #[clap(long, default_value = "25")] - pub batch_size: usize, -} - -fn main() -> anyhow::Result<()> { - let (args, keymap, mut tracker, mut db) = cli::init::()?; - - let electrum_url = match args.network { - Network::Bitcoin => "ssl://electrum.blockstream.info:50002", - Network::Testnet => "ssl://electrum.blockstream.info:60002", - Network::Regtest => "tcp://localhost:60401", - Network::Signet => "tcp://signet-electrumx.wakiyamap.dev:50001", - }; - let config = electrum_client::Config::builder() - .validate_domain(match args.network { - Network::Bitcoin => true, - _ => false, - }) - .build(); - - let client = electrum_client::Client::from_config(electrum_url, config)?; - - let electrum_cmd = match args.command { - cli::Commands::ChainSpecific(electrum_cmd) => electrum_cmd, - general_command => { - return cli::handle_commands( - general_command, - |transaction| { - let _txid = client.transaction_broadcast(transaction)?; - Ok(()) - }, - &mut tracker, - &mut db, - args.network, - &keymap, - ) - } - }; - - let response = match electrum_cmd { - ElectrumCommands::Scan { - stop_gap, - scan_options: scan_option, - } => { - let (spk_iterators, local_chain) = { - // Get a short lock on the tracker to get the spks iterators - // and local chain state - let tracker = &*tracker.lock().unwrap(); - let spk_iterators = tracker - .txout_index - .spks_of_all_keychains() - .into_iter() - .map(|(keychain, iter)| { - let mut first = true; - let spk_iter = iter.inspect(move |(i, _)| { - if first { - eprint!("\nscanning {}: ", keychain); - first = false; - } - - eprint!("{} ", i); - let _ = io::stdout().flush(); - }); - (keychain, spk_iter) - }) - .collect::>(); - let local_chain = tracker.chain().checkpoints().clone(); - (spk_iterators, local_chain) - }; - - // we scan the spks **without** a lock on the tracker - client.scan( - &local_chain, - spk_iterators, - core::iter::empty(), - core::iter::empty(), - stop_gap, - scan_option.batch_size, - )? - } - ElectrumCommands::Sync { - mut unused_spks, - mut utxos, - mut unconfirmed, - all_spks, - scan_options, - } => { - // Get a short lock on the tracker to get the spks we're interested in - let tracker = tracker.lock().unwrap(); - - if !(all_spks || unused_spks || utxos || unconfirmed) { - unused_spks = true; - unconfirmed = true; - utxos = true; - } else if all_spks { - unused_spks = false; - } - - let mut spks: Box> = - Box::new(core::iter::empty()); - if all_spks { - let all_spks = tracker - .txout_index - .all_spks() - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - spks = Box::new(spks.chain(all_spks.into_iter().map(|(index, script)| { - eprintln!("scanning {:?}", index); - script - }))); - } - if unused_spks { - let unused_spks = tracker - .txout_index - .unused_spks(..) - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| { - eprintln!( - "Checking if address {} {:?} has been used", - Address::from_script(&script, args.network).unwrap(), - index - ); - - script - }))); - } - - let mut outpoints: Box> = Box::new(core::iter::empty()); - - if utxos { - let utxos = tracker - .full_utxos() - .map(|(_, utxo)| utxo) - .collect::>(); - outpoints = Box::new( - utxos - .into_iter() - .inspect(|utxo| { - eprintln!( - "Checking if outpoint {} (value: {}) has been spent", - utxo.outpoint, utxo.txout.value - ); - }) - .map(|utxo| utxo.outpoint), - ); - }; - - let mut txids: Box> = Box::new(core::iter::empty()); - - if unconfirmed { - let unconfirmed_txids = tracker - .chain() - .range_txids_by_height(TxHeight::Unconfirmed..) - .map(|(_, txid)| *txid) - .collect::>(); - - txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| { - eprintln!("Checking if {} is confirmed yet", txid); - })); - } - - let local_chain = tracker.chain().checkpoints().clone(); - // drop lock on tracker - drop(tracker); - - // we scan the spks **without** a lock on the tracker - ElectrumUpdate { - chain_update: client - .scan_without_keychain( - &local_chain, - spks, - txids, - outpoints, - scan_options.batch_size, - ) - .context("scanning the blockchain")?, - ..Default::default() - } - } - }; - - let missing_txids = response.missing_full_txs(&*tracker.lock().unwrap()); - - // fetch the missing full transactions **without** a lock on the tracker - let new_txs = client - .batch_transaction_get(missing_txids) - .context("fetching full transactions")?; - - { - // Get a final short lock to apply the changes - let mut tracker = tracker.lock().unwrap(); - let changeset = { - let scan = response.into_keychain_scan(new_txs, &*tracker)?; - tracker.determine_changeset(&scan)? - }; - db.lock().unwrap().append_changeset(&changeset)?; - tracker.apply_changeset(changeset); - }; - - Ok(()) -} diff --git a/example-crates/keychain_tracker_esplora/.gitignore b/example-crates/keychain_tracker_esplora/.gitignore new file mode 100644 index 00000000..8359723a --- /dev/null +++ b/example-crates/keychain_tracker_esplora/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +.bdk_example_db diff --git a/example-crates/keychain_tracker_esplora/Cargo.toml b/example-crates/keychain_tracker_esplora/Cargo.toml new file mode 100644 index 00000000..57e9d9c0 --- /dev/null +++ b/example-crates/keychain_tracker_esplora/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "keychain_tracker_esplora_example" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bdk_chain = { path = "../../crates/chain", version = "0.3", features = ["serde", "miniscript"] } +bdk_esplora = { path = "../../crates/esplora" } +keychain_tracker_example_cli = { path = "../keychain_tracker_example_cli" } diff --git a/example-crates/keychain_tracker_esplora/src/main.rs b/example-crates/keychain_tracker_esplora/src/main.rs new file mode 100644 index 00000000..fba4a3b4 --- /dev/null +++ b/example-crates/keychain_tracker_esplora/src/main.rs @@ -0,0 +1,241 @@ +use bdk_chain::bitcoin::{Address, OutPoint, Txid}; +use bdk_chain::{bitcoin::Network, TxHeight}; +use bdk_esplora::esplora_client; +use bdk_esplora::EsploraExt; + +use std::io::{self, Write}; + +use keychain_tracker_example_cli::{ + self as cli, + anyhow::{self, Context}, + clap::{self, Parser, Subcommand}, +}; + +#[derive(Subcommand, Debug, Clone)] +enum EsploraCommands { + /// Scans the addresses in the wallet using esplora API. + Scan { + /// When a gap this large has been found for a keychain it will stop. + #[clap(long, default_value = "5")] + stop_gap: usize, + + #[clap(flatten)] + scan_options: ScanOptions, + }, + /// Scans particular addresses using esplora API + Sync { + /// Scan all the unused addresses + #[clap(long)] + unused_spks: bool, + /// Scan every address that you have derived + #[clap(long)] + all_spks: bool, + /// Scan unspent outpoints for spends or changes to confirmation status of residing tx + #[clap(long)] + utxos: bool, + /// Scan unconfirmed transactions for updates + #[clap(long)] + unconfirmed: bool, + + #[clap(flatten)] + scan_options: ScanOptions, + }, +} + +#[derive(Parser, Debug, Clone, PartialEq)] +pub struct ScanOptions { + #[clap(long, default_value = "5")] + pub parallel_requests: usize, +} + +fn main() -> anyhow::Result<()> { + let (args, keymap, keychain_tracker, db) = cli::init::()?; + let esplora_url = match args.network { + Network::Bitcoin => "https://mempool.space/api", + Network::Testnet => "https://mempool.space/testnet/api", + Network::Regtest => "http://localhost:3002", + Network::Signet => "https://mempool.space/signet/api", + }; + + let client = esplora_client::Builder::new(esplora_url).build_blocking()?; + + let esplora_cmd = match args.command { + cli::Commands::ChainSpecific(esplora_cmd) => esplora_cmd, + general_command => { + return cli::handle_commands( + general_command, + |transaction| Ok(client.broadcast(transaction)?), + &keychain_tracker, + &db, + args.network, + &keymap, + ) + } + }; + + match esplora_cmd { + EsploraCommands::Scan { + stop_gap, + scan_options, + } => { + let (spk_iterators, local_chain) = { + // Get a short lock on the tracker to get the spks iterators + // and local chain state + let tracker = &*keychain_tracker.lock().unwrap(); + let spk_iterators = tracker + .txout_index + .spks_of_all_keychains() + .into_iter() + .map(|(keychain, iter)| { + let mut first = true; + ( + keychain, + iter.inspect(move |(i, _)| { + if first { + eprint!("\nscanning {}: ", keychain); + first = false; + } + + eprint!("{} ", i); + let _ = io::stdout().flush(); + }), + ) + }) + .collect(); + + let local_chain = tracker.chain().checkpoints().clone(); + (spk_iterators, local_chain) + }; + + // we scan the iterators **without** a lock on the tracker + let wallet_scan = client + .scan( + &local_chain, + spk_iterators, + core::iter::empty(), + core::iter::empty(), + stop_gap, + scan_options.parallel_requests, + ) + .context("scanning the blockchain")?; + eprintln!(); + + { + // we take a short lock to apply results to tracker and db + let tracker = &mut *keychain_tracker.lock().unwrap(); + let db = &mut *db.lock().unwrap(); + let changeset = tracker.apply_update(wallet_scan)?; + db.append_changeset(&changeset)?; + } + } + EsploraCommands::Sync { + mut unused_spks, + mut utxos, + mut unconfirmed, + all_spks, + scan_options, + } => { + // Get a short lock on the tracker to get the spks we're interested in + let tracker = keychain_tracker.lock().unwrap(); + + if !(all_spks || unused_spks || utxos || unconfirmed) { + unused_spks = true; + unconfirmed = true; + utxos = true; + } else if all_spks { + unused_spks = false; + } + + let mut spks: Box> = + Box::new(core::iter::empty()); + if all_spks { + let all_spks = tracker + .txout_index + .all_spks() + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + spks = Box::new(spks.chain(all_spks.into_iter().map(|(index, script)| { + eprintln!("scanning {:?}", index); + script + }))); + } + if unused_spks { + let unused_spks = tracker + .txout_index + .unused_spks(..) + .map(|(k, v)| (k.clone(), v.clone())) + .collect::>(); + spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| { + eprintln!( + "Checking if address {} {:?} has been used", + Address::from_script(&script, args.network).unwrap(), + index + ); + + script + }))); + } + + let mut outpoints: Box> = Box::new(core::iter::empty()); + + if utxos { + let utxos = tracker + .full_utxos() + .map(|(_, utxo)| utxo) + .collect::>(); + outpoints = Box::new( + utxos + .into_iter() + .inspect(|utxo| { + eprintln!( + "Checking if outpoint {} (value: {}) has been spent", + utxo.outpoint, utxo.txout.value + ); + }) + .map(|utxo| utxo.outpoint), + ); + }; + + let mut txids: Box> = Box::new(core::iter::empty()); + + if unconfirmed { + let unconfirmed_txids = tracker + .chain() + .range_txids_by_height(TxHeight::Unconfirmed..) + .map(|(_, txid)| *txid) + .collect::>(); + + txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| { + eprintln!("Checking if {} is confirmed yet", txid); + })); + } + + let local_chain = tracker.chain().checkpoints().clone(); + + // drop lock on tracker + drop(tracker); + + // we scan the desired spks **without** a lock on the tracker + let scan = client + .scan_without_keychain( + &local_chain, + spks, + txids, + outpoints, + scan_options.parallel_requests, + ) + .context("scanning the blockchain")?; + + { + // we take a short lock to apply the results to the tracker and db + let tracker = &mut *keychain_tracker.lock().unwrap(); + let changeset = tracker.apply_update(scan.into())?; + let db = &mut *db.lock().unwrap(); + db.append_changeset(&changeset)?; + } + } + } + + Ok(()) +} diff --git a/example-crates/keychain_tracker_esplora_example/.gitignore b/example-crates/keychain_tracker_esplora_example/.gitignore deleted file mode 100644 index 8359723a..00000000 --- a/example-crates/keychain_tracker_esplora_example/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -Cargo.lock -.bdk_example_db diff --git a/example-crates/keychain_tracker_esplora_example/Cargo.toml b/example-crates/keychain_tracker_esplora_example/Cargo.toml deleted file mode 100644 index 57e9d9c0..00000000 --- a/example-crates/keychain_tracker_esplora_example/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "keychain_tracker_esplora_example" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bdk_chain = { path = "../../crates/chain", version = "0.3", features = ["serde", "miniscript"] } -bdk_esplora = { path = "../../crates/esplora" } -keychain_tracker_example_cli = { path = "../keychain_tracker_example_cli" } diff --git a/example-crates/keychain_tracker_esplora_example/src/main.rs b/example-crates/keychain_tracker_esplora_example/src/main.rs deleted file mode 100644 index fba4a3b4..00000000 --- a/example-crates/keychain_tracker_esplora_example/src/main.rs +++ /dev/null @@ -1,241 +0,0 @@ -use bdk_chain::bitcoin::{Address, OutPoint, Txid}; -use bdk_chain::{bitcoin::Network, TxHeight}; -use bdk_esplora::esplora_client; -use bdk_esplora::EsploraExt; - -use std::io::{self, Write}; - -use keychain_tracker_example_cli::{ - self as cli, - anyhow::{self, Context}, - clap::{self, Parser, Subcommand}, -}; - -#[derive(Subcommand, Debug, Clone)] -enum EsploraCommands { - /// Scans the addresses in the wallet using esplora API. - Scan { - /// When a gap this large has been found for a keychain it will stop. - #[clap(long, default_value = "5")] - stop_gap: usize, - - #[clap(flatten)] - scan_options: ScanOptions, - }, - /// Scans particular addresses using esplora API - Sync { - /// Scan all the unused addresses - #[clap(long)] - unused_spks: bool, - /// Scan every address that you have derived - #[clap(long)] - all_spks: bool, - /// Scan unspent outpoints for spends or changes to confirmation status of residing tx - #[clap(long)] - utxos: bool, - /// Scan unconfirmed transactions for updates - #[clap(long)] - unconfirmed: bool, - - #[clap(flatten)] - scan_options: ScanOptions, - }, -} - -#[derive(Parser, Debug, Clone, PartialEq)] -pub struct ScanOptions { - #[clap(long, default_value = "5")] - pub parallel_requests: usize, -} - -fn main() -> anyhow::Result<()> { - let (args, keymap, keychain_tracker, db) = cli::init::()?; - let esplora_url = match args.network { - Network::Bitcoin => "https://mempool.space/api", - Network::Testnet => "https://mempool.space/testnet/api", - Network::Regtest => "http://localhost:3002", - Network::Signet => "https://mempool.space/signet/api", - }; - - let client = esplora_client::Builder::new(esplora_url).build_blocking()?; - - let esplora_cmd = match args.command { - cli::Commands::ChainSpecific(esplora_cmd) => esplora_cmd, - general_command => { - return cli::handle_commands( - general_command, - |transaction| Ok(client.broadcast(transaction)?), - &keychain_tracker, - &db, - args.network, - &keymap, - ) - } - }; - - match esplora_cmd { - EsploraCommands::Scan { - stop_gap, - scan_options, - } => { - let (spk_iterators, local_chain) = { - // Get a short lock on the tracker to get the spks iterators - // and local chain state - let tracker = &*keychain_tracker.lock().unwrap(); - let spk_iterators = tracker - .txout_index - .spks_of_all_keychains() - .into_iter() - .map(|(keychain, iter)| { - let mut first = true; - ( - keychain, - iter.inspect(move |(i, _)| { - if first { - eprint!("\nscanning {}: ", keychain); - first = false; - } - - eprint!("{} ", i); - let _ = io::stdout().flush(); - }), - ) - }) - .collect(); - - let local_chain = tracker.chain().checkpoints().clone(); - (spk_iterators, local_chain) - }; - - // we scan the iterators **without** a lock on the tracker - let wallet_scan = client - .scan( - &local_chain, - spk_iterators, - core::iter::empty(), - core::iter::empty(), - stop_gap, - scan_options.parallel_requests, - ) - .context("scanning the blockchain")?; - eprintln!(); - - { - // we take a short lock to apply results to tracker and db - let tracker = &mut *keychain_tracker.lock().unwrap(); - let db = &mut *db.lock().unwrap(); - let changeset = tracker.apply_update(wallet_scan)?; - db.append_changeset(&changeset)?; - } - } - EsploraCommands::Sync { - mut unused_spks, - mut utxos, - mut unconfirmed, - all_spks, - scan_options, - } => { - // Get a short lock on the tracker to get the spks we're interested in - let tracker = keychain_tracker.lock().unwrap(); - - if !(all_spks || unused_spks || utxos || unconfirmed) { - unused_spks = true; - unconfirmed = true; - utxos = true; - } else if all_spks { - unused_spks = false; - } - - let mut spks: Box> = - Box::new(core::iter::empty()); - if all_spks { - let all_spks = tracker - .txout_index - .all_spks() - .iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - spks = Box::new(spks.chain(all_spks.into_iter().map(|(index, script)| { - eprintln!("scanning {:?}", index); - script - }))); - } - if unused_spks { - let unused_spks = tracker - .txout_index - .unused_spks(..) - .map(|(k, v)| (k.clone(), v.clone())) - .collect::>(); - spks = Box::new(spks.chain(unused_spks.into_iter().map(|(index, script)| { - eprintln!( - "Checking if address {} {:?} has been used", - Address::from_script(&script, args.network).unwrap(), - index - ); - - script - }))); - } - - let mut outpoints: Box> = Box::new(core::iter::empty()); - - if utxos { - let utxos = tracker - .full_utxos() - .map(|(_, utxo)| utxo) - .collect::>(); - outpoints = Box::new( - utxos - .into_iter() - .inspect(|utxo| { - eprintln!( - "Checking if outpoint {} (value: {}) has been spent", - utxo.outpoint, utxo.txout.value - ); - }) - .map(|utxo| utxo.outpoint), - ); - }; - - let mut txids: Box> = Box::new(core::iter::empty()); - - if unconfirmed { - let unconfirmed_txids = tracker - .chain() - .range_txids_by_height(TxHeight::Unconfirmed..) - .map(|(_, txid)| *txid) - .collect::>(); - - txids = Box::new(unconfirmed_txids.into_iter().inspect(|txid| { - eprintln!("Checking if {} is confirmed yet", txid); - })); - } - - let local_chain = tracker.chain().checkpoints().clone(); - - // drop lock on tracker - drop(tracker); - - // we scan the desired spks **without** a lock on the tracker - let scan = client - .scan_without_keychain( - &local_chain, - spks, - txids, - outpoints, - scan_options.parallel_requests, - ) - .context("scanning the blockchain")?; - - { - // we take a short lock to apply the results to the tracker and db - let tracker = &mut *keychain_tracker.lock().unwrap(); - let changeset = tracker.apply_update(scan.into())?; - let db = &mut *db.lock().unwrap(); - db.append_changeset(&changeset)?; - } - } - } - - Ok(()) -} diff --git a/example-crates/wallet_electrum/Cargo.toml b/example-crates/wallet_electrum/Cargo.toml new file mode 100644 index 00000000..0415d227 --- /dev/null +++ b/example-crates/wallet_electrum/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wallet_electrum_example" +version = "0.1.0" +edition = "2021" + +[dependencies] +bdk = { path = "../../crates/bdk" } diff --git a/example-crates/wallet_electrum/src/main.rs b/example-crates/wallet_electrum/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/example-crates/wallet_electrum/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/example-crates/wallet_esplora/Cargo.toml b/example-crates/wallet_esplora/Cargo.toml new file mode 100644 index 00000000..944f09b3 --- /dev/null +++ b/example-crates/wallet_esplora/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bdk-esplora-wallet-example" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bdk = { path = "../../crates/bdk" } diff --git a/example-crates/wallet_esplora/src/main.rs b/example-crates/wallet_esplora/src/main.rs new file mode 100644 index 00000000..e7a11a96 --- /dev/null +++ b/example-crates/wallet_esplora/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}