1919//! .network(Network::Signet)
2020//! .create_wallet_no_persist()?;
2121//!
22- //! let LightClient {
23- //! requester,
24- //! info_subscriber: _,
25- //! warning_subscriber: _,
26- //! mut update_subscriber,
27- //! node
28- //! } = Builder::new(Network::Signet).build_with_wallet(&wallet, ScanType::Sync)?;
29- //!
30- //! tokio::task::spawn(async move { node.run().await });
22+ //! let client = Builder::new(Network::Signet).build_with_wallet(&wallet, ScanType::Sync)?;
23+ //! let (client, _, mut update_subscriber) = client.subscribe();
24+ //! client.start();
3125//!
3226//! loop {
3327//! let update = update_subscriber.update().await?;
@@ -51,6 +45,7 @@ use bdk_wallet::KeychainKind;
5145pub extern crate bip157;
5246
5347use bip157:: chain:: BlockHeaderChanges ;
48+ use bip157:: tokio;
5449use bip157:: ScriptBuf ;
5550#[ doc( inline) ]
5651pub use bip157:: {
@@ -68,19 +63,156 @@ pub use bip157::UnboundedReceiver;
6863pub use builder:: BuilderExt ;
6964pub mod builder;
7065
66+ /// Client state when idle.
67+ pub struct Idle ;
68+ /// Client state when subscribed to events.
69+ pub struct Subscribed ;
70+ /// Client state when active.
71+ pub struct Active ;
72+
73+ mod sealed {
74+ pub trait Sealed { }
75+ }
76+
77+ impl sealed:: Sealed for Idle { }
78+ impl sealed:: Sealed for Subscribed { }
79+ impl sealed:: Sealed for Active { }
80+
81+ /// State of the client.
82+ pub trait State : sealed:: Sealed { }
83+
84+ impl State for Idle { }
85+ impl State for Subscribed { }
86+ impl State for Active { }
87+
88+ /// Subscribe to events, notably
7189#[ derive( Debug ) ]
72- /// A node and associated structs to send and receive events to and from the node.
73- pub struct LightClient {
74- /// Send events to a running node (i.e. broadcast a transaction).
75- pub requester : Requester ,
90+ pub struct LoggingSubscribers {
7691 /// Receive informational messages as the node runs.
7792 pub info_subscriber : Receiver < Info > ,
7893 /// Receive warnings from the node as it runs.
7994 pub warning_subscriber : UnboundedReceiver < Warning > ,
80- /// Receive wallet updates from a node.
81- pub update_subscriber : UpdateSubscriber ,
82- /// The underlying node that must be run to fetch blocks from peers.
83- pub node : Node ,
95+ }
96+
97+ #[ derive( Debug ) ]
98+ /// A client and associated structs to send and receive events to and from a node process.
99+ ///
100+ /// The client has three states:
101+ /// - [`Idle`]: the client has been initialized.
102+ /// - [`Subscribed`]: the application is ready to handle logs and updates, but the process is not
103+ /// running yet
104+ /// - [`Active`]: the client is actively fetching data and may now handle requests.
105+ pub struct LightClient < S : State > {
106+ // Send events to a running node (i.e. broadcast a transaction).
107+ requester : Requester ,
108+ // Receive info/warnings from the node as it runs.
109+ logging_subscribers : Option < LoggingSubscribers > ,
110+ // Receive wallet updates from a node.
111+ update_subscriber : Option < UpdateSubscriber > ,
112+ // The underlying node that must be run to fetch blocks from peers.
113+ node : Option < Node > ,
114+ _marker : core:: marker:: PhantomData < S > ,
115+ }
116+
117+ impl LightClient < Idle > {
118+ fn new (
119+ requester : Requester ,
120+ logging : LoggingSubscribers ,
121+ update : UpdateSubscriber ,
122+ node : bip157:: Node ,
123+ ) -> LightClient < Idle > {
124+ LightClient {
125+ requester,
126+ logging_subscribers : Some ( logging) ,
127+ update_subscriber : Some ( update) ,
128+ node : Some ( node) ,
129+ _marker : core:: marker:: PhantomData ,
130+ }
131+ }
132+
133+ /// Subscribe to events emitted by the underlying data fetching process. This includes logging
134+ /// and wallet updates. During this step, one may start threads that log to a file and apply
135+ /// updates to a wallet.
136+ ///
137+ /// # Returns
138+ ///
139+ /// - [`LightClient<Subscribed>`], a client ready to start.
140+ /// - [`LoggingSubscribers`], info and warning messages to display to a user or write to file.
141+ /// - [`UpdateSubscriber`], used to await updates related to the user's wallet.
142+ pub fn subscribe (
143+ mut self ,
144+ ) -> (
145+ LightClient < Subscribed > ,
146+ LoggingSubscribers ,
147+ UpdateSubscriber ,
148+ ) {
149+ let logging =
150+ core:: mem:: take ( & mut self . logging_subscribers ) . expect ( "cannot subscribe twice." ) ;
151+ let updates =
152+ core:: mem:: take ( & mut self . update_subscriber ) . expect ( "cannot subscribe twice." ) ;
153+ let client = LightClient {
154+ requester : self . requester ,
155+ logging_subscribers : None ,
156+ update_subscriber : None ,
157+ node : self . node ,
158+ _marker : core:: marker:: PhantomData ,
159+ } ;
160+ ( client, logging, updates)
161+ }
162+ }
163+
164+ impl LightClient < Subscribed > {
165+ /// Start fetching data for the wallet on a dedicated [`tokio::task`]. This will continually
166+ /// run until terminated or no peers could be found.
167+ ///
168+ /// # Panics
169+ ///
170+ /// If there is no [`tokio::runtime::Runtime`] to drive execution. Common in synchronous
171+ /// setups.
172+ pub fn start ( mut self ) -> LightClient < Active > {
173+ let node = core:: mem:: take ( & mut self . node ) . expect ( "cannot start twice." ) ;
174+ tokio:: task:: spawn ( async move { node. run ( ) . await } ) ;
175+ LightClient {
176+ requester : self . requester ,
177+ logging_subscribers : None ,
178+ update_subscriber : None ,
179+ node : None ,
180+ _marker : core:: marker:: PhantomData ,
181+ }
182+ }
183+
184+ /// Take the underlying node process to run in a custom way. Examples include using a dedicated
185+ /// [`tokio::runtime::Runtime`] or [`tokio::runtime::Handle`] to drive execution.
186+ pub fn managed_start ( mut self ) -> ( LightClient < Active > , Node ) {
187+ let node = core:: mem:: take ( & mut self . node ) . expect ( "cannot start twice." ) ;
188+ let client = LightClient {
189+ requester : self . requester ,
190+ logging_subscribers : None ,
191+ update_subscriber : None ,
192+ node : None ,
193+ _marker : core:: marker:: PhantomData ,
194+ } ;
195+ ( client, node)
196+ }
197+ }
198+
199+ impl LightClient < Active > {
200+ /// The client is active and may now handle requests with a [`Requester`].
201+ pub fn requester ( self ) -> Requester {
202+ self . requester
203+ }
204+ }
205+
206+ impl From < LightClient < Active > > for Requester {
207+ fn from ( value : LightClient < Active > ) -> Self {
208+ value. requester
209+ }
210+ }
211+
212+ impl AsRef < Requester > for LightClient < Active > {
213+ fn as_ref ( & self ) -> & Requester {
214+ & self . requester
215+ }
84216}
85217
86218/// Interpret events from a node that is running to apply
0 commit comments