Skip to content

Commit ca44721

Browse files
authored
Merge pull request #281 from tnull/2024-04-improve-on-wallet-friction
Improve on (potentially) blocking wallet behaviors
2 parents b180a65 + f839015 commit ca44721

File tree

10 files changed

+447
-142
lines changed

10 files changed

+447
-142
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ bip39 = "2.0.0"
6464

6565
rand = "0.8.5"
6666
chrono = { version = "0.4", default-features = false, features = ["clock"] }
67-
tokio = { version = "1", default-features = false, features = [ "rt-multi-thread", "time", "sync" ] }
67+
tokio = { version = "1.37", default-features = false, features = [ "rt-multi-thread", "time", "sync" ] }
6868
esplora-client = { version = "0.6", default-features = false }
6969
libc = "0.2"
7070
uniffi = { version = "0.26.0", features = ["build"], optional = true }

bindings/ldk_node.udl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,15 @@ enum NodeError {
165165
"ChannelConfigUpdateFailed",
166166
"PersistenceFailed",
167167
"FeerateEstimationUpdateFailed",
168+
"FeerateEstimationUpdateTimeout",
168169
"WalletOperationFailed",
170+
"WalletOperationTimeout",
169171
"OnchainTxSigningFailed",
170172
"MessageSigningFailed",
171173
"TxSyncFailed",
174+
"TxSyncTimeout",
172175
"GossipUpdateFailed",
176+
"GossipUpdateTimeout",
173177
"LiquidityRequestFailed",
174178
"InvalidAddress",
175179
"InvalidSocketAddress",

src/builder.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::config::{
22
default_user_config, Config, BDK_CLIENT_CONCURRENCY, BDK_CLIENT_STOP_GAP,
3-
DEFAULT_ESPLORA_SERVER_URL, WALLET_KEYS_SEED_LEN,
3+
DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS, DEFAULT_ESPLORA_SERVER_URL, WALLET_KEYS_SEED_LEN,
44
};
55
use crate::connection::ConnectionManager;
66
use crate::event::EventQueue;
@@ -558,10 +558,15 @@ fn build_with_store_internal(
558558

559559
let (blockchain, tx_sync, tx_broadcaster, fee_estimator) = match chain_data_source_config {
560560
Some(ChainDataSourceConfig::Esplora(server_url)) => {
561-
let tx_sync = Arc::new(EsploraSyncClient::new(server_url.clone(), Arc::clone(&logger)));
562-
let blockchain =
563-
EsploraBlockchain::from_client(tx_sync.client().clone(), BDK_CLIENT_STOP_GAP)
564-
.with_concurrency(BDK_CLIENT_CONCURRENCY);
561+
let mut client_builder = esplora_client::Builder::new(&server_url.clone());
562+
client_builder = client_builder.timeout(DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS);
563+
let esplora_client = client_builder.build_async().unwrap();
564+
let tx_sync = Arc::new(EsploraSyncClient::from_client(
565+
esplora_client.clone(),
566+
Arc::clone(&logger),
567+
));
568+
let blockchain = EsploraBlockchain::from_client(esplora_client, BDK_CLIENT_STOP_GAP)
569+
.with_concurrency(BDK_CLIENT_CONCURRENCY);
565570
let tx_broadcaster = Arc::new(TransactionBroadcaster::new(
566571
tx_sync.client().clone(),
567572
Arc::clone(&logger),
@@ -959,6 +964,7 @@ fn build_with_store_internal(
959964
};
960965

961966
let (stop_sender, _) = tokio::sync::watch::channel(());
967+
let (event_handling_stopped_sender, _) = tokio::sync::watch::channel(());
962968

963969
let is_listening = Arc::new(AtomicBool::new(false));
964970
let latest_wallet_sync_timestamp = Arc::new(RwLock::new(None));
@@ -971,6 +977,7 @@ fn build_with_store_internal(
971977
Ok(Node {
972978
runtime,
973979
stop_sender,
980+
event_handling_stopped_sender,
974981
config,
975982
wallet,
976983
tx_sync,

src/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub(crate) const BDK_CLIENT_CONCURRENCY: u8 = 4;
2828
// The default Esplora server we're using.
2929
pub(crate) const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/api";
3030

31+
// The default Esplora client timeout we're using.
32+
pub(crate) const DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS: u64 = 10;
33+
3134
// The timeout after which we abandon retrying failed payments.
3235
pub(crate) const LDK_PAYMENT_RETRY_TIMEOUT: Duration = Duration::from_secs(10);
3336

@@ -46,6 +49,21 @@ pub(crate) const NODE_ANN_BCAST_INTERVAL: Duration = Duration::from_secs(60 * 60
4649
// The lower limit which we apply to any configured wallet sync intervals.
4750
pub(crate) const WALLET_SYNC_INTERVAL_MINIMUM_SECS: u64 = 10;
4851

52+
// The timeout after which we abort a wallet syncing operation.
53+
pub(crate) const BDK_WALLET_SYNC_TIMEOUT_SECS: u64 = 90;
54+
55+
// The timeout after which we abort a wallet syncing operation.
56+
pub(crate) const LDK_WALLET_SYNC_TIMEOUT_SECS: u64 = 30;
57+
58+
// The timeout after which we abort a fee rate cache update operation.
59+
pub(crate) const FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS: u64 = 5;
60+
61+
// The timeout after which we abort a transaction broadcast operation.
62+
pub(crate) const TX_BROADCAST_TIMEOUT_SECS: u64 = 5;
63+
64+
// The timeout after which we abort a RGS sync operation.
65+
pub(crate) const RGS_SYNC_TIMEOUT_SECS: u64 = 5;
66+
4967
// The length in bytes of our wallets' keys seed.
5068
pub(crate) const WALLET_KEYS_SEED_LEN: usize = 64;
5169

src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,24 @@ pub enum Error {
3333
PersistenceFailed,
3434
/// A fee rate estimation update failed.
3535
FeerateEstimationUpdateFailed,
36+
/// A fee rate estimation update timed out.
37+
FeerateEstimationUpdateTimeout,
3638
/// A wallet operation failed.
3739
WalletOperationFailed,
40+
/// A wallet operation timed out.
41+
WalletOperationTimeout,
3842
/// A signing operation for transaction failed.
3943
OnchainTxSigningFailed,
4044
/// A signing operation for message failed.
4145
MessageSigningFailed,
4246
/// A transaction sync operation failed.
4347
TxSyncFailed,
48+
/// A transaction sync operation timed out.
49+
TxSyncTimeout,
4450
/// A gossip updating operation failed.
4551
GossipUpdateFailed,
52+
/// A gossip updating operation timed out.
53+
GossipUpdateTimeout,
4654
/// A liquidity request operation failed.
4755
LiquidityRequestFailed,
4856
/// The given address is invalid.
@@ -111,11 +119,17 @@ impl fmt::Display for Error {
111119
Self::FeerateEstimationUpdateFailed => {
112120
write!(f, "Failed to update fee rate estimates.")
113121
},
122+
Self::FeerateEstimationUpdateTimeout => {
123+
write!(f, "Updating fee rate estimates timed out.")
124+
},
114125
Self::WalletOperationFailed => write!(f, "Failed to conduct wallet operation."),
126+
Self::WalletOperationTimeout => write!(f, "A wallet operation timed out."),
115127
Self::OnchainTxSigningFailed => write!(f, "Failed to sign given transaction."),
116128
Self::MessageSigningFailed => write!(f, "Failed to sign given message."),
117129
Self::TxSyncFailed => write!(f, "Failed to sync transactions."),
130+
Self::TxSyncTimeout => write!(f, "Syncing transactions timed out."),
118131
Self::GossipUpdateFailed => write!(f, "Failed to update gossip data."),
132+
Self::GossipUpdateTimeout => write!(f, "Updating gossip data timed out."),
119133
Self::LiquidityRequestFailed => write!(f, "Failed to request inbound liquidity."),
120134
Self::InvalidAddress => write!(f, "The given address is invalid."),
121135
Self::InvalidSocketAddress => write!(f, "The given network address is invalid."),

src/fee_estimator.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::config::FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS;
12
use crate::logger::{log_error, log_trace, Logger};
23
use crate::{Config, Error};
34

@@ -14,6 +15,7 @@ use bitcoin::Network;
1415
use std::collections::HashMap;
1516
use std::ops::Deref;
1617
use std::sync::{Arc, RwLock};
18+
use std::time::Duration;
1719

1820
pub(crate) struct OnchainFeeEstimator<L: Deref>
1921
where
@@ -55,7 +57,21 @@ where
5557
ConfirmationTarget::OutputSpendingFee => 12,
5658
};
5759

58-
let estimates = self.esplora_client.get_fee_estimates().await.map_err(|e| {
60+
let estimates = tokio::time::timeout(
61+
Duration::from_secs(FEE_RATE_CACHE_UPDATE_TIMEOUT_SECS),
62+
self.esplora_client.get_fee_estimates(),
63+
)
64+
.await
65+
.map_err(|e| {
66+
log_error!(
67+
self.logger,
68+
"Updating fee rate estimates for {:?} timed out: {}",
69+
target,
70+
e
71+
);
72+
Error::FeerateEstimationUpdateTimeout
73+
})?
74+
.map_err(|e| {
5975
log_error!(
6076
self.logger,
6177
"Failed to retrieve fee rate estimates for {:?}: {}",

src/gossip.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::config::RGS_SYNC_TIMEOUT_SECS;
12
use crate::logger::{log_trace, FilesystemLogger, Logger};
23
use crate::types::{GossipSync, Graph, P2PGossipSync, RapidGossipSync};
34
use crate::Error;
@@ -6,6 +7,7 @@ use lightning::routing::utxo::UtxoLookup;
67

78
use std::sync::atomic::{AtomicU32, Ordering};
89
use std::sync::Arc;
10+
use std::time::Duration;
911

1012
pub(crate) enum GossipSource {
1113
P2PNetwork {
@@ -55,7 +57,17 @@ impl GossipSource {
5557
Self::RapidGossipSync { gossip_sync, server_url, latest_sync_timestamp, logger } => {
5658
let query_timestamp = latest_sync_timestamp.load(Ordering::Acquire);
5759
let query_url = format!("{}/{}", server_url, query_timestamp);
58-
let response = reqwest::get(query_url).await.map_err(|e| {
60+
61+
let response = tokio::time::timeout(
62+
Duration::from_secs(RGS_SYNC_TIMEOUT_SECS),
63+
reqwest::get(query_url),
64+
)
65+
.await
66+
.map_err(|e| {
67+
log_trace!(logger, "Retrieving RGS gossip update timed out: {}", e);
68+
Error::GossipUpdateTimeout
69+
})?
70+
.map_err(|e| {
5971
log_trace!(logger, "Failed to retrieve RGS gossip update: {}", e);
6072
Error::GossipUpdateFailed
6173
})?;

0 commit comments

Comments
 (0)