7
7
8
8
mod bitcoind_rpc;
9
9
10
+ use crate :: chain:: bitcoind_rpc:: { BitcoindRpcClient , BoundedHeaderCache , ChainListener } ;
10
11
use crate :: config:: {
11
12
Config , EsploraSyncConfig , BDK_CLIENT_CONCURRENCY , BDK_CLIENT_STOP_GAP ,
12
13
BDK_WALLET_SYNC_TIMEOUT_SECS , FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS , LDK_WALLET_SYNC_TIMEOUT_SECS ,
@@ -22,11 +23,15 @@ use crate::logger::{log_bytes, log_error, log_info, log_trace, FilesystemLogger,
22
23
use crate :: types:: { Broadcaster , ChainMonitor , ChannelManager , DynStore , Sweeper , Wallet } ;
23
24
use crate :: { Error , NodeMetrics } ;
24
25
25
- use lightning:: chain:: { Confirm , Filter } ;
26
+ use lightning:: chain:: { Confirm , Filter , Listen } ;
26
27
use lightning:: util:: ser:: Writeable ;
27
28
28
29
use lightning_transaction_sync:: EsploraSyncClient ;
29
30
31
+ use lightning_block_sync:: init:: synchronize_listeners;
32
+ use lightning_block_sync:: poll:: ChainPoller ;
33
+ use lightning_block_sync:: SpvClient ;
34
+
30
35
use bdk_esplora:: EsploraAsyncExt ;
31
36
32
37
use esplora_client:: AsyncClient as EsploraAsyncClient ;
@@ -37,14 +42,14 @@ use std::collections::HashMap;
37
42
use std:: sync:: { Arc , Mutex , RwLock } ;
38
43
use std:: time:: { Duration , Instant , SystemTime , UNIX_EPOCH } ;
39
44
40
- use self :: bitcoind_rpc:: BitcoindRpcClient ;
41
-
42
45
// The default Esplora server we're using.
43
46
pub ( crate ) const DEFAULT_ESPLORA_SERVER_URL : & str = "https://blockstream.info/api" ;
44
47
45
48
// The default Esplora client timeout we're using.
46
49
pub ( crate ) const DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS : u64 = 10 ;
47
50
51
+ const CHAIN_POLLING_INTERVAL_SECS : u64 = 1 ;
52
+
48
53
pub ( crate ) enum WalletSyncStatus {
49
54
Completed ,
50
55
InProgress { subscribers : tokio:: sync:: broadcast:: Sender < Result < ( ) , Error > > } ,
@@ -241,7 +246,144 @@ impl ChainSource {
241
246
}
242
247
}
243
248
} ,
244
- Self :: BitcoindRpc { .. } => todo ! ( ) ,
249
+ Self :: BitcoindRpc {
250
+ bitcoind_rpc_client,
251
+ onchain_wallet,
252
+ kv_store,
253
+ config,
254
+ logger,
255
+ node_metrics,
256
+ ..
257
+ } => {
258
+ let mut header_cache = BoundedHeaderCache :: new ( ) ;
259
+ let channel_manager_best_block_hash =
260
+ channel_manager. current_best_block ( ) . block_hash ;
261
+ let sweeper_best_block_hash = output_sweeper. current_best_block ( ) . block_hash ;
262
+ let onchain_wallet_best_block_hash = onchain_wallet. current_best_block ( ) . block_hash ;
263
+
264
+ let mut chain_listeners = vec ! [
265
+ (
266
+ onchain_wallet_best_block_hash,
267
+ & * * onchain_wallet as & ( dyn Listen + Send + Sync ) ,
268
+ ) ,
269
+ (
270
+ channel_manager_best_block_hash,
271
+ & * channel_manager as & ( dyn Listen + Send + Sync ) ,
272
+ ) ,
273
+ ( sweeper_best_block_hash, & * output_sweeper as & ( dyn Listen + Send + Sync ) ) ,
274
+ ] ;
275
+
276
+ // TODO: Eventually we might want to see if we can synchronize `ChannelMonitor`s
277
+ // before giving them to `ChainMonitor` it the first place. However, this isn't
278
+ // trivial as we load them on initialization (in the `Builder`) and only gain
279
+ // network access during `start`. For now, we just make sure we get the worst known
280
+ // block hash and sychronize them via `ChainMonitor`.
281
+ if let Some ( worst_channel_monitor_block_hash) = chain_monitor
282
+ . list_monitors ( )
283
+ . iter ( )
284
+ . flat_map ( |( txo, _) | chain_monitor. get_monitor ( * txo) )
285
+ . map ( |m| m. current_best_block ( ) )
286
+ . min_by_key ( |b| b. height )
287
+ . map ( |b| b. block_hash )
288
+ {
289
+ chain_listeners. push ( (
290
+ worst_channel_monitor_block_hash,
291
+ & * chain_monitor as & ( dyn Listen + Send + Sync ) ,
292
+ ) ) ;
293
+ }
294
+
295
+ let chain_tip = loop {
296
+ match synchronize_listeners (
297
+ bitcoind_rpc_client. as_ref ( ) ,
298
+ config. network ,
299
+ & mut header_cache,
300
+ chain_listeners. clone ( ) ,
301
+ )
302
+ . await
303
+ {
304
+ Ok ( chain_tip) => {
305
+ {
306
+ let unix_time_secs_opt = SystemTime :: now ( )
307
+ . duration_since ( UNIX_EPOCH )
308
+ . ok ( )
309
+ . map ( |d| d. as_secs ( ) ) ;
310
+ let mut locked_node_metrics = node_metrics. write ( ) . unwrap ( ) ;
311
+ locked_node_metrics. latest_lightning_wallet_sync_timestamp =
312
+ unix_time_secs_opt;
313
+ locked_node_metrics. latest_onchain_wallet_sync_timestamp =
314
+ unix_time_secs_opt;
315
+ write_node_metrics (
316
+ & * locked_node_metrics,
317
+ Arc :: clone ( & kv_store) ,
318
+ Arc :: clone ( & logger) ,
319
+ )
320
+ . unwrap_or_else ( |e| {
321
+ log_error ! ( logger, "Failed to persist node metrics: {}" , e) ;
322
+ } ) ;
323
+ }
324
+ break chain_tip;
325
+ } ,
326
+
327
+ Err ( e) => {
328
+ log_error ! ( logger, "Failed to synchronize chain listeners: {:?}" , e) ;
329
+ tokio:: time:: sleep ( Duration :: from_secs ( CHAIN_POLLING_INTERVAL_SECS ) )
330
+ . await ;
331
+ } ,
332
+ }
333
+ } ;
334
+
335
+ let chain_poller =
336
+ ChainPoller :: new ( Arc :: clone ( & bitcoind_rpc_client) , config. network ) ;
337
+ let chain_listener = ChainListener {
338
+ onchain_wallet : Arc :: clone ( & onchain_wallet) ,
339
+ channel_manager : Arc :: clone ( & channel_manager) ,
340
+ chain_monitor : Arc :: clone ( & chain_monitor) ,
341
+ output_sweeper : Arc :: clone ( & output_sweeper) ,
342
+ } ;
343
+ let mut spv_client =
344
+ SpvClient :: new ( chain_tip, chain_poller, & mut header_cache, & chain_listener) ;
345
+ let mut chain_polling_interval =
346
+ tokio:: time:: interval ( Duration :: from_secs ( CHAIN_POLLING_INTERVAL_SECS ) ) ;
347
+ chain_polling_interval
348
+ . set_missed_tick_behavior ( tokio:: time:: MissedTickBehavior :: Skip ) ;
349
+
350
+ // Start the polling loop.
351
+ loop {
352
+ tokio:: select! {
353
+ _ = stop_sync_receiver. changed( ) => {
354
+ log_trace!(
355
+ logger,
356
+ "Stopping polling for new chain data." ,
357
+ ) ;
358
+ return ;
359
+ }
360
+ _ = chain_polling_interval. tick( ) => {
361
+ let _ = spv_client. poll_best_tip( ) . await . map_err( |e| {
362
+ log_error!( logger, "Failed to poll for chain data: {:?}" , e) ;
363
+ } ) ;
364
+ {
365
+ let unix_time_secs_opt = SystemTime :: now( )
366
+ . duration_since( UNIX_EPOCH )
367
+ . ok( )
368
+ . map( |d| d. as_secs( ) ) ;
369
+ let mut locked_node_metrics = node_metrics. write( ) . unwrap( ) ;
370
+ locked_node_metrics. latest_lightning_wallet_sync_timestamp =
371
+ unix_time_secs_opt;
372
+ locked_node_metrics. latest_onchain_wallet_sync_timestamp =
373
+ unix_time_secs_opt;
374
+ write_node_metrics(
375
+ & * locked_node_metrics,
376
+ Arc :: clone( & kv_store) ,
377
+ Arc :: clone( & logger) ,
378
+ )
379
+ . unwrap_or_else( |e| {
380
+ log_error!( logger, "Failed to persist node metrics: {}" , e) ;
381
+ } ) ;
382
+ }
383
+ }
384
+ }
385
+ }
386
+ } ,
245
387
}
246
388
}
247
389
0 commit comments