Skip to content

Commit bbbfb13

Browse files
feat: create CBF example with example_cli
1 parent 5dd5d3d commit bbbfb13

File tree

4 files changed

+119
-16
lines changed

4 files changed

+119
-16
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ members = [
1111
"example-crates/wallet_electrum",
1212
"example-crates/wallet_esplora",
1313
"example-crates/wallet_esplora_async",
14+
"example-crates/example_cbf",
1415
"nursery/tmp_plan",
1516
"nursery/coin_select"
1617
]

crates/bdk_cbf/src/lib.rs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use std::{net, thread};
2+
use core::fmt::Debug;
23

34
use bdk_chain::keychain::DerivationAdditions;
45
use nakamoto::client::network::Services;
56
use nakamoto::client::Handle;
67
use nakamoto::client::traits::Handle as HandleTrait;
78
use nakamoto::client::{chan, Client, Config, Error, Event};
8-
use nakamoto::common::block::Height;
99
use nakamoto::net::poll;
1010

1111
pub use nakamoto::client::network::Network;
12+
pub use nakamoto::common::block::Height;
1213

1314
use bdk_chain::{
1415
bitcoin::{Script, Transaction},
@@ -18,10 +19,13 @@ use bdk_chain::{
1819
BlockId, ChainOracle, ConfirmationHeightAnchor, TxGraph,
1920
};
2021

21-
use core::fmt::Debug;
22-
2322
type Reactor = poll::Reactor<net::TcpStream>;
2423

24+
#[derive(Clone)]
25+
pub struct CBFClient {
26+
handle: Handle<poll::reactor::Waker>,
27+
}
28+
2529
impl ChainOracle for CBFClient {
2630
type Error = nakamoto::client::Error;
2731

@@ -56,11 +60,6 @@ impl ChainOracle for CBFClient {
5660
}
5761
}
5862

59-
#[derive(Clone)]
60-
pub struct CBFClient {
61-
handle: Handle<poll::reactor::Waker>,
62-
}
63-
6463
#[derive(Debug, Clone)]
6564
pub enum CBFUpdate {
6665
Synced {
@@ -97,7 +96,11 @@ impl Iterator for CBFUpdateIterator {
9796
}
9897

9998
impl CBFClient {
100-
pub fn start_client(cfg: Config, peer_count: usize) -> Result<Self, Error> {
99+
pub fn start_client(network: Network, peer_count: usize) -> Result<Self, Error> {
100+
let cfg = Config {
101+
network,
102+
..Default::default()
103+
};
101104
let client = Client::<Reactor>::new()?;
102105
let handle = client.handle();
103106

@@ -112,7 +115,7 @@ impl CBFClient {
112115
Ok(Self { handle })
113116
}
114117

115-
//create a function to watch
118+
/// Given a list of scripts, start scanning the chain from the given height.
116119
pub fn start_scanning(
117120
&self,
118121
start_height: Height,
@@ -123,7 +126,7 @@ impl CBFClient {
123126
Ok(())
124127
}
125128

126-
// Watch for Block events that match the scripts we're interested in
129+
/// Listen for nakamoto events that are relevant to scripts we are watching.
127130
pub fn watch_events(&self) -> Result<CBFUpdate, Error> {
128131
let events_chan = self.handle.events();
129132
loop {
@@ -158,7 +161,7 @@ impl CBFClient {
158161
}
159162
}
160163

161-
// Turns a CBFUpdate into a TxGraph update
164+
/// Given a list of tuples of block and their transactions, create a TxGraph update.
162165
pub fn into_tx_graph_update(
163166
&self,
164167
block_txs: Vec<(BlockId, Vec<Transaction>)>,
@@ -201,7 +204,7 @@ impl CBFClient {
201204

202205
while let Some(keychains) = Self::check_stop_gap(stop_gap, &empty_scripts_counter) {
203206
keychains.iter().for_each(|k| {
204-
/*let (_, _) =*/ indexed_tx_graph.index.set_lookahead(k, watch_per_keychain);
207+
indexed_tx_graph.index.set_lookahead(k, watch_per_keychain);
205208
});
206209

207210
let mut spk_watchlist = BTreeMap::<K, Vec<Script>>::new();
@@ -244,7 +247,7 @@ impl CBFClient {
244247
for (k, scripts) in spk_watchlist.iter() {
245248
for script in scripts {
246249
let counter = empty_scripts_counter.get_mut(k).unwrap();
247-
if Self::is_script_in_udpate(script.clone(), &updates) {
250+
if Self::is_script_in_udpates(script.clone(), &updates) {
248251
*counter = 0;
249252
} else {
250253
*counter += 1;
@@ -262,7 +265,7 @@ impl CBFClient {
262265
Ok(additions)
263266
}
264267

265-
fn is_script_in_udpate(script: Script, updates: &Vec<(BlockId, Vec<Transaction>)>) -> bool {
268+
fn is_script_in_udpates(script: Script, updates: &Vec<(BlockId, Vec<Transaction>)>) -> bool {
266269
for update in updates {
267270
for tx in update.1.iter() {
268271
for output in tx.output.iter() {

example-crates/example_cbf/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ edition = "2021"
88
[dependencies]
99
bdk_cbf = { path = "../../crates/bdk_cbf"}
1010
bdk_chain = { path = "../../crates/chain"}
11-
bdk_cli = { path = "../example_cli" }
11+
example_cli = { path = "../example_cli" }
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use std::sync::Mutex;
2+
3+
use bdk_cbf::{CBFClient, Network};
4+
use bdk_chain::{keychain::LocalChangeSet, ConfirmationHeightAnchor, IndexedTxGraph};
5+
use example_cli::{
6+
anyhow,
7+
clap::{self, Args, Subcommand},
8+
Keychain,
9+
};
10+
11+
const DB_MAGIC: &[u8] = b"bdk_example_cbf";
12+
const DB_PATH: &str = ".bdk_example_cbf.db";
13+
14+
type ChangeSet = LocalChangeSet<Keychain, ConfirmationHeightAnchor>;
15+
16+
#[derive(Debug, Clone, Args)]
17+
struct CBFArgs {}
18+
19+
#[derive(Subcommand, Debug, Clone)]
20+
enum CBFCommands {
21+
Scan {
22+
/// The block height to start scanning from
23+
#[clap(long, default_value = "0")]
24+
start_height: u64,
25+
/// The block height to stop scanning at
26+
#[clap(long, default_value = "5")]
27+
stop_gap: u32,
28+
/// Number of scripts to watch for every sync
29+
#[clap(long, default_value = "1000")]
30+
watchlist_size: u32,
31+
},
32+
}
33+
34+
fn main() -> anyhow::Result<()> {
35+
let (args, keymap, index, db, init_changeset) =
36+
example_cli::init::<CBFCommands, ChangeSet>(DB_MAGIC, DB_PATH)?;
37+
38+
let graph = Mutex::new({
39+
let mut graph = IndexedTxGraph::new(index);
40+
graph.apply_additions(init_changeset.indexed_additions);
41+
graph
42+
});
43+
44+
let client = Mutex::new({
45+
let client = CBFClient::start_client(Network::Testnet, 1)?;
46+
client
47+
});
48+
49+
let cbf_cmd = match args.command {
50+
example_cli::Commands::ChainSpecific(cbf_cmd) => cbf_cmd,
51+
general_cmd => {
52+
let res = example_cli::handle_commands(
53+
&graph,
54+
&db,
55+
&client,
56+
&keymap,
57+
args.network,
58+
|tx| {
59+
client
60+
.lock()
61+
.unwrap()
62+
.submit_transaction(tx.clone())
63+
.map_err(anyhow::Error::from)
64+
},
65+
general_cmd,
66+
);
67+
db.lock().unwrap().commit()?;
68+
return res;
69+
}
70+
};
71+
72+
match cbf_cmd {
73+
CBFCommands::Scan {
74+
start_height,
75+
stop_gap,
76+
watchlist_size,
77+
} => {
78+
println!("Scanning from height {} to {}", start_height, stop_gap);
79+
let indexed_additions = {
80+
let mut graph = graph.lock().unwrap();
81+
client
82+
.lock()
83+
.unwrap()
84+
.scan(watchlist_size, start_height, &mut graph, stop_gap)?
85+
};
86+
87+
let curr_changeset = LocalChangeSet::from(indexed_additions);
88+
89+
// stage changes to the database
90+
let mut db = db.lock().unwrap();
91+
db.stage(curr_changeset);
92+
db.commit()?;
93+
94+
println!("commited to database!");
95+
}
96+
}
97+
98+
Ok(())
99+
}

0 commit comments

Comments
 (0)