Skip to content

Commit

Permalink
feat: create CBF example with example_cli
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirfomene committed Aug 7, 2023
1 parent 5dd5d3d commit bbbfb13
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
Expand Down
33 changes: 18 additions & 15 deletions crates/bdk_cbf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -18,10 +19,13 @@ use bdk_chain::{
BlockId, ChainOracle, ConfirmationHeightAnchor, TxGraph,
};

use core::fmt::Debug;

type Reactor = poll::Reactor<net::TcpStream>;

#[derive(Clone)]
pub struct CBFClient {
handle: Handle<poll::reactor::Waker>,
}

impl ChainOracle for CBFClient {
type Error = nakamoto::client::Error;

Expand Down Expand Up @@ -56,11 +60,6 @@ impl ChainOracle for CBFClient {
}
}

#[derive(Clone)]
pub struct CBFClient {
handle: Handle<poll::reactor::Waker>,
}

#[derive(Debug, Clone)]
pub enum CBFUpdate {
Synced {
Expand Down Expand Up @@ -97,7 +96,11 @@ impl Iterator for CBFUpdateIterator {
}

impl CBFClient {
pub fn start_client(cfg: Config, peer_count: usize) -> Result<Self, Error> {
pub fn start_client(network: Network, peer_count: usize) -> Result<Self, Error> {
let cfg = Config {
network,
..Default::default()
};
let client = Client::<Reactor>::new()?;
let handle = client.handle();

Expand All @@ -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,
Expand All @@ -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<CBFUpdate, Error> {
let events_chan = self.handle.events();
loop {
Expand Down Expand Up @@ -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<Transaction>)>,
Expand Down Expand Up @@ -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::<K, Vec<Script>>::new();
Expand Down Expand Up @@ -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;
Expand All @@ -262,7 +265,7 @@ impl CBFClient {
Ok(additions)
}

fn is_script_in_udpate(script: Script, updates: &Vec<(BlockId, Vec<Transaction>)>) -> bool {
fn is_script_in_udpates(script: Script, updates: &Vec<(BlockId, Vec<Transaction>)>) -> bool {
for update in updates {
for tx in update.1.iter() {
for output in tx.output.iter() {
Expand Down
2 changes: 1 addition & 1 deletion example-crates/example_cbf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
99 changes: 99 additions & 0 deletions example-crates/example_cbf/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<Keychain, ConfirmationHeightAnchor>;

#[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::<CBFCommands, ChangeSet>(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(())
}

0 comments on commit bbbfb13

Please sign in to comment.