diff --git a/Cargo.toml b/Cargo.toml index f76134b38..048cacc6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "example-crates/wallet_electrum", "example-crates/wallet_esplora", "example-crates/wallet_esplora_async", + "example-crates/example_cbf", "nursery/tmp_plan", "nursery/coin_select" ] diff --git a/crates/bdk_cbf/src/lib.rs b/crates/bdk_cbf/src/lib.rs index 476178acc..64841bce3 100644 --- a/crates/bdk_cbf/src/lib.rs +++ b/crates/bdk_cbf/src/lib.rs @@ -1,14 +1,15 @@ use std::{net, thread}; +use core::fmt::Debug; use bdk_chain::keychain::DerivationAdditions; use nakamoto::client::network::Services; use nakamoto::client::Handle; use nakamoto::client::traits::Handle as HandleTrait; use nakamoto::client::{chan, Client, Config, Error, Event}; -use nakamoto::common::block::Height; use nakamoto::net::poll; pub use nakamoto::client::network::Network; +pub use nakamoto::common::block::Height; use bdk_chain::{ bitcoin::{Script, Transaction}, @@ -18,10 +19,13 @@ use bdk_chain::{ BlockId, ChainOracle, ConfirmationHeightAnchor, TxGraph, }; -use core::fmt::Debug; - type Reactor = poll::Reactor; +#[derive(Clone)] +pub struct CBFClient { + handle: Handle, +} + impl ChainOracle for CBFClient { type Error = nakamoto::client::Error; @@ -56,11 +60,6 @@ impl ChainOracle for CBFClient { } } -#[derive(Clone)] -pub struct CBFClient { - handle: Handle, -} - #[derive(Debug, Clone)] pub enum CBFUpdate { Synced { @@ -97,7 +96,11 @@ impl Iterator for CBFUpdateIterator { } impl CBFClient { - pub fn start_client(cfg: Config, peer_count: usize) -> Result { + pub fn start_client(network: Network, peer_count: usize) -> Result { + let cfg = Config { + network, + ..Default::default() + }; let client = Client::::new()?; let handle = client.handle(); @@ -112,7 +115,7 @@ impl CBFClient { Ok(Self { handle }) } - //create a function to watch + /// Given a list of scripts, start scanning the chain from the given height. pub fn start_scanning( &self, start_height: Height, @@ -123,7 +126,7 @@ impl CBFClient { Ok(()) } - // Watch for Block events that match the scripts we're interested in + /// Listen for nakamoto events that are relevant to scripts we are watching. pub fn watch_events(&self) -> Result { let events_chan = self.handle.events(); loop { @@ -158,7 +161,7 @@ impl CBFClient { } } - // Turns a CBFUpdate into a TxGraph update + /// Given a list of tuples of block and their transactions, create a TxGraph update. pub fn into_tx_graph_update( &self, block_txs: Vec<(BlockId, Vec)>, @@ -201,7 +204,7 @@ impl CBFClient { while let Some(keychains) = Self::check_stop_gap(stop_gap, &empty_scripts_counter) { keychains.iter().for_each(|k| { - /*let (_, _) =*/ indexed_tx_graph.index.set_lookahead(k, watch_per_keychain); + indexed_tx_graph.index.set_lookahead(k, watch_per_keychain); }); let mut spk_watchlist = BTreeMap::>::new(); @@ -244,7 +247,7 @@ impl CBFClient { for (k, scripts) in spk_watchlist.iter() { for script in scripts { let counter = empty_scripts_counter.get_mut(k).unwrap(); - if Self::is_script_in_udpate(script.clone(), &updates) { + if Self::is_script_in_udpates(script.clone(), &updates) { *counter = 0; } else { *counter += 1; @@ -262,7 +265,7 @@ impl CBFClient { Ok(additions) } - fn is_script_in_udpate(script: Script, updates: &Vec<(BlockId, Vec)>) -> bool { + fn is_script_in_udpates(script: Script, updates: &Vec<(BlockId, Vec)>) -> bool { for update in updates { for tx in update.1.iter() { for output in tx.output.iter() { diff --git a/example-crates/example_cbf/Cargo.toml b/example-crates/example_cbf/Cargo.toml index 1ed622fdb..fda610c6d 100644 --- a/example-crates/example_cbf/Cargo.toml +++ b/example-crates/example_cbf/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] bdk_cbf = { path = "../../crates/bdk_cbf"} bdk_chain = { path = "../../crates/chain"} -bdk_cli = { path = "../example_cli" } +example_cli = { path = "../example_cli" } diff --git a/example-crates/example_cbf/src/main.rs b/example-crates/example_cbf/src/main.rs new file mode 100644 index 000000000..4d1db9d2c --- /dev/null +++ b/example-crates/example_cbf/src/main.rs @@ -0,0 +1,99 @@ +use std::sync::Mutex; + +use bdk_cbf::{CBFClient, Network}; +use bdk_chain::{keychain::LocalChangeSet, ConfirmationHeightAnchor, IndexedTxGraph}; +use example_cli::{ + anyhow, + clap::{self, Args, Subcommand}, + Keychain, +}; + +const DB_MAGIC: &[u8] = b"bdk_example_cbf"; +const DB_PATH: &str = ".bdk_example_cbf.db"; + +type ChangeSet = LocalChangeSet; + +#[derive(Debug, Clone, Args)] +struct CBFArgs {} + +#[derive(Subcommand, Debug, Clone)] +enum CBFCommands { + Scan { + /// The block height to start scanning from + #[clap(long, default_value = "0")] + start_height: u64, + /// The block height to stop scanning at + #[clap(long, default_value = "5")] + stop_gap: u32, + /// Number of scripts to watch for every sync + #[clap(long, default_value = "1000")] + watchlist_size: u32, + }, +} + +fn main() -> anyhow::Result<()> { + let (args, keymap, index, db, init_changeset) = + example_cli::init::(DB_MAGIC, DB_PATH)?; + + let graph = Mutex::new({ + let mut graph = IndexedTxGraph::new(index); + graph.apply_additions(init_changeset.indexed_additions); + graph + }); + + let client = Mutex::new({ + let client = CBFClient::start_client(Network::Testnet, 1)?; + client + }); + + let cbf_cmd = match args.command { + example_cli::Commands::ChainSpecific(cbf_cmd) => cbf_cmd, + general_cmd => { + let res = example_cli::handle_commands( + &graph, + &db, + &client, + &keymap, + args.network, + |tx| { + client + .lock() + .unwrap() + .submit_transaction(tx.clone()) + .map_err(anyhow::Error::from) + }, + general_cmd, + ); + db.lock().unwrap().commit()?; + return res; + } + }; + + match cbf_cmd { + CBFCommands::Scan { + start_height, + stop_gap, + watchlist_size, + } => { + println!("Scanning from height {} to {}", start_height, stop_gap); + let indexed_additions = { + let mut graph = graph.lock().unwrap(); + client + .lock() + .unwrap() + .scan(watchlist_size, start_height, &mut graph, stop_gap)? + }; + + let curr_changeset = LocalChangeSet::from(indexed_additions); + + // stage changes to the database + let mut db = db.lock().unwrap(); + db.stage(curr_changeset); + db.commit()?; + + println!("commited to database!"); + } + } + + Ok(()) +}