use std::{collections::BTreeMap, path::PathBuf, time::Duration};
use bdk_wallet::{chain::IndexedTxGraph, Wallet};
use kyoto::NodeBuilder;
pub use kyoto::{
db::error::SqlInitializationError, AddrV2, HeaderCheckpoint, ScriptBuf, ServiceFlags,
TrustedPeer,
};
use crate::{LightClient, LogSubscriber, UpdateSubscriber, WalletExt, WarningSubscriber};
const RECOMMENDED_PEERS: u8 = 2;
#[derive(Debug)]
pub struct LightClientBuilder {
peers: Option<Vec<TrustedPeer>>,
connections: Option<u8>,
birthday_height: Option<u32>,
data_dir: Option<PathBuf>,
timeout: Option<Duration>,
}
impl LightClientBuilder {
pub fn new() -> Self {
Self {
peers: None,
connections: None,
birthday_height: None,
data_dir: None,
timeout: None,
}
}
pub fn peers(mut self, peers: Vec<TrustedPeer>) -> Self {
self.peers = Some(peers);
self
}
pub fn connections(mut self, num_connections: u8) -> Self {
self.connections = Some(num_connections);
self
}
pub fn data_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.data_dir = Some(dir.into());
self
}
pub fn scan_after(mut self, height: u32) -> Self {
self.birthday_height = Some(height);
self
}
pub fn timeout_duration(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn build(self, wallet: &Wallet) -> Result<LightClient, SqlInitializationError> {
let network = wallet.network();
let mut node_builder = NodeBuilder::new(network);
if let Some(whitelist) = self.peers {
node_builder = node_builder.add_peers(whitelist);
}
match self.birthday_height {
Some(birthday) => {
if birthday < wallet.local_chain().tip().height() {
let block_id = wallet.local_chain().tip();
let header_cp = HeaderCheckpoint::new(block_id.height(), block_id.hash());
node_builder = node_builder.anchor_checkpoint(header_cp)
} else {
let cp = HeaderCheckpoint::closest_checkpoint_below_height(birthday, network);
node_builder = node_builder.anchor_checkpoint(cp)
}
}
None => {
let block_id = wallet.local_chain().tip();
if block_id.height() > 0 {
let header_cp = HeaderCheckpoint::new(block_id.height(), block_id.hash());
node_builder = node_builder.anchor_checkpoint(header_cp)
}
}
}
if let Some(dir) = self.data_dir {
node_builder = node_builder.add_data_dir(dir);
}
if let Some(duration) = self.timeout {
node_builder = node_builder.set_response_timeout(duration)
}
node_builder =
node_builder.num_required_peers(self.connections.unwrap_or(RECOMMENDED_PEERS));
let (node, kyoto_client) = node_builder
.add_scripts(wallet.peek_revealed_plus_lookahead().collect())
.build_node()?;
let kyoto::Client {
requester,
log_rx,
warn_rx,
event_rx,
} = kyoto_client;
let indexed_graph = IndexedTxGraph::new(wallet.spk_index().clone());
let update_subscriber = UpdateSubscriber {
receiver: event_rx,
chain: wallet.local_chain().clone(),
graph: indexed_graph,
chain_changeset: BTreeMap::new(),
};
Ok(LightClient {
requester,
log_subscriber: LogSubscriber::new(log_rx),
warning_subscriber: WarningSubscriber::new(warn_rx),
update_subscriber,
node,
})
}
}
impl Default for LightClientBuilder {
fn default() -> Self {
Self::new()
}
}