diff --git a/Cargo.toml b/Cargo.toml index 7b1818a..4658131 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,20 +12,14 @@ edition = "2021" rust-version = "1.63.0" [dependencies] -bdk_chain = { version = "0.21.1" } -kyoto-cbf = { version = "0.6.0", default-features = false, features = ["dns"] } +bdk_wallet = { version = "1.0.0" } +kyoto-cbf = { version = "0.6.0", default-features = false, features = ["dns", "database"] } tracing = { version = "0.1", optional = true } tracing-subscriber = { version = "0.3", optional = true } -[dependencies.bdk_wallet] -version = "1.0.0" -optional = true - [features] -default = ["wallet", "rusqlite", "events", "callbacks", "trace"] +default = ["events", "callbacks", "trace"] trace = ["tracing", "tracing-subscriber"] -wallet = ["bdk_wallet"] -rusqlite = ["kyoto-cbf/database"] callbacks = [] events = [] @@ -39,13 +33,9 @@ tracing-subscriber = { version = "0.3" } [[example]] -name = "signet" -required-features = ["rusqlite", "callbacks"] - -[[example]] -name = "wallet" -required-features = ["wallet", "trace", "rusqlite", "callbacks"] +name = "callbacks" +required-features = ["trace", "callbacks"] [[example]] name = "events" -required-features = ["wallet", "rusqlite", "events"] +required-features = ["events"] diff --git a/examples/wallet.rs b/examples/callbacks.rs similarity index 100% rename from examples/wallet.rs rename to examples/callbacks.rs diff --git a/examples/signet.rs b/examples/signet.rs deleted file mode 100644 index c7212af..0000000 --- a/examples/signet.rs +++ /dev/null @@ -1,115 +0,0 @@ -use std::collections::HashSet; -use std::net::{IpAddr, Ipv4Addr}; -use std::str::FromStr; -use tokio::task; - -use bdk_chain::bitcoin::{ - constants::genesis_block, secp256k1::Secp256k1, Address, BlockHash, Network, ScriptBuf, -}; -use bdk_chain::{ - keychain_txout::KeychainTxOutIndex, local_chain::LocalChain, miniscript::Descriptor, FullTxOut, - IndexedTxGraph, SpkIterator, -}; -use bdk_kyoto::kyoto::{HeaderCheckpoint, NodeBuilder}; -use bdk_kyoto::logger::PrintLogger; -use bdk_kyoto::EventReceiver; - -const TARGET_INDEX: u32 = 20; - -/* Sync bdk chain and txgraph structures */ - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let secp = Secp256k1::new(); - - let desc = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/0/*)"; - let change_desc = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/1/*)"; - let (descriptor, _) = Descriptor::parse_descriptor(&secp, desc)?; - let (change_descriptor, _) = Descriptor::parse_descriptor(&secp, change_desc)?; - - let genesis_hash = genesis_block(Network::Signet).block_hash(); - let (mut chain, _) = LocalChain::from_genesis_hash(genesis_hash); - - let mut graph = IndexedTxGraph::new({ - let mut index = KeychainTxOutIndex::default(); - let _ = index.insert_descriptor("external", descriptor); - let _ = index.insert_descriptor("internal", change_descriptor); - index - }); - - let mut spks_to_watch: HashSet = HashSet::new(); - for (_k, desc) in graph.index.keychains() { - for (_i, spk) in SpkIterator::new_with_range(desc, 0..TARGET_INDEX) { - spks_to_watch.insert(spk); - } - } - - let peers = vec![ - IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), - IpAddr::V4(Ipv4Addr::new(170, 75, 163, 219)), - IpAddr::V4(Ipv4Addr::new(23, 137, 57, 100)), - ]; - - let builder = NodeBuilder::new(Network::Signet); - let (node, client) = builder - .add_peers(peers.into_iter().map(|ip| ip.into()).collect()) - .add_scripts(spks_to_watch) - .anchor_checkpoint(HeaderCheckpoint::new( - 205_000, - BlockHash::from_str( - "0000002bd0f82f8c0c0f1e19128f84c938763641dba85c44bdb6aed1678d16cb", - )?, - )) - .num_required_peers(2) - .build_node()?; - let (sender, receiver) = client.split(); - let mut event_receiver = EventReceiver::from_index(chain.tip(), &graph.index, receiver)?; - - // Run the node - if !node.is_running() { - task::spawn(async move { node.run().await }); - } - - // Sync and apply updates - let logger = PrintLogger::new(); - if let Some(update) = event_receiver.update(&logger).await { - let _ = chain.apply_update(update.chain_update.unwrap())?; - let _ = graph.apply_update(update.tx_update); - let _ = graph - .index - .reveal_to_target_multi(&update.last_active_indices); - } - - // Shutdown - sender.shutdown().await?; - - let cp = chain.tip(); - let index = &graph.index; - let outpoints = index.outpoints().clone(); - let unspent: Vec> = graph - .graph() - .filter_chain_unspents(&chain, cp.block_id(), outpoints) - .map(|(_, txout)| txout) - .collect(); - for utxo in unspent { - let addr = Address::from_script(utxo.txout.script_pubkey.as_script(), Network::Signet)?; - println!("Funded: {:?}", addr); - } - println!("Graph txs count: {}", graph.graph().full_txs().count()); - println!("Local tip: {} {}", cp.height(), cp.hash()); - println!( - "Balance: {:#?}", - graph.graph().balance( - &chain, - cp.block_id(), - index.outpoints().iter().cloned(), - |_, _| true - ) - ); - println!( - "Last revealed indices: {:#?}", - index.last_revealed_indices() - ); - - Ok(()) -} diff --git a/src/builder.rs b/src/builder.rs index 99270aa..60f3fdf 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -47,7 +47,7 @@ use std::{path::PathBuf, time::Duration}; -use bdk_chain::local_chain::MissingGenesisError; +use bdk_wallet::chain::local_chain::MissingGenesisError; use bdk_wallet::Wallet; use kyoto::NodeBuilder; pub use kyoto::{ @@ -157,8 +157,11 @@ impl LightClientBuilder { .add_scripts(wallet.peek_revealed_plus_lookahead().collect()) .build_node()?; let (sender, receiver) = kyoto_client.split(); - let event_receiver = - EventReceiver::from_index(wallet.local_chain().tip(), wallet.spk_index(), receiver)?; + let event_receiver = EventReceiver::from_index( + wallet.local_chain().tip(), + wallet.spk_index().clone(), + receiver, + )?; Ok(LightClient { sender, receiver: event_receiver, diff --git a/src/lib.rs b/src/lib.rs index 8feccc4..42e4f85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,110 +80,30 @@ //! } //! } //! ``` -//! -//! Custom wallet implementations may still take advantage of BDK-Kyoto, however building the -//! [`EventReceiver`] will involve configuring Kyoto directly. -//! -//! ```no_run -//! # const RECEIVE: &str = "tr([7d94197e/86'/1'/0']tpubDCyQVJj8KzjiQsFjmb3KwECVXPvMwvAxxZGCP9XmWSopmjW3bCV3wD7TgxrUhiGSueDS1MU5X1Vb1YjYcp8jitXc5fXfdC1z68hDDEyKRNr/0/*)"; -//! # const CHANGE: &str = "tr([7d94197e/86'/1'/0']tpubDCyQVJj8KzjiQsFjmb3KwECVXPvMwvAxxZGCP9XmWSopmjW3bCV3wD7TgxrUhiGSueDS1MU5X1Vb1YjYcp8jitXc5fXfdC1z68hDDEyKRNr/1/*)"; -//! # use std::collections::HashSet; -//! # use std::net::{IpAddr, Ipv4Addr}; -//! # use std::str::FromStr; -//! # use bdk_wallet::bitcoin::{ -//! # constants::genesis_block, secp256k1::Secp256k1, Address, BlockHash, Network, ScriptBuf, -//! # }; -//! # use bdk_wallet::chain::{ -//! # keychain_txout::KeychainTxOutIndex, local_chain::LocalChain, miniscript::Descriptor, FullTxOut, -//! # IndexedTxGraph, SpkIterator, Merge, -//! # }; -//! use bdk_kyoto::EventReceiver; -//! use bdk_kyoto::logger::PrintLogger; -//! use bdk_kyoto::kyoto::{TrustedPeer, NodeBuilder, HeaderCheckpoint}; -//! -//! const TARGET_INDEX: u32 = 20; -//! -//! #[tokio::main] -//! async fn main() -> anyhow::Result<()> { -//! let secp = Secp256k1::new(); -//! let (descriptor, _) = Descriptor::parse_descriptor(&secp, &RECEIVE)?; -//! let (change_descriptor, _) = Descriptor::parse_descriptor(&secp, &CHANGE)?; -//! -//! let genesis_hash = genesis_block(Network::Signet).block_hash(); -//! let (mut chain, _) = LocalChain::from_genesis_hash(genesis_hash); -//! -//! let mut graph = IndexedTxGraph::new({ -//! let mut index = KeychainTxOutIndex::default(); -//! let _ = index.insert_descriptor("external", descriptor); -//! let _ = index.insert_descriptor("internal", change_descriptor); -//! index -//! }); -//! -//! let mut spks_to_watch: HashSet = HashSet::new(); -//! for (_k, desc) in graph.index.keychains() { -//! for (_i, spk) in SpkIterator::new_with_range(desc, 0..TARGET_INDEX) { -//! spks_to_watch.insert(spk); -//! } -//! } -//! -//! let peer = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); -//! let trusted = TrustedPeer::from_ip(peer); -//! let cp = HeaderCheckpoint::closest_checkpoint_below_height(170_000, Network::Signet); -//! -//! let builder = NodeBuilder::new(Network::Signet); -//! let (node, kyoto_client) = builder -//! .add_peer(trusted) -//! .add_scripts(spks_to_watch) -//! .anchor_checkpoint(cp) -//! .num_required_peers(2) -//! .build_node() -//! .unwrap(); -//! -//! let (sender, receiver) = kyoto_client.split(); -//! let mut client = EventReceiver::from_index(chain.tip(), &graph.index, receiver).unwrap(); -//! -//! tokio::task::spawn(async move { node.run().await }); -//! -//! let logger = PrintLogger::new(); -//! if let Some(update) = client.update(&logger).await { -//! let _ = chain.apply_update(update.chain_update.unwrap())?; -//! let _ = graph.apply_update(update.tx_update); -//! let _ = graph -//! .index -//! .reveal_to_target_multi(&update.last_active_indices); -//! } -//! sender.shutdown().await?; -//! Ok(()) -//! } -//! ``` #![warn(missing_docs)] use core::fmt; -#[cfg(feature = "wallet")] use core::{future::Future, pin::Pin}; use std::collections::BTreeMap; -#[cfg(feature = "wallet")] use std::collections::HashSet; -#[cfg(feature = "wallet")] type FutureResult<'a, T, E> = Pin> + Send + 'a>>; -use bdk_chain::{ +use bdk_wallet::chain::{ keychain_txout::KeychainTxOutIndex, local_chain::{self, CheckPoint, LocalChain}, spk_client::FullScanResponse, IndexedTxGraph, }; -use bdk_chain::{ConfirmationBlockTime, TxUpdate}; +use bdk_wallet::chain::{ConfirmationBlockTime, TxUpdate}; -pub use bdk_chain::bitcoin::FeeRate; -pub use bdk_chain::local_chain::MissingGenesisError; +pub use bdk_wallet::chain::bitcoin::FeeRate; +pub use bdk_wallet::chain::local_chain::MissingGenesisError; pub extern crate kyoto; -#[cfg(feature = "wallet")] use bdk_wallet::KeychainKind; -#[cfg(feature = "rusqlite")] + pub use kyoto::core::builder::NodeDefault; #[cfg(feature = "events")] pub use kyoto::{DisconnectedHeader, FailurePayload}; @@ -194,19 +114,17 @@ pub use kyoto::{ NodeState, Receiver, ScriptBuf, SyncUpdate, TxBroadcast, TxBroadcastPolicy, Txid, Warning, }; -#[cfg(all(feature = "wallet", feature = "rusqlite"))] pub mod builder; #[cfg(feature = "callbacks")] pub mod logger; -#[cfg(feature = "wallet")] #[derive(Debug)] /// A node and associated structs to send and receive events to and from the node. pub struct LightClient { /// Send events to a running node (i.e. broadcast a transaction). pub sender: EventSender, /// Receive wallet updates from a node. - pub receiver: EventReceiver, + pub receiver: EventReceiver, /// The underlying node that must be run to fetch blocks from peers. pub node: NodeDefault, } @@ -214,25 +132,22 @@ pub struct LightClient { /// Interpret events from a node that is running to apply /// updates to an underlying wallet. #[derive(Debug)] -pub struct EventReceiver { +pub struct EventReceiver { // channel receiver receiver: kyoto::Receiver, // changes to local chain chain: local_chain::LocalChain, // receive graph - graph: IndexedTxGraph>, + graph: IndexedTxGraph>, // the network minimum to broadcast a transaction min_broadcast_fee: FeeRate, } -impl EventReceiver -where - K: fmt::Debug + Clone + Ord, -{ +impl EventReceiver { /// Build a light client event handler from a [`KeychainTxOutIndex`] and [`CheckPoint`]. - pub fn from_index( + pub(crate) fn from_index( cp: CheckPoint, - index: &KeychainTxOutIndex, + index: KeychainTxOutIndex, receiver: Receiver, ) -> Result { Ok(Self { @@ -251,7 +166,10 @@ where /// running node. Production applications should define how the application handles /// these events and displays them to end users. #[cfg(feature = "callbacks")] - pub async fn update(&mut self, logger: &dyn NodeEventHandler) -> Option> { + pub async fn update( + &mut self, + logger: &dyn NodeEventHandler, + ) -> Option> { let mut chain_changeset = BTreeMap::new(); while let Ok(message) = self.receiver.recv().await { self.log(&message, logger); @@ -330,7 +248,7 @@ where // When the client is believed to have synced to the chain tip of most work, // we can return a wallet update. - fn get_scan_response(&mut self) -> FullScanResponse { + fn get_scan_response(&mut self) -> FullScanResponse { let tx_update = TxUpdate::from(self.graph.graph().clone()); let graph = core::mem::take(&mut self.graph); let last_active_indices = graph.index.last_used_indices(); @@ -352,7 +270,7 @@ where /// Informational messages on the node operation may be filtered out with /// [`LogLevel::Warning`], which will only emit warnings when called. #[cfg(feature = "events")] - pub async fn next_event(&mut self, log_level: LogLevel) -> Option> { + pub async fn next_event(&mut self, log_level: LogLevel) -> Option { while let Ok(message) = self.receiver.recv().await { match message { NodeMessage::Dialog(log) => { @@ -455,7 +373,7 @@ pub trait NodeEventHandler: Send + Sync + fmt::Debug + 'static { /// Events emitted by a node that may be used by a wallet or application. #[cfg(feature = "events")] -pub enum Event { +pub enum Event { /// Information about the current node process. Log(String), /// Warnings emitted by the node that may effect sync times or node operation. @@ -474,7 +392,7 @@ pub enum Event { /// /// This event will be emitted every time a new block is found while the node /// is running and is connected to peers. - ScanResponse(FullScanResponse), + ScanResponse(FullScanResponse), /// Blocks were reorganized from the chain of most work. /// /// ## Note @@ -498,14 +416,12 @@ pub enum LogLevel { /// Extend the functionality of [`Wallet`](bdk_wallet) for interoperablility /// with the light client. -#[cfg(feature = "wallet")] pub trait WalletExt { /// Collect relevant scripts for addition to the node. Peeks scripts /// `lookahead` + `last_revealed_index` for each keychain. fn peek_revealed_plus_lookahead(&self) -> Box>; } -#[cfg(feature = "wallet")] impl WalletExt for bdk_wallet::Wallet { fn peek_revealed_plus_lookahead(&self) -> Box> { let mut spks: HashSet = HashSet::new(); @@ -521,7 +437,6 @@ impl WalletExt for bdk_wallet::Wallet { } /// Extend the [`EventSender`] functionality to work conveniently with a [`Wallet`](bdk_wallet). -#[cfg(feature = "wallet")] pub trait EventSenderExt { /// Add all revealed scripts to the node to monitor. fn add_revealed_scripts<'a>( @@ -530,7 +445,6 @@ pub trait EventSenderExt { ) -> FutureResult<'a, (), kyoto::ClientError>; } -#[cfg(feature = "wallet")] impl EventSenderExt for EventSender { fn add_revealed_scripts<'a>( &'a self,