Skip to content

Commit c4f3fe4

Browse files
committed
Enforce successful initial fee update and make sync interval configurable
So far we'd immediately start background syncing wallets and the fee cache upon `start()` which would work fine, except when these initial syncs failed, in which case we'd use our fallback fee. This might lead to being disconnected from peers as our fallback values are likely out of current fee ranges. To mitigate this we here simply enforce that an initial fee rate update in `start()` is successful. Moreover, we make the wallet background syncing intervals configurable, however enforcing a floor of 10 seconds.
1 parent 5fca28e commit c4f3fe4

File tree

4 files changed

+138
-69
lines changed

4 files changed

+138
-69
lines changed

bindings/ldk_node.udl

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dictionary Config {
66
Network network;
77
NetAddress? listening_address;
88
u32 default_cltv_expiry_delta;
9+
u64 onchain_wallet_sync_interval_secs;
10+
u64 wallet_sync_interval_secs;
911
LogLevel log_level;
1012
};
1113

src/lib.rs

+135-60
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ use types::{
118118
pub use types::{ChannelDetails, ChannelId, PeerDetails, UserChannelId};
119119
use wallet::Wallet;
120120

121-
use logger::{log_error, log_info, FilesystemLogger, Logger};
121+
use logger::{log_error, log_info, log_trace, FilesystemLogger, Logger};
122122

123123
use lightning::chain::keysinterface::EntropySource;
124124
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
@@ -173,7 +173,9 @@ const DEFAULT_STORAGE_DIR_PATH: &str = "/tmp/ldk_node/";
173173
const DEFAULT_NETWORK: Network = Network::Bitcoin;
174174
const DEFAULT_LISTENING_ADDR: &str = "0.0.0.0:9735";
175175
const DEFAULT_CLTV_EXPIRY_DELTA: u32 = 144;
176-
const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/api";
176+
const DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS: u64 = 60;
177+
const DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS: u64 = 20;
178+
const DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS: u64 = 60 * 60;
177179
const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Debug;
178180

179181
// The 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
@@ -183,6 +185,9 @@ const BDK_CLIENT_STOP_GAP: usize = 20;
183185
// The number of concurrent requests made against the API provider.
184186
const BDK_CLIENT_CONCURRENCY: u8 = 8;
185187

188+
// The default Esplora server we're using.
189+
const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/api";
190+
186191
// The timeout after which we abandon retrying failed payments.
187192
const LDK_PAYMENT_RETRY_TIMEOUT: Duration = Duration::from_secs(10);
188193

@@ -203,13 +208,16 @@ const WALLET_KEYS_SEED_LEN: usize = 64;
203208
///
204209
/// ### Defaults
205210
///
206-
/// | Parameter | Value |
207-
/// |-----------------------------|------------------|
208-
/// | `storage_dir_path` | /tmp/ldk_node/ |
209-
/// | `network` | Network::Bitcoin |
210-
/// | `listening_address` | 0.0.0.0:9735 |
211-
/// | `default_cltv_expiry_delta` | 144 |
212-
/// | `log_level` | `Debug` |
211+
/// | Parameter | Value |
212+
/// |-------------------------------------|------------------|
213+
/// | `storage_dir_path` | /tmp/ldk_node/ |
214+
/// | `network` | Network::Bitcoin |
215+
/// | `listening_address` | 0.0.0.0:9735 |
216+
/// | `default_cltv_expiry_delta` | 144 |
217+
/// | `onchain_wallet_sync_interval_secs` | 60 |
218+
/// | `wallet_sync_interval_secs` | 20 |
219+
/// | `fee_rate_cache_update_interval` | 3600 |
220+
/// | `log_level` | `Debug` |
213221
///
214222
pub struct Config {
215223
/// The path where the underlying LDK and BDK persist their data.
@@ -220,6 +228,18 @@ pub struct Config {
220228
pub listening_address: Option<NetAddress>,
221229
/// The default CLTV expiry delta to be used for payments.
222230
pub default_cltv_expiry_delta: u32,
231+
/// The time in-between background sync attempts of the onchain wallet, in seconds.
232+
///
233+
/// **Note:** A minimum of 10 seconds is always enforced.
234+
pub onchain_wallet_sync_interval_secs: u64,
235+
/// The time in-between background sync attempts of the LDK wallet, in seconds.
236+
///
237+
/// **Note:** A minimum of 10 seconds is always enforced.
238+
pub wallet_sync_interval_secs: u64,
239+
/// The time in-between background update attempts to our fee rate cache, in seconds.
240+
///
241+
/// **Note:** A minimum of 10 seconds is always enforced.
242+
pub fee_rate_cache_update_interval_secs: u64,
223243
/// The level at which we log messages.
224244
///
225245
/// Any messages below this level will be excluded from the logs.
@@ -233,6 +253,9 @@ impl Default for Config {
233253
network: DEFAULT_NETWORK,
234254
listening_address: Some(DEFAULT_LISTENING_ADDR.parse().unwrap()),
235255
default_cltv_expiry_delta: DEFAULT_CLTV_EXPIRY_DELTA,
256+
onchain_wallet_sync_interval_secs: DEFAULT_BDK_WALLET_SYNC_INTERVAL_SECS,
257+
wallet_sync_interval_secs: DEFAULT_LDK_WALLET_SYNC_INTERVAL_SECS,
258+
fee_rate_cache_update_interval_secs: DEFAULT_FEE_RATE_CACHE_UPDATE_INTERVAL_SECS,
236259
log_level: DEFAULT_LOG_LEVEL,
237260
}
238261
}
@@ -766,40 +789,59 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
766789

767790
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
768791

769-
let event_handler = Arc::new(EventHandler::new(
770-
Arc::clone(&self.wallet),
771-
Arc::clone(&self.event_queue),
772-
Arc::clone(&self.channel_manager),
773-
Arc::clone(&self.network_graph),
774-
Arc::clone(&self.keys_manager),
775-
Arc::clone(&self.payment_store),
776-
Arc::clone(&self.runtime),
777-
Arc::clone(&self.logger),
778-
Arc::clone(&self.config),
779-
));
792+
// Block to ensure we update our fee rate cache once on startup
793+
let wallet = Arc::clone(&self.wallet);
794+
let sync_logger = Arc::clone(&self.logger);
795+
runtime.block_on(async move {
796+
let now = Instant::now();
797+
match wallet.update_fee_estimates().await {
798+
Ok(()) => {
799+
log_info!(
800+
sync_logger,
801+
"Initial fee rate cache update finished in {}ms.",
802+
now.elapsed().as_millis()
803+
);
804+
Ok(())
805+
}
806+
Err(e) => {
807+
log_error!(sync_logger, "Initial fee rate cache update failed: {}", e,);
808+
Err(e)
809+
}
810+
}
811+
})?;
780812

781813
// Setup wallet sync
782814
let wallet = Arc::clone(&self.wallet);
783-
let tx_sync = Arc::clone(&self.tx_sync);
784-
let sync_cman = Arc::clone(&self.channel_manager);
785-
let sync_cmon = Arc::clone(&self.chain_monitor);
786815
let sync_logger = Arc::clone(&self.logger);
787816
let mut stop_sync = self.stop_receiver.clone();
788-
817+
let onchain_wallet_sync_interval_secs =
818+
self.config.onchain_wallet_sync_interval_secs.max(10);
819+
let fee_rate_cache_update_interval_secs =
820+
self.config.fee_rate_cache_update_interval_secs.max(10);
789821
std::thread::spawn(move || {
790822
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(
791823
async move {
792-
let mut interval = tokio::time::interval(Duration::from_secs(30));
793-
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
824+
let mut onchain_wallet_sync_interval = tokio::time::interval(
825+
Duration::from_secs(onchain_wallet_sync_interval_secs),
826+
);
827+
onchain_wallet_sync_interval
828+
.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
829+
let mut fee_rate_update_interval = tokio::time::interval(Duration::from_secs(
830+
fee_rate_cache_update_interval_secs,
831+
));
832+
fee_rate_update_interval
833+
.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
834+
// We just blocked on updating, so skip the first tick.
835+
fee_rate_update_interval.reset();
794836
loop {
795837
let now = Instant::now();
796838
tokio::select! {
797839
_ = stop_sync.changed() => {
798840
return;
799841
}
800-
_ = interval.tick() => {
842+
_ = onchain_wallet_sync_interval.tick() => {
801843
match wallet.sync().await {
802-
Ok(()) => log_info!(
844+
Ok(()) => log_trace!(
803845
sync_logger,
804846
"Background sync of on-chain wallet finished in {}ms.",
805847
now.elapsed().as_millis()
@@ -813,12 +855,64 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
813855
}
814856
}
815857
}
858+
_ = fee_rate_update_interval.tick() => {
859+
match wallet.update_fee_estimates().await {
860+
Ok(()) => log_trace!(
861+
sync_logger,
862+
"Background update of fee rate cache finished in {}ms.",
863+
now.elapsed().as_millis()
864+
),
865+
Err(err) => {
866+
log_error!(
867+
sync_logger,
868+
"Background update of fee rate cache failed: {}",
869+
err
870+
)
871+
}
872+
}
873+
}
816874
}
817875
}
818876
},
819877
);
820878
});
821879

880+
let tx_sync = Arc::clone(&self.tx_sync);
881+
let sync_cman = Arc::clone(&self.channel_manager);
882+
let sync_cmon = Arc::clone(&self.chain_monitor);
883+
let sync_logger = Arc::clone(&self.logger);
884+
let mut stop_sync = self.stop_receiver.clone();
885+
let wallet_sync_interval_secs = self.config.wallet_sync_interval_secs.max(10);
886+
runtime.spawn(async move {
887+
let mut wallet_sync_interval =
888+
tokio::time::interval(Duration::from_secs(wallet_sync_interval_secs));
889+
wallet_sync_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
890+
loop {
891+
let now = Instant::now();
892+
tokio::select! {
893+
_ = stop_sync.changed() => {
894+
return;
895+
}
896+
_ = wallet_sync_interval.tick() => {
897+
let confirmables = vec![
898+
&*sync_cman as &(dyn Confirm + Sync + Send),
899+
&*sync_cmon as &(dyn Confirm + Sync + Send),
900+
];
901+
match tx_sync.sync(confirmables).await {
902+
Ok(()) => log_trace!(
903+
sync_logger,
904+
"Background sync of Lightning wallet finished in {}ms.",
905+
now.elapsed().as_millis()
906+
),
907+
Err(e) => {
908+
log_error!(sync_logger, "Background sync of Lightning wallet failed: {}", e)
909+
}
910+
}
911+
}
912+
}
913+
}
914+
});
915+
822916
if self.gossip_source.is_rgs() {
823917
let gossip_source = Arc::clone(&self.gossip_source);
824918
let gossip_sync_store = Arc::clone(&self.kv_store);
@@ -836,7 +930,7 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
836930
let now = Instant::now();
837931
match gossip_source.update_rgs_snapshot().await {
838932
Ok(updated_timestamp) => {
839-
log_info!(
933+
log_trace!(
840934
gossip_sync_logger,
841935
"Background sync of RGS gossip data finished in {}ms.",
842936
now.elapsed().as_millis()
@@ -860,37 +954,6 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
860954
});
861955
}
862956

863-
let sync_logger = Arc::clone(&self.logger);
864-
let mut stop_sync = self.stop_receiver.clone();
865-
runtime.spawn(async move {
866-
let mut interval = tokio::time::interval(Duration::from_secs(10));
867-
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
868-
loop {
869-
let now = Instant::now();
870-
tokio::select! {
871-
_ = stop_sync.changed() => {
872-
return;
873-
}
874-
_ = interval.tick() => {
875-
let confirmables = vec![
876-
&*sync_cman as &(dyn Confirm + Sync + Send),
877-
&*sync_cmon as &(dyn Confirm + Sync + Send),
878-
];
879-
match tx_sync.sync(confirmables).await {
880-
Ok(()) => log_info!(
881-
sync_logger,
882-
"Background sync of Lightning wallet finished in {}ms.",
883-
now.elapsed().as_millis()
884-
),
885-
Err(e) => {
886-
log_error!(sync_logger, "Background sync of Lightning wallet failed: {}", e)
887-
}
888-
}
889-
}
890-
}
891-
}
892-
});
893-
894957
if let Some(listening_address) = &self.config.listening_address {
895958
// Setup networking
896959
let peer_manager_connection_handler = Arc::clone(&self.peer_manager);
@@ -1024,6 +1087,18 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
10241087
}
10251088
});
10261089

1090+
let event_handler = Arc::new(EventHandler::new(
1091+
Arc::clone(&self.wallet),
1092+
Arc::clone(&self.event_queue),
1093+
Arc::clone(&self.channel_manager),
1094+
Arc::clone(&self.network_graph),
1095+
Arc::clone(&self.keys_manager),
1096+
Arc::clone(&self.payment_store),
1097+
Arc::clone(&self.runtime),
1098+
Arc::clone(&self.logger),
1099+
Arc::clone(&self.config),
1100+
));
1101+
10271102
// Setup background processing
10281103
let background_persister = Arc::clone(&self.kv_store);
10291104
let background_event_handler = Arc::clone(&event_handler);

src/test/functional_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ fn onchain_spend_receive() {
390390

391391
#[test]
392392
fn sign_verify_msg() {
393-
let (_, electrsd) = setup_bitcoind_and_electrsd();
393+
let (_bitcoind, electrsd) = setup_bitcoind_and_electrsd();
394394
let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap());
395395
let config = random_config();
396396
let builder = Builder::from_config(config.clone());

src/wallet.rs

-8
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,6 @@ where
7373
}
7474
};
7575

76-
match self.update_fee_estimates().await {
77-
Ok(()) => (),
78-
Err(e) => {
79-
log_error!(self.logger, "Fee estimation error: {}", e);
80-
return Err(e);
81-
}
82-
}
83-
8476
let sync_options = SyncOptions { progress: None };
8577
let wallet_lock = self.inner.lock().unwrap();
8678
let res = match wallet_lock.sync(&self.blockchain, sync_options).await {

0 commit comments

Comments
 (0)