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 3, 2023
1 parent 4167445 commit d4d897a
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 19 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
38 changes: 20 additions & 18 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::traits::Handle as HandleTrait;
use nakamoto::client::Handle;
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 @@ -98,7 +97,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 @@ -111,7 +114,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 @@ -121,7 +124,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 @@ -153,7 +156,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 All @@ -178,15 +181,14 @@ impl CBFClient {

pub fn sync<K>(
&self,
lookahead: u32,
watch_per_keychain: u32,
start_height: Height,
indexed_tx_graph: &mut IndexedTxGraph<ConfirmationHeightAnchor, KeychainTxOutIndex<K>>,
stop_gap: u32,
) -> Result<IndexedAdditions<ConfirmationHeightAnchor, DerivationAdditions<K>>, Error>
where
K: Ord + Clone + Debug,
{
indexed_tx_graph.index.set_lookahead_for_all(lookahead);
let mut keychain_spks = indexed_tx_graph.index.spks_of_all_keychains();
let mut empty_scripts_counter = BTreeMap::<K, u32>::new();
keychain_spks.keys().for_each(|k| {
Expand All @@ -197,12 +199,12 @@ impl CBFClient {

while let Some(keychains) = Self::check_stop_gap(stop_gap, &empty_scripts_counter) {
keychains.iter().for_each(|k| {
indexed_tx_graph.index.set_lookahead(k, lookahead);
indexed_tx_graph.index.set_lookahead(k, watch_per_keychain);
});

let mut spk_watchlist = BTreeMap::<K, Vec<Script>>::new();
for (k, script_iter) in keychain_spks.iter_mut() {
(0..lookahead).for_each(|_| {
(0..watch_per_keychain).for_each(|_| {
if let Some((_, script)) = script_iter.next() {
let spks = spk_watchlist.entry(k.clone()).or_insert(vec![]);
spks.push(script);
Expand Down Expand Up @@ -241,7 +243,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 @@ -257,7 +259,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" }
92 changes: 92 additions & 0 deletions example-crates/example_cbf/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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 {
Sync {
/// 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,
|_| Err(anyhow::anyhow!("use `tx` instead")),
general_cmd,
);
db.lock().unwrap().commit()?;
return res;
}
};

match cbf_cmd {
CBFCommands::Sync {
start_height,
stop_gap,
watchlist_size,
} => {
let indexed_additions = {
let mut graph = graph.lock().unwrap();
client
.lock()
.unwrap()
.sync(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 d4d897a

Please sign in to comment.