1
1
use crate :: logger:: {
2
2
log_error, log_given_level, log_internal, log_trace, FilesystemLogger , Logger ,
3
3
} ;
4
- use crate :: Error ;
4
+ use crate :: { Config , Error } ;
5
5
6
6
use lightning:: chain:: chaininterface:: { BroadcasterInterface , ConfirmationTarget , FeeEstimator } ;
7
7
use lightning:: chain:: WatchedOutput ;
8
8
use lightning:: chain:: { Confirm , Filter } ;
9
9
10
- use bdk:: blockchain:: { Blockchain , EsploraBlockchain , GetBlockHash , GetHeight , GetTx } ;
10
+ use bdk:: blockchain:: { Blockchain , EsploraBlockchain } ;
11
11
use bdk:: database:: BatchDatabase ;
12
+ use bdk:: esplora_client;
12
13
use bdk:: wallet:: AddressIndex ;
13
14
use bdk:: { SignOptions , SyncOptions } ;
14
15
15
- use bitcoin:: { BlockHash , Script , Transaction , Txid } ;
16
+ use bitcoin:: { Script , Transaction , Txid } ;
16
17
17
18
use std:: collections:: HashSet ;
18
- use std:: sync:: { Arc , Mutex } ;
19
+ use std:: sync:: { Arc , Mutex , RwLock } ;
19
20
20
21
/// The minimum feerate we are allowed to send, as specify by LDK.
21
22
const MIN_FEERATE : u32 = 253 ;
22
23
24
+ // The used 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
25
+ // number of blocks after which BDK stops looking for scripts belonging to the wallet.
26
+ const BDK_CLIENT_STOP_GAP : usize = 20 ;
27
+
28
+ // The number of concurrent requests made against the API provider.
29
+ const BDK_CLIENT_CONCURRENCY : u8 = 8 ;
30
+
23
31
pub struct ChainAccess < D >
24
32
where
25
33
D : BatchDatabase ,
26
34
{
27
35
blockchain : EsploraBlockchain ,
36
+ _client : Arc < esplora_client:: AsyncClient > ,
28
37
wallet : Mutex < bdk:: Wallet < D > > ,
29
38
queued_transactions : Mutex < Vec < Txid > > ,
30
39
watched_transactions : Mutex < Vec < Txid > > ,
31
40
queued_outputs : Mutex < Vec < WatchedOutput > > ,
32
41
watched_outputs : Mutex < Vec < WatchedOutput > > ,
33
42
last_sync_height : tokio:: sync:: Mutex < Option < u32 > > ,
43
+ tokio_runtime : RwLock < Option < Arc < tokio:: runtime:: Runtime > > > ,
44
+ _config : Arc < Config > ,
34
45
logger : Arc < FilesystemLogger > ,
35
46
}
36
47
@@ -39,30 +50,49 @@ where
39
50
D : BatchDatabase ,
40
51
{
41
52
pub ( crate ) fn new (
42
- blockchain : EsploraBlockchain , wallet : bdk:: Wallet < D > , logger : Arc < FilesystemLogger > ,
53
+ wallet : bdk:: Wallet < D > , config : Arc < Config > , logger : Arc < FilesystemLogger > ,
43
54
) -> Self {
44
55
let wallet = Mutex :: new ( wallet) ;
45
56
let watched_transactions = Mutex :: new ( Vec :: new ( ) ) ;
46
57
let queued_transactions = Mutex :: new ( Vec :: new ( ) ) ;
47
58
let watched_outputs = Mutex :: new ( Vec :: new ( ) ) ;
48
59
let queued_outputs = Mutex :: new ( Vec :: new ( ) ) ;
49
60
let last_sync_height = tokio:: sync:: Mutex :: new ( None ) ;
61
+ let tokio_runtime = RwLock :: new ( None ) ;
62
+ // TODO: Check that we can be sure that the Esplora client re-connects in case of failure
63
+ // and and exits cleanly on drop. Otherwise we need to handle this/move it to the runtime?
64
+ let blockchain = EsploraBlockchain :: new ( & config. esplora_server_url , BDK_CLIENT_STOP_GAP )
65
+ . with_concurrency ( BDK_CLIENT_CONCURRENCY ) ;
66
+ let client_builder =
67
+ esplora_client:: Builder :: new ( & format ! ( "http://{}" , & config. esplora_server_url) ) ;
68
+ let client = Arc :: new ( client_builder. build_async ( ) . unwrap ( ) ) ;
50
69
Self {
51
70
blockchain,
71
+ _client : client,
52
72
wallet,
53
73
queued_transactions,
54
74
watched_transactions,
55
75
queued_outputs,
56
76
watched_outputs,
57
77
last_sync_height,
78
+ tokio_runtime,
79
+ _config : config,
58
80
logger,
59
81
}
60
82
}
61
83
84
+ pub ( crate ) fn set_runtime ( & self , tokio_runtime : Arc < tokio:: runtime:: Runtime > ) {
85
+ * self . tokio_runtime . write ( ) . unwrap ( ) = Some ( tokio_runtime) ;
86
+ }
87
+
88
+ pub ( crate ) fn drop_runtime ( & self ) {
89
+ * self . tokio_runtime . write ( ) . unwrap ( ) = None ;
90
+ }
91
+
62
92
pub ( crate ) async fn sync_wallet ( & self ) -> Result < ( ) , Error > {
63
93
let sync_options = SyncOptions { progress : None } ;
64
94
65
- self . wallet . lock ( ) . unwrap ( ) . sync ( & self . blockchain , sync_options) ?;
95
+ self . wallet . lock ( ) . unwrap ( ) . sync ( & self . blockchain , sync_options) . await ?;
66
96
67
97
Ok ( ( ) )
68
98
}
@@ -237,11 +267,11 @@ where
237
267
Ok ( ( ) )
238
268
}
239
269
240
- pub ( crate ) fn create_funding_transaction (
270
+ pub ( crate ) async fn create_funding_transaction (
241
271
& self , output_script : & Script , value_sats : u64 , confirmation_target : ConfirmationTarget ,
242
272
) -> Result < Transaction , Error > {
243
273
let num_blocks = num_blocks_from_conf_target ( confirmation_target) ;
244
- let fee_rate = self . blockchain . estimate_fee ( num_blocks) ?;
274
+ let fee_rate = self . blockchain . estimate_fee ( num_blocks) . await ?;
245
275
246
276
let locked_wallet = self . wallet . lock ( ) . unwrap ( ) ;
247
277
let mut tx_builder = locked_wallet. build_tx ( ) ;
@@ -280,9 +310,18 @@ where
280
310
fn get_est_sat_per_1000_weight ( & self , confirmation_target : ConfirmationTarget ) -> u32 {
281
311
let num_blocks = num_blocks_from_conf_target ( confirmation_target) ;
282
312
let fallback_fee = fallback_fee_from_conf_target ( confirmation_target) ;
283
- self . blockchain
284
- . estimate_fee ( num_blocks)
285
- . map_or ( fallback_fee, |fee_rate| ( fee_rate. fee_wu ( 1000 ) as u32 ) . max ( MIN_FEERATE ) ) as u32
313
+
314
+ let locked_runtime = self . tokio_runtime . read ( ) . unwrap ( ) ;
315
+ if locked_runtime. as_ref ( ) . is_none ( ) {
316
+ return fallback_fee;
317
+ }
318
+
319
+ locked_runtime. as_ref ( ) . unwrap ( ) . block_on ( async {
320
+ self . blockchain
321
+ . estimate_fee ( num_blocks)
322
+ . await
323
+ . map_or ( fallback_fee, |fee_rate| ( fee_rate. fee_wu ( 1000 ) as u32 ) . max ( MIN_FEERATE ) ) as u32
324
+ } )
286
325
}
287
326
}
288
327
@@ -291,13 +330,20 @@ where
291
330
D : BatchDatabase ,
292
331
{
293
332
fn broadcast_transaction ( & self , tx : & Transaction ) {
294
- match self . blockchain . broadcast ( tx) {
295
- Ok ( _) => { }
296
- Err ( err) => {
297
- log_error ! ( self . logger, "Failed to broadcast transaction: {}" , err) ;
298
- panic ! ( "Failed to broadcast transaction: {}" , err) ;
299
- }
333
+ let locked_runtime = self . tokio_runtime . read ( ) . unwrap ( ) ;
334
+ if locked_runtime. as_ref ( ) . is_none ( ) {
335
+ return ;
300
336
}
337
+
338
+ locked_runtime. as_ref ( ) . unwrap ( ) . block_on ( async {
339
+ match self . blockchain . broadcast ( tx) . await {
340
+ Ok ( _) => { }
341
+ Err ( err) => {
342
+ log_error ! ( self . logger, "Failed to broadcast transaction: {}" , err) ;
343
+ panic ! ( "Failed to broadcast transaction: {}" , err) ;
344
+ }
345
+ }
346
+ } )
301
347
}
302
348
}
303
349
@@ -315,33 +361,6 @@ where
315
361
}
316
362
}
317
363
318
- impl < D > GetHeight for ChainAccess < D >
319
- where
320
- D : BatchDatabase ,
321
- {
322
- fn get_height ( & self ) -> Result < u32 , bdk:: Error > {
323
- self . blockchain . get_height ( )
324
- }
325
- }
326
-
327
- impl < D > GetBlockHash for ChainAccess < D >
328
- where
329
- D : BatchDatabase ,
330
- {
331
- fn get_block_hash ( & self , height : u64 ) -> Result < BlockHash , bdk:: Error > {
332
- self . blockchain . get_block_hash ( height)
333
- }
334
- }
335
-
336
- impl < D > GetTx for ChainAccess < D >
337
- where
338
- D : BatchDatabase ,
339
- {
340
- fn get_tx ( & self , txid : & Txid ) -> Result < Option < Transaction > , bdk:: Error > {
341
- self . blockchain . get_tx ( txid)
342
- }
343
- }
344
-
345
364
fn num_blocks_from_conf_target ( confirmation_target : ConfirmationTarget ) -> usize {
346
365
match confirmation_target {
347
366
ConfirmationTarget :: Background => 12 ,
0 commit comments