From f97514ec7c69dc11bcab8f9e6561c92c8c76f92b Mon Sep 17 00:00:00 2001 From: Rob N Date: Sat, 21 Sep 2024 08:09:32 -1000 Subject: [PATCH] fix(client): allow getting multiple `Receiver` --- src/builder.rs | 19 +++++++------ src/lib.rs | 72 ++++++++++++++++++++++++++++++-------------------- src/logger.rs | 16 ++++++----- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 0b3a0d1..a0393bd 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -2,8 +2,8 @@ //! //! ## Details //! -//! The node has a number of configurations. Notably, the height of in the blockchain to start a wallet recovery -//! and the nodes on the peer-to-peer network are both configurable. +//! The node has a number of configurations. Notably, the height of in the blockchain to start a +//! wallet recovery and the nodes on the peer-to-peer network are both configurable. //! //! ```no_run //! # const RECEIVE: &str = "tr([7d94197e/86'/1'/0']tpubDCyQVJj8KzjiQsFjmb3KwECVXPvMwvAxxZGCP9XmWSopmjW3bCV3wD7TgxrUhiGSueDS1MU5X1Vb1YjYcp8jitXc5fXfdC1z68hDDEyKRNr/0/*)"; @@ -112,8 +112,9 @@ impl<'a> LightClientBuilder<'a> { self } - /// Use all the scripts up to the wallet's lookahead parameter. Only to be used during wallet recovery, - /// when we do not have any knowledge of the scripts used in the wallet yet. For more information on the wallet lookahead, + /// Use all the scripts up to the wallet's lookahead parameter. Only to be used during wallet + /// recovery, when we do not have any knowledge of the scripts used in the wallet yet. For + /// more information on the wallet lookahead, /// see [`KeychainTxOutIndex`](bdk_wallet::chain::indexer::keychain_txout::KeychainTxOutIndex). pub fn use_lookahead_scripts(mut self) -> Self { self.lookahead = true; @@ -173,8 +174,9 @@ impl<'a> LightClientBuilder<'a> { } match self.birthday_height { Some(birthday) => { - // If there is a birthday at a height less than our local chain, we may assume we've already synced - // the wallet past the birthday height and no longer need it. + // If there is a birthday at a height less than our local chain, we may assume we've + // already synced the wallet past the birthday height and no longer + // need it. if birthday < self.wallet.local_chain().tip().height() { let block_id = self.wallet.local_chain().tip(); let header_cp = HeaderCheckpoint::new(block_id.height(), block_id.hash()); @@ -185,8 +187,9 @@ impl<'a> LightClientBuilder<'a> { } } None => { - // If there is no birthday provided and the local chain starts at the genesis block, we assume this - // is a new wallet and use the most recent checkpoint. Otherwise we sync from the last known tip in the + // If there is no birthday provided and the local chain starts at the genesis block, + // we assume this is a new wallet and use the most recent + // checkpoint. Otherwise we sync from the last known tip in the // LocalChain. let block_id = self.wallet.local_chain().tip(); if block_id.height() > 0 { diff --git a/src/lib.rs b/src/lib.rs index c5fd6c4..5b9bbb0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,23 @@ //! BDK-Kyoto is an extension of [Kyoto](https://github.com/rustaceanrob/kyoto), a client-side implementation of BIP157/BIP158. -//! These proposals define a way for users to fetch transactions privately, using _compact block filters_. -//! You may want to read the specification [here](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki). +//! These proposals define a way for users to fetch transactions privately, using _compact block +//! filters_. You may want to read the specification [here](https://github.com/bitcoin/bips/blob/master/bip-0158.mediawiki). //! -//! Kyoto runs as a psuedo-node, sending messages over the Bitcoin peer-to-peer layer, finding new peers to connect to, and managing a -//! light-weight database of Bitcoin block headers. As such, developing a wallet application using this crate is distinct from a typical -//! client/server relationship. Esplora and Electrum offer _proactive_ APIs, in that the servers will respond to events as they are requested. -//! In the case of running a node as a background process, the developer experience is far more _reactive_, in that the node may emit any number of events, -//! and the application may respond to them. +//! Kyoto runs as a psuedo-node, sending messages over the Bitcoin peer-to-peer layer, finding new +//! peers to connect to, and managing a light-weight database of Bitcoin block headers. As such, +//! developing a wallet application using this crate is distinct from a typical client/server +//! relationship. Esplora and Electrum offer _proactive_ APIs, in that the servers will respond to +//! events as they are requested. In the case of running a node as a background process, the +//! developer experience is far more _reactive_, in that the node may emit any number of events, and +//! the application may respond to them. //! -//! BDK-Kyoto curates these events into structures that are easily handled by BDK APIs, making integration of compact block filters easily understood. -//! Developers are free to use [`bdk_wallet`], or only primatives found in [`bdk_core`](https://docs.rs/bdk_core/latest/bdk_core/) and [`bdk_chain`]. +//! BDK-Kyoto curates these events into structures that are easily handled by BDK APIs, making +//! integration of compact block filters easily understood. Developers are free to use [`bdk_wallet`], or only primatives found in [`bdk_core`](https://docs.rs/bdk_core/latest/bdk_core/) and [`bdk_chain`]. //! //! ## Examples //! -//! If you have an existing project that leverages `bdk_wallet`, building the compact block filter _node_ and _client_ is simple. -//! You may construct and configure a node to integrate with your wallet by using a [`LightClientBuilder`](crate). +//! If you have an existing project that leverages `bdk_wallet`, building the compact block filter +//! _node_ and _client_ is simple. You may construct and configure a node to integrate with your +//! wallet by using a [`LightClientBuilder`](crate). //! //! ```no_run //! # const RECEIVE: &str = "tr([7d94197e/86'/1'/0']tpubDCyQVJj8KzjiQsFjmb3KwECVXPvMwvAxxZGCP9XmWSopmjW3bCV3wD7TgxrUhiGSueDS1MU5X1Vb1YjYcp8jitXc5fXfdC1z68hDDEyKRNr/0/*)"; @@ -44,7 +47,8 @@ //! } //! ``` //! -//! Custom wallet implementations may still take advantage of BDK-Kyoto, however building the [`Client`] will involve configuring Kyoto directly. +//! Custom wallet implementations may still take advantage of BDK-Kyoto, however building the +//! [`Client`] will involve configuring Kyoto directly. //! //! ```no_run //! # const RECEIVE: &str = "tr([7d94197e/86'/1'/0']tpubDCyQVJj8KzjiQsFjmb3KwECVXPvMwvAxxZGCP9XmWSopmjW3bCV3wD7TgxrUhiGSueDS1MU5X1Vb1YjYcp8jitXc5fXfdC1z68hDDEyKRNr/0/*)"; @@ -119,7 +123,7 @@ //! } //! client.shutdown().await?; //! Ok(()) -//!} +//! } //! ``` #![warn(missing_docs)] @@ -153,7 +157,7 @@ pub use kyoto::{ #[derive(Debug)] pub struct Client { // channel sender - sender: kyoto::ClientSender, + client: kyoto::Client, // channel receiver receiver: kyoto::Receiver, // changes to local chain @@ -172,9 +176,9 @@ where index: &KeychainTxOutIndex, client: kyoto::Client, ) -> Result { - let (sender, receiver) = client.split(); + let receiver = client.receiver(); Ok(Self { - sender, + client, receiver, chain: LocalChain::from_tip(cp)?, graph: IndexedTxGraph::new(index.clone()), @@ -185,9 +189,9 @@ where /// This may take a significant portion of time during wallet recoveries or dormant wallets. /// Note that you may call this method in a loop as long as the [`Node`] is running. /// - /// A reference to a [`NodeMessageHandler`] is required, which handles events emitted from a running - /// [`Node`]. Production applications should define how the application handles these events and displays - /// them to end users. + /// A reference to a [`NodeMessageHandler`] is required, which handles events emitted from a + /// running [`Node`]. Production applications should define how the application handles + /// these events and displays them to end users. pub async fn update(&mut self, logger: &dyn NodeMessageHandler) -> Option> { let mut chain_changeset = BTreeMap::new(); while let Ok(message) = self.receiver.recv().await { @@ -272,7 +276,7 @@ where tx: Transaction, policy: TxBroadcastPolicy, ) -> Result<(), ClientError> { - self.sender + self.client .broadcast_tx(TxBroadcast { tx, broadcast_policy: policy, @@ -280,25 +284,35 @@ where .await } - /// Add more scripts to the node. For example, a user may reveal a Bitcoin address to receive a payment, - /// so this script should be added to the [`Node`]. + /// Add more scripts to the node. For example, a user may reveal a Bitcoin address to receive a + /// payment, so this script should be added to the [`Node`]. pub async fn add_script(&self, script: impl Into) -> Result<(), ClientError> { - self.sender.add_script(script).await + self.client.add_script(script).await } /// Shutdown the node. pub async fn shutdown(&self) -> Result<(), ClientError> { - self.sender.shutdown().await + self.client.shutdown().await } - /// Get a structure to broadcast transactions. Useful for broadcasting transactions and updating concurrently. + /// Get a structure to broadcast transactions. Useful for broadcasting transactions and updating + /// concurrently. pub fn transaction_broadcaster(&self) -> TransactionBroadcaster { - TransactionBroadcaster::new(self.sender.clone()) + TransactionBroadcaster::new(self.client.sender()) } - /// Get a mutable reference to the underlying channel [`Receiver`]. - pub fn channel_receiver(&mut self) -> &mut Receiver { - &mut self.receiver + /// Generate a new channel [`Receiver`] to get [`NodeMessage`] directly. Note that + /// [`Client::update`] will handle messages and build updates. + /// + /// ## Performance + /// + /// When generating a new channel [`Receiver`], a message must be consumed by _all_ [`Receiver`] + /// before it is removed from memory. For channels that process data slowly, this can cause + /// messages to build up in the channel, and may even cause errors if too many messages + /// are held in the channel at one time. For the best results, applications may not want to call + /// this method at all, or only a few times. + pub fn channel_receiver(&self) -> Receiver { + self.client.receiver() } } diff --git a/src/logger.rs b/src/logger.rs index d20601e..ff37cee 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -2,12 +2,13 @@ //! //! # Examples //! -//! For quick iteration and debugging, the [`PrintLogger`] responds to node events by simply printing the display to the console. +//! For quick iteration and debugging, the [`PrintLogger`] responds to node events by simply +//! printing the display to the console. //! //! ```rust -//! use bdk_kyoto::logger::{PrintLogger, NodeMessageHandler}; +//! use bdk_kyoto::logger::{NodeMessageHandler, PrintLogger}; //! use bdk_kyoto::Warning; -//! +//! //! let logger = PrintLogger::new(); //! logger.dialog("The node is running".into()); //! logger.warning(Warning::PeerTimedOut); @@ -16,16 +17,17 @@ //! For a more descriptive console log, the `tracing` feature may be used. //! //! ```rust -//! use bdk_kyoto::logger::{TraceLogger, NodeMessageHandler}; +//! use bdk_kyoto::logger::{NodeMessageHandler, TraceLogger}; //! use bdk_kyoto::Warning; -//! +//! //! let logger = TraceLogger::new(); //! logger.dialog("The node is running".into()); //! logger.warning(Warning::PeerTimedOut); //! ``` //! -//! For production applications, a custom implementation of [`NodeMessageHandler`] should be implemented. -//! An example of a good applciation logger should implement user interface behavior and potentially save information to a file. +//! For production applications, a custom implementation of [`NodeMessageHandler`] should be +//! implemented. An example of a good applciation logger should implement user interface behavior +//! and potentially save information to a file. use std::fmt::Debug;