|
1 | | -use bdk_kyoto::bip157::lookup_host; |
| 1 | +use bdk_kyoto::bip157::{lookup_host}; |
2 | 2 | use bdk_kyoto::bip157::tokio; |
3 | 3 | use bdk_kyoto::bip157::AddrV2; |
4 | 4 | use bdk_kyoto::bip157::Network; |
5 | | -use bdk_kyoto::bip157::Node; |
6 | 5 | use bdk_kyoto::bip157::ServiceFlags; |
7 | 6 | use bdk_kyoto::builder::Builder as BDKCbfBuilder; |
8 | 7 | use bdk_kyoto::builder::BuilderExt; |
9 | | -use bdk_kyoto::HeaderCheckpoint; |
10 | | -use bdk_kyoto::LightClient as BDKLightClient; |
| 8 | +use bdk_kyoto::{HeaderCheckpoint, Idle, LightClient}; |
11 | 9 | use bdk_kyoto::Receiver; |
12 | 10 | use bdk_kyoto::RejectReason; |
13 | 11 | use bdk_kyoto::Requester; |
@@ -37,51 +35,37 @@ const CWD_PATH: &str = "."; |
37 | 35 | const TCP_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(2); |
38 | 36 | const MESSAGE_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5); |
39 | 37 |
|
40 | | -/// Receive a [`CbfClient`] and [`CbfNode`]. |
41 | | -#[derive(Debug, uniffi::Record)] |
42 | | -pub struct CbfComponents { |
43 | | - /// Publish events to the node, like broadcasting transactions or adding scripts. |
44 | | - pub client: Arc<CbfClient>, |
45 | | - /// The node to run and fetch transactions for a [`Wallet`]. |
46 | | - pub node: Arc<CbfNode>, |
| 38 | +/// A compact block filters node that has been configured but not yet started. |
| 39 | +/// |
| 40 | +/// Built via [`CbfBuilder`]. Call [`CbfNode::start`] to prepare and spawn the node, receiving a |
| 41 | +/// [`CbfClient`] to interact with the running node. |
| 42 | +/// |
| 43 | +/// `CbfNode::start` may only be called once. Calling it a second time returns |
| 44 | +/// [`CbfError::AlreadyStarted`]. |
| 45 | +#[derive(uniffi::Object)] |
| 46 | +pub struct CbfNode { |
| 47 | + client: std::sync::Mutex<Option<LightClient<Idle>>>, |
47 | 48 | } |
48 | 49 |
|
49 | | -/// A [`CbfClient`] handles wallet updates from a [`CbfNode`]. |
50 | | -#[derive(Debug, uniffi::Object)] |
| 50 | +/// Interact with a running compact block filters node. |
| 51 | +/// |
| 52 | +/// Obtained by calling [`CbfNode::start`]. Provides access to three independent |
| 53 | +/// channels — each message is consumed by exactly one caller, so dedicate a single |
| 54 | +/// task or thread to draining each channel: |
| 55 | +/// |
| 56 | +/// * [`CbfClient::next_info`] — progress and connection events. |
| 57 | +/// * [`CbfClient::next_warning`] — non-fatal issues the node encountered. |
| 58 | +/// * [`CbfClient::update`] — wallet updates ready to be applied to a [`Wallet`]. |
| 59 | +/// |
| 60 | +/// Transactions can be broadcast and peers added at any time via the remaining methods. |
| 61 | +#[derive(uniffi::Object)] |
51 | 62 | pub struct CbfClient { |
52 | | - sender: Arc<Requester>, |
| 63 | + sender: Requester, |
53 | 64 | info_rx: Mutex<Receiver<bdk_kyoto::Info>>, |
54 | 65 | warning_rx: Mutex<UnboundedReceiver<bdk_kyoto::Warning>>, |
55 | 66 | update_rx: Mutex<UpdateSubscriber>, |
56 | 67 | } |
57 | 68 |
|
58 | | -/// A [`CbfNode`] gathers transactions for a [`Wallet`]. |
59 | | -/// To receive [`Update`] for [`Wallet`], refer to the |
60 | | -/// [`CbfClient`]. The [`CbfNode`] will run until instructed |
61 | | -/// to stop. |
62 | | -#[derive(Debug, uniffi::Object)] |
63 | | -pub struct CbfNode { |
64 | | - node: std::sync::Mutex<Option<Node>>, |
65 | | -} |
66 | | - |
67 | | -#[uniffi::export] |
68 | | -impl CbfNode { |
69 | | - /// Start the node on a detached OS thread and immediately return. |
70 | | - pub fn run(self: Arc<Self>) { |
71 | | - let mut lock = self.node.lock().unwrap(); |
72 | | - let node = lock.take().expect("cannot call run more than once"); |
73 | | - std::thread::spawn(|| { |
74 | | - tokio::runtime::Builder::new_multi_thread() |
75 | | - .enable_all() |
76 | | - .build() |
77 | | - .unwrap() |
78 | | - .block_on(async move { |
79 | | - let _ = node.run().await; |
80 | | - }) |
81 | | - }); |
82 | | - } |
83 | | -} |
84 | | - |
85 | 69 | /// Build a BIP 157/158 light client to fetch transactions for a `Wallet`. |
86 | 70 | /// |
87 | 71 | /// Options: |
@@ -178,7 +162,7 @@ impl CbfBuilder { |
178 | 162 | } |
179 | 163 |
|
180 | 164 | /// Construct a [`CbfComponents`] for a [`Wallet`]. |
181 | | - pub fn build(&self, wallet: &Wallet) -> CbfComponents { |
| 165 | + pub fn build(&self, wallet: &Wallet) -> CbfNode { |
182 | 166 | let wallet = wallet.get_wallet(); |
183 | 167 |
|
184 | 168 | let mut trusted_peers = Vec::new(); |
@@ -241,31 +225,51 @@ impl CbfBuilder { |
241 | 225 | builder = builder.socks5_proxy((addr, port)); |
242 | 226 | } |
243 | 227 |
|
244 | | - let BDKLightClient { |
245 | | - requester, |
246 | | - info_subscriber, |
247 | | - warning_subscriber, |
248 | | - update_subscriber, |
249 | | - node, |
250 | | - } = builder |
| 228 | + let light_client_idle = builder |
251 | 229 | .build_with_wallet(&wallet, scan_type) |
252 | 230 | .expect("networks match by definition"); |
253 | 231 |
|
254 | | - let node = CbfNode { |
255 | | - node: std::sync::Mutex::new(Some(node)), |
256 | | - }; |
| 232 | + CbfNode { |
| 233 | + client: std::sync::Mutex::new(Some(light_client_idle)), |
| 234 | + } |
| 235 | + } |
| 236 | +} |
257 | 237 |
|
258 | | - let client = CbfClient { |
259 | | - sender: Arc::new(requester), |
260 | | - info_rx: Mutex::new(info_subscriber), |
261 | | - warning_rx: Mutex::new(warning_subscriber), |
262 | | - update_rx: Mutex::new(update_subscriber), |
263 | | - }; |
| 238 | +#[uniffi::export] |
| 239 | +impl CbfNode { |
| 240 | + /// Subscribe to log and update channels, then spawn the node on a background thread. |
| 241 | + /// |
| 242 | + /// Returns a [`CbfClient`] that can be used to receive wallet updates, info and warning |
| 243 | + /// messages, and to broadcast transactions. |
| 244 | + /// |
| 245 | + /// This method may only be called once. A second call returns [`CbfError::AlreadyStarted`]. |
| 246 | + pub fn start(&self) -> Result<Arc<CbfClient>, CbfError> { |
| 247 | + let light_client = self |
| 248 | + .client |
| 249 | + .lock() |
| 250 | + .unwrap() |
| 251 | + .take() |
| 252 | + .ok_or(CbfError::AlreadyStarted)?; |
264 | 253 |
|
265 | | - CbfComponents { |
266 | | - client: Arc::new(client), |
267 | | - node: Arc::new(node), |
268 | | - } |
| 254 | + let (subscribed, logging, updates) = light_client.subscribe(); |
| 255 | + let (active, node) = subscribed.managed_start(); |
| 256 | + |
| 257 | + std::thread::spawn(|| { |
| 258 | + tokio::runtime::Builder::new_multi_thread() |
| 259 | + .enable_all() |
| 260 | + .build() |
| 261 | + .unwrap() |
| 262 | + .block_on(async move { |
| 263 | + let _ = node.run().await; |
| 264 | + }) |
| 265 | + }); |
| 266 | + |
| 267 | + Ok(Arc::new(CbfClient { |
| 268 | + sender: active.requester(), |
| 269 | + info_rx: Mutex::new(logging.info_subscriber), |
| 270 | + warning_rx: Mutex::new(logging.warning_subscriber), |
| 271 | + update_rx: Mutex::new(updates), |
| 272 | + })) |
269 | 273 | } |
270 | 274 | } |
271 | 275 |
|
@@ -340,7 +344,7 @@ impl CbfClient { |
340 | 344 | /// Add another [`Peer`] to attempt a connection with. |
341 | 345 | pub fn connect(&self, peer: Peer) -> Result<(), CbfError> { |
342 | 346 | self.sender |
343 | | - .add_peer(peer) |
| 347 | + .add_peer(TrustedPeer::from(peer)) |
344 | 348 | .map_err(|_| CbfError::NodeStopped) |
345 | 349 | } |
346 | 350 |
|
|
0 commit comments