From 423ed511cba07e9181fc9f68301d611ed52f251e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 11:55:50 +0200 Subject: [PATCH 1/9] Replace unreachable `unwrap`s with `expect`s We replace all `unwrap`s with `expect`s. Co-Authored-By: HAL 9000 --- build.rs | 3 +- src/balance.rs | 4 +- src/builder.rs | 68 ++++++++------- src/chain/bitcoind.rs | 28 +++--- src/chain/electrum.rs | 86 ++++++++++--------- src/chain/esplora.rs | 22 +++-- src/chain/mod.rs | 4 +- src/connection.rs | 4 +- src/data_store.rs | 14 +-- src/event.rs | 23 ++--- src/fee_estimator.rs | 4 +- src/ffi/types.rs | 4 +- src/io/sqlite_store/mod.rs | 19 ++-- src/io/vss_store.rs | 32 ++++--- src/lib.rs | 41 +++++---- src/liquidity.rs | 30 ++++--- src/lnurl_auth.rs | 4 +- src/logger.rs | 4 +- src/payment/asynchronous/om_mailbox.rs | 13 +-- .../asynchronous/static_invoice_store.rs | 2 +- src/payment/bolt11.rs | 11 +-- src/payment/bolt12.rs | 16 ++-- src/payment/onchain.rs | 4 +- src/payment/spontaneous.rs | 4 +- src/peer_store.rs | 8 +- src/runtime.rs | 14 +-- src/scoring.rs | 9 +- src/types.rs | 1 + src/wallet/mod.rs | 70 +++++++-------- src/wallet/ser.rs | 49 ++++++++--- 30 files changed, 335 insertions(+), 260 deletions(-) diff --git a/build.rs b/build.rs index f011148e7..2e080ddcd 100644 --- a/build.rs +++ b/build.rs @@ -7,5 +7,6 @@ fn main() { #[cfg(feature = "uniffi")] - uniffi::generate_scaffolding("bindings/ldk_node.udl").unwrap(); + uniffi::generate_scaffolding("bindings/ldk_node.udl") + .expect("the checked-in UniFFI UDL should always generate scaffolding"); } diff --git a/src/balance.rs b/src/balance.rs index 6c6ad946d..2339c83e1 100644 --- a/src/balance.rs +++ b/src/balance.rs @@ -232,7 +232,9 @@ impl LightningBalance { inbound_htlc_rounded_msat, } => { // unwrap safety: confirmed_balance_candidate_index is guaranteed to index into balance_candidates - let balance = balance_candidates.get(confirmed_balance_candidate_index).unwrap(); + let balance = balance_candidates + .get(confirmed_balance_candidate_index) + .expect("LDK should provide a valid confirmed balance candidate index"); Self::ClaimableOnChannelClose { channel_id, diff --git a/src/builder.rs b/src/builder.rs index cd8cc184f..c1c56ff78 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -861,7 +861,7 @@ impl ArcedNodeBuilder { pub fn set_chain_source_esplora( &self, server_url: String, sync_config: Option, ) { - self.inner.write().unwrap().set_chain_source_esplora(server_url, sync_config); + self.inner.write().expect("lock").set_chain_source_esplora(server_url, sync_config); } /// Configures the [`Node`] instance to source its chain data from the given Esplora server. @@ -875,7 +875,7 @@ impl ArcedNodeBuilder { &self, server_url: String, headers: HashMap, sync_config: Option, ) { - self.inner.write().unwrap().set_chain_source_esplora_with_headers( + self.inner.write().expect("lock").set_chain_source_esplora_with_headers( server_url, headers, sync_config, @@ -889,7 +889,7 @@ impl ArcedNodeBuilder { pub fn set_chain_source_electrum( &self, server_url: String, sync_config: Option, ) { - self.inner.write().unwrap().set_chain_source_electrum(server_url, sync_config); + self.inner.write().expect("lock").set_chain_source_electrum(server_url, sync_config); } /// Configures the [`Node`] instance to connect to a Bitcoin Core node via RPC. @@ -903,7 +903,7 @@ impl ArcedNodeBuilder { pub fn set_chain_source_bitcoind_rpc( &self, rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, ) { - self.inner.write().unwrap().set_chain_source_bitcoind_rpc( + self.inner.write().expect("lock").set_chain_source_bitcoind_rpc( rpc_host, rpc_port, rpc_user, @@ -924,7 +924,7 @@ impl ArcedNodeBuilder { &self, rest_host: String, rest_port: u16, rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String, ) { - self.inner.write().unwrap().set_chain_source_bitcoind_rest( + self.inner.write().expect("lock").set_chain_source_bitcoind_rest( rest_host, rest_port, rpc_host, @@ -937,20 +937,20 @@ impl ArcedNodeBuilder { /// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer /// network. pub fn set_gossip_source_p2p(&self) { - self.inner.write().unwrap().set_gossip_source_p2p(); + self.inner.write().expect("lock").set_gossip_source_p2p(); } /// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync /// server. pub fn set_gossip_source_rgs(&self, rgs_server_url: String) { - self.inner.write().unwrap().set_gossip_source_rgs(rgs_server_url); + self.inner.write().expect("lock").set_gossip_source_rgs(rgs_server_url); } /// Configures the [`Node`] instance to source its external scores from the given URL. /// /// The external scores are merged into the local scoring system to improve routing. pub fn set_pathfinding_scores_source(&self, url: String) { - self.inner.write().unwrap().set_pathfinding_scores_source(url); + self.inner.write().expect("lock").set_pathfinding_scores_source(url); } /// Configures the [`Node`] instance to source inbound liquidity from the given @@ -964,7 +964,7 @@ impl ArcedNodeBuilder { pub fn set_liquidity_source_lsps1( &self, node_id: PublicKey, address: SocketAddress, token: Option, ) { - self.inner.write().unwrap().set_liquidity_source_lsps1(node_id, address, token); + self.inner.write().expect("lock").set_liquidity_source_lsps1(node_id, address, token); } /// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given @@ -978,7 +978,7 @@ impl ArcedNodeBuilder { pub fn set_liquidity_source_lsps2( &self, node_id: PublicKey, address: SocketAddress, token: Option, ) { - self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token); + self.inner.write().expect("lock").set_liquidity_source_lsps2(node_id, address, token); } /// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time @@ -988,12 +988,12 @@ impl ArcedNodeBuilder { /// /// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md pub fn set_liquidity_provider_lsps2(&self, service_config: LSPS2ServiceConfig) { - self.inner.write().unwrap().set_liquidity_provider_lsps2(service_config); + self.inner.write().expect("lock").set_liquidity_provider_lsps2(service_config); } /// Sets the used storage directory path. pub fn set_storage_dir_path(&self, storage_dir_path: String) { - self.inner.write().unwrap().set_storage_dir_path(storage_dir_path); + self.inner.write().expect("lock").set_storage_dir_path(storage_dir_path); } /// Configures the [`Node`] instance to write logs to the filesystem. @@ -1012,29 +1012,29 @@ impl ArcedNodeBuilder { pub fn set_filesystem_logger( &self, log_file_path: Option, log_level: Option, ) { - self.inner.write().unwrap().set_filesystem_logger(log_file_path, log_level); + self.inner.write().expect("lock").set_filesystem_logger(log_file_path, log_level); } /// Configures the [`Node`] instance to write logs to the [`log`](https://crates.io/crates/log) facade. pub fn set_log_facade_logger(&self) { - self.inner.write().unwrap().set_log_facade_logger(); + self.inner.write().expect("lock").set_log_facade_logger(); } /// Configures the [`Node`] instance to write logs to the provided custom [`LogWriter`]. pub fn set_custom_logger(&self, log_writer: Arc) { - self.inner.write().unwrap().set_custom_logger(log_writer); + self.inner.write().expect("lock").set_custom_logger(log_writer); } /// Sets the Bitcoin network used. pub fn set_network(&self, network: Network) { - self.inner.write().unwrap().set_network(network); + self.inner.write().expect("lock").set_network(network); } /// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections. pub fn set_listening_addresses( &self, listening_addresses: Vec, ) -> Result<(), BuildError> { - self.inner.write().unwrap().set_listening_addresses(listening_addresses).map(|_| ()) + self.inner.write().expect("lock").set_listening_addresses(listening_addresses).map(|_| ()) } /// Sets the IP address and TCP port which [`Node`] will announce to the gossip network that it accepts connections on. @@ -1045,7 +1045,11 @@ impl ArcedNodeBuilder { pub fn set_announcement_addresses( &self, announcement_addresses: Vec, ) -> Result<(), BuildError> { - self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ()) + self.inner + .write() + .expect("lock") + .set_announcement_addresses(announcement_addresses) + .map(|_| ()) } /// Configures the [`Node`] instance to use a Tor SOCKS proxy for outbound connections to peers with OnionV3 addresses. @@ -1054,7 +1058,7 @@ impl ArcedNodeBuilder { /// /// **Note**: If unset, connecting to peer OnionV3 addresses will fail. pub fn set_tor_config(&self, tor_config: TorConfig) -> Result<(), BuildError> { - self.inner.write().unwrap().set_tor_config(tor_config).map(|_| ()) + self.inner.write().expect("lock").set_tor_config(tor_config).map(|_| ()) } /// Sets the node alias that will be used when broadcasting announcements to the gossip @@ -1062,14 +1066,14 @@ impl ArcedNodeBuilder { /// /// The provided alias must be a valid UTF-8 string and no longer than 32 bytes in total. pub fn set_node_alias(&self, node_alias: String) -> Result<(), BuildError> { - self.inner.write().unwrap().set_node_alias(node_alias).map(|_| ()) + self.inner.write().expect("lock").set_node_alias(node_alias).map(|_| ()) } /// Sets the role of the node in an asynchronous payments context. pub fn set_async_payments_role( &self, role: Option, ) -> Result<(), BuildError> { - self.inner.write().unwrap().set_async_payments_role(role).map(|_| ()) + self.inner.write().expect("lock").set_async_payments_role(role).map(|_| ()) } /// Configures the [`Node`] to resync chain data from genesis on first startup, recovering any @@ -1078,13 +1082,13 @@ impl ArcedNodeBuilder { /// This should only be set on first startup when importing an older wallet from a previously /// used [`NodeEntropy`]. pub fn set_wallet_recovery_mode(&self) { - self.inner.write().unwrap().set_wallet_recovery_mode(); + self.inner.write().expect("lock").set_wallet_recovery_mode(); } /// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options /// previously configured. pub fn build(&self, node_entropy: Arc) -> Result, BuildError> { - self.inner.read().unwrap().build(*node_entropy).map(Arc::new) + self.inner.read().expect("lock").build(*node_entropy).map(Arc::new) } /// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options @@ -1092,7 +1096,7 @@ impl ArcedNodeBuilder { pub fn build_with_fs_store( &self, node_entropy: Arc, ) -> Result, BuildError> { - self.inner.read().unwrap().build_with_fs_store(*node_entropy).map(Arc::new) + self.inner.read().expect("lock").build_with_fs_store(*node_entropy).map(Arc::new) } /// Builds a [`Node`] instance with a [VSS] backend and according to the options @@ -1118,7 +1122,7 @@ impl ArcedNodeBuilder { ) -> Result, BuildError> { self.inner .read() - .unwrap() + .expect("lock") .build_with_vss_store(*node_entropy, vss_url, store_id, fixed_headers) .map(Arc::new) } @@ -1151,7 +1155,7 @@ impl ArcedNodeBuilder { ) -> Result, BuildError> { self.inner .read() - .unwrap() + .expect("lock") .build_with_vss_store_and_lnurl_auth( *node_entropy, vss_url, @@ -1180,7 +1184,7 @@ impl ArcedNodeBuilder { ) -> Result, BuildError> { self.inner .read() - .unwrap() + .expect("lock") .build_with_vss_store_and_fixed_headers(*node_entropy, vss_url, store_id, fixed_headers) .map(Arc::new) } @@ -1203,7 +1207,7 @@ impl ArcedNodeBuilder { let adapter = Arc::new(crate::ffi::VssHeaderProviderAdapter::new(header_provider)); self.inner .read() - .unwrap() + .expect("lock") .build_with_vss_store_and_header_provider(*node_entropy, vss_url, store_id, adapter) .map(Arc::new) } @@ -1214,7 +1218,7 @@ impl ArcedNodeBuilder { pub fn build_with_store( &self, node_entropy: Arc, kv_store: S, ) -> Result, BuildError> { - self.inner.read().unwrap().build_with_store(*node_entropy, kv_store).map(Arc::new) + self.inner.read().expect("lock").build_with_store(*node_entropy, kv_store).map(Arc::new) } } @@ -1610,7 +1614,7 @@ fn build_with_store_internal( // Restore external pathfinding scores from cache if possible. match external_scores_res { Ok(external_scores) => { - scorer.lock().unwrap().merge(external_scores, cur_time); + scorer.lock().expect("lock").merge(external_scores, cur_time); log_trace!(logger, "External scores from cache merged successfully"); }, Err(e) => { @@ -1763,7 +1767,7 @@ fn build_with_store_internal( // Reset the RGS sync timestamp in case we somehow switch gossip sources { - let mut locked_node_metrics = node_metrics.write().unwrap(); + let mut locked_node_metrics = node_metrics.write().expect("lock"); locked_node_metrics.latest_rgs_snapshot_timestamp = None; write_node_metrics(&*locked_node_metrics, &*kv_store, Arc::clone(&logger)) .map_err(|e| { @@ -1775,7 +1779,7 @@ fn build_with_store_internal( }, GossipSourceConfig::RapidGossipSync(rgs_server) => { let latest_sync_timestamp = - node_metrics.read().unwrap().latest_rgs_snapshot_timestamp.unwrap_or(0); + node_metrics.read().expect("lock").latest_rgs_snapshot_timestamp.unwrap_or(0); Arc::new(GossipSource::new_rgs( rgs_server.clone(), latest_sync_timestamp, diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 26924d8af..86266d543 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -132,7 +132,7 @@ impl BitcoindChainSource { // First register for the wallet polling status to make sure `Node::sync_wallets` calls // wait on the result before proceeding. { - let mut status_lock = self.wallet_polling_status.lock().unwrap(); + let mut status_lock = self.wallet_polling_status.lock().expect("lock"); if status_lock.register_or_subscribe_pending_sync().is_some() { debug_assert!(false, "Sync already in progress. This should never happen."); } @@ -197,12 +197,12 @@ impl BitcoindChainSource { log_info!( self.logger, "Finished synchronizing listeners in {}ms", - now.elapsed().unwrap().as_millis() + now.elapsed().expect("system time must not go backwards").as_millis() ); - *self.latest_chain_tip.write().unwrap() = Some(chain_tip); + *self.latest_chain_tip.write().expect("lock") = Some(chain_tip); let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; locked_node_metrics.latest_onchain_wallet_sync_timestamp = @@ -262,7 +262,7 @@ impl BitcoindChainSource { } // Now propagate the initial result to unblock waiting subscribers. - self.wallet_polling_status.lock().unwrap().propagate_result_to_subscribers(Ok(())); + self.wallet_polling_status.lock().expect("lock").propagate_result_to_subscribers(Ok(())); let mut chain_polling_interval = tokio::time::interval(Duration::from_secs(CHAIN_POLLING_INTERVAL_SECS)); @@ -346,7 +346,7 @@ impl BitcoindChainSource { match validate_res { Ok(tip) => { - *self.latest_chain_tip.write().unwrap() = Some(tip); + *self.latest_chain_tip.write().expect("lock") = Some(tip); Ok(tip) }, Err(e) => { @@ -361,7 +361,7 @@ impl BitcoindChainSource { chain_monitor: Arc, output_sweeper: Arc, ) -> Result<(), Error> { let receiver_res = { - let mut status_lock = self.wallet_polling_status.lock().unwrap(); + let mut status_lock = self.wallet_polling_status.lock().expect("lock"); status_lock.register_or_subscribe_pending_sync() }; @@ -383,7 +383,7 @@ impl BitcoindChainSource { ) .await; - self.wallet_polling_status.lock().unwrap().propagate_result_to_subscribers(res); + self.wallet_polling_status.lock().expect("lock").propagate_result_to_subscribers(res); res } @@ -392,7 +392,7 @@ impl BitcoindChainSource { &self, onchain_wallet: Arc, channel_manager: Arc, chain_monitor: Arc, output_sweeper: Arc, ) -> Result<(), Error> { - let latest_chain_tip_opt = self.latest_chain_tip.read().unwrap().clone(); + let latest_chain_tip_opt = self.latest_chain_tip.read().expect("lock").clone(); let chain_tip = if let Some(tip) = latest_chain_tip_opt { tip } else { self.poll_chain_tip().await? }; @@ -413,9 +413,9 @@ impl BitcoindChainSource { log_trace!( self.logger, "Finished polling best tip in {}ms", - now.elapsed().unwrap().as_millis() + now.elapsed().expect("system time must not go backwards").as_millis() ); - *self.latest_chain_tip.write().unwrap() = Some(tip); + *self.latest_chain_tip.write().expect("lock") = Some(tip); }, Ok(_) => {}, Err(e) => { @@ -439,7 +439,7 @@ impl BitcoindChainSource { "Finished polling mempool of size {} and {} evicted transactions in {}ms", unconfirmed_txs.len(), evicted_txids.len(), - now.elapsed().unwrap().as_millis() + now.elapsed().expect("system time must not go backwards").as_millis() ); onchain_wallet.apply_mempool_txs(unconfirmed_txs, evicted_txids).unwrap_or_else( |e| { @@ -455,7 +455,7 @@ impl BitcoindChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; @@ -570,7 +570,7 @@ impl BitcoindChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; } diff --git a/src/chain/electrum.rs b/src/chain/electrum.rs index 7b08c3845..5199c135d 100644 --- a/src/chain/electrum.rs +++ b/src/chain/electrum.rs @@ -76,7 +76,7 @@ impl ElectrumChainSource { } pub(super) fn start(&self, runtime: Arc) -> Result<(), Error> { - self.electrum_runtime_status.write().unwrap().start( + self.electrum_runtime_status.write().expect("lock").start( self.server_url.clone(), self.sync_config.clone(), Arc::clone(&runtime), @@ -86,14 +86,14 @@ impl ElectrumChainSource { } pub(super) fn stop(&self) { - self.electrum_runtime_status.write().unwrap().stop(); + self.electrum_runtime_status.write().expect("lock").stop(); } pub(crate) async fn sync_onchain_wallet( &self, onchain_wallet: Arc, ) -> Result<(), Error> { let receiver_res = { - let mut status_lock = self.onchain_wallet_sync_status.lock().unwrap(); + let mut status_lock = self.onchain_wallet_sync_status.lock().expect("lock"); status_lock.register_or_subscribe_pending_sync() }; if let Some(mut sync_receiver) = receiver_res { @@ -107,26 +107,27 @@ impl ElectrumChainSource { let res = self.sync_onchain_wallet_inner(onchain_wallet).await; - self.onchain_wallet_sync_status.lock().unwrap().propagate_result_to_subscribers(res); + self.onchain_wallet_sync_status.lock().expect("lock").propagate_result_to_subscribers(res); res } async fn sync_onchain_wallet_inner(&self, onchain_wallet: Arc) -> Result<(), Error> { - let electrum_client: Arc = - if let Some(client) = self.electrum_runtime_status.read().unwrap().client().as_ref() { - Arc::clone(client) - } else { - debug_assert!( - false, - "We should have started the chain source before syncing the onchain wallet" - ); - return Err(Error::FeerateEstimationUpdateFailed); - }; + let electrum_client: Arc = if let Some(client) = + self.electrum_runtime_status.read().expect("lock").client().as_ref() + { + Arc::clone(client) + } else { + debug_assert!( + false, + "We should have started the chain source before syncing the onchain wallet" + ); + return Err(Error::FeerateEstimationUpdateFailed); + }; // If this is our first sync, do a full scan with the configured gap limit. // Otherwise just do an incremental sync. let incremental_sync = - self.node_metrics.read().unwrap().latest_onchain_wallet_sync_timestamp.is_some(); + self.node_metrics.read().expect("lock").latest_onchain_wallet_sync_timestamp.is_some(); let apply_wallet_update = |update_res: Result, now: Instant| match update_res { @@ -141,7 +142,7 @@ impl ElectrumChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; write_node_metrics( @@ -184,7 +185,7 @@ impl ElectrumChainSource { output_sweeper: Arc, ) -> Result<(), Error> { let receiver_res = { - let mut status_lock = self.lightning_wallet_sync_status.lock().unwrap(); + let mut status_lock = self.lightning_wallet_sync_status.lock().expect("lock"); status_lock.register_or_subscribe_pending_sync() }; if let Some(mut sync_receiver) = receiver_res { @@ -199,7 +200,10 @@ impl ElectrumChainSource { let res = self.sync_lightning_wallet_inner(channel_manager, chain_monitor, output_sweeper).await; - self.lightning_wallet_sync_status.lock().unwrap().propagate_result_to_subscribers(res); + self.lightning_wallet_sync_status + .lock() + .expect("lock") + .propagate_result_to_subscribers(res); res } @@ -217,16 +221,17 @@ impl ElectrumChainSource { sync_sweeper as Arc, ]; - let electrum_client: Arc = - if let Some(client) = self.electrum_runtime_status.read().unwrap().client().as_ref() { - Arc::clone(client) - } else { - debug_assert!( - false, - "We should have started the chain source before syncing the lightning wallet" - ); - return Err(Error::TxSyncFailed); - }; + let electrum_client: Arc = if let Some(client) = + self.electrum_runtime_status.read().expect("lock").client().as_ref() + { + Arc::clone(client) + } else { + debug_assert!( + false, + "We should have started the chain source before syncing the lightning wallet" + ); + return Err(Error::TxSyncFailed); + }; let res = electrum_client.sync_confirmables(confirmables).await; @@ -234,7 +239,7 @@ impl ElectrumChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; } @@ -245,7 +250,7 @@ impl ElectrumChainSource { pub(crate) async fn update_fee_rate_estimates(&self) -> Result<(), Error> { let electrum_client: Arc = if let Some(client) = - self.electrum_runtime_status.read().unwrap().client().as_ref() + self.electrum_runtime_status.read().expect("lock").client().as_ref() { Arc::clone(client) } else { @@ -267,7 +272,7 @@ impl ElectrumChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; } @@ -276,13 +281,14 @@ impl ElectrumChainSource { } pub(crate) async fn process_broadcast_package(&self, package: Vec) { - let electrum_client: Arc = - if let Some(client) = self.electrum_runtime_status.read().unwrap().client().as_ref() { - Arc::clone(client) - } else { - debug_assert!(false, "We should have started the chain source before broadcasting"); - return; - }; + let electrum_client: Arc = if let Some(client) = + self.electrum_runtime_status.read().expect("lock").client().as_ref() + { + Arc::clone(client) + } else { + debug_assert!(false, "We should have started the chain source before broadcasting"); + return; + }; for tx in package { electrum_client.broadcast(tx).await; @@ -292,10 +298,10 @@ impl ElectrumChainSource { impl Filter for ElectrumChainSource { fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { - self.electrum_runtime_status.write().unwrap().register_tx(txid, script_pubkey) + self.electrum_runtime_status.write().expect("lock").register_tx(txid, script_pubkey) } fn register_output(&self, output: lightning::chain::WatchedOutput) { - self.electrum_runtime_status.write().unwrap().register_output(output) + self.electrum_runtime_status.write().expect("lock").register_output(output) } } diff --git a/src/chain/esplora.rs b/src/chain/esplora.rs index 245db72f6..0b91fb606 100644 --- a/src/chain/esplora.rs +++ b/src/chain/esplora.rs @@ -54,7 +54,8 @@ impl EsploraChainSource { client_builder = client_builder.header(header_name, header_value); } - let esplora_client = client_builder.build_async().unwrap(); + let esplora_client = + client_builder.build_async().expect("esplora client build must succeed"); let tx_sync = Arc::new(EsploraSyncClient::from_client(esplora_client.clone(), Arc::clone(&logger))); @@ -78,7 +79,7 @@ impl EsploraChainSource { &self, onchain_wallet: Arc, ) -> Result<(), Error> { let receiver_res = { - let mut status_lock = self.onchain_wallet_sync_status.lock().unwrap(); + let mut status_lock = self.onchain_wallet_sync_status.lock().expect("lock"); status_lock.register_or_subscribe_pending_sync() }; if let Some(mut sync_receiver) = receiver_res { @@ -92,7 +93,7 @@ impl EsploraChainSource { let res = self.sync_onchain_wallet_inner(onchain_wallet).await; - self.onchain_wallet_sync_status.lock().unwrap().propagate_result_to_subscribers(res); + self.onchain_wallet_sync_status.lock().expect("lock").propagate_result_to_subscribers(res); res } @@ -101,7 +102,7 @@ impl EsploraChainSource { // If this is our first sync, do a full scan with the configured gap limit. // Otherwise just do an incremental sync. let incremental_sync = - self.node_metrics.read().unwrap().latest_onchain_wallet_sync_timestamp.is_some(); + self.node_metrics.read().expect("lock").latest_onchain_wallet_sync_timestamp.is_some(); macro_rules! get_and_apply_wallet_update { ($sync_future: expr) => {{ @@ -121,7 +122,7 @@ impl EsploraChainSource { .ok() .map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_onchain_wallet_sync_timestamp = unix_time_secs_opt; write_node_metrics( &*locked_node_metrics, @@ -207,7 +208,7 @@ impl EsploraChainSource { output_sweeper: Arc, ) -> Result<(), Error> { let receiver_res = { - let mut status_lock = self.lightning_wallet_sync_status.lock().unwrap(); + let mut status_lock = self.lightning_wallet_sync_status.lock().expect("lock"); status_lock.register_or_subscribe_pending_sync() }; if let Some(mut sync_receiver) = receiver_res { @@ -222,7 +223,10 @@ impl EsploraChainSource { let res = self.sync_lightning_wallet_inner(channel_manager, chain_monitor, output_sweeper).await; - self.lightning_wallet_sync_status.lock().unwrap().propagate_result_to_subscribers(res); + self.lightning_wallet_sync_status + .lock() + .expect("lock") + .propagate_result_to_subscribers(res); res } @@ -259,7 +263,7 @@ impl EsploraChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_lightning_wallet_sync_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; @@ -344,7 +348,7 @@ impl EsploraChainSource { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = self.node_metrics.write().unwrap(); + let mut locked_node_metrics = self.node_metrics.write().expect("lock"); locked_node_metrics.latest_fee_rate_cache_update_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*self.kv_store, &*self.logger)?; } diff --git a/src/chain/mod.rs b/src/chain/mod.rs index 49c011a78..e1cbf08ac 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -215,7 +215,7 @@ impl ChainSource { } pub(crate) fn registered_txids(&self) -> Vec { - self.registered_txids.lock().unwrap().clone() + self.registered_txids.lock().expect("lock").clone() } pub(crate) fn is_transaction_based(&self) -> bool { @@ -472,7 +472,7 @@ impl ChainSource { impl Filter for ChainSource { fn register_tx(&self, txid: &Txid, script_pubkey: &Script) { - self.registered_txids.lock().unwrap().push(*txid); + self.registered_txids.lock().expect("lock").push(*txid); match &self.kind { ChainSourceKind::Esplora(esplora_chain_source) => { esplora_chain_source.register_tx(txid, script_pubkey) diff --git a/src/connection.rs b/src/connection.rs index a1d24e36d..b8946ffe3 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -238,7 +238,7 @@ where fn register_or_subscribe_pending_connection( &self, node_id: &PublicKey, ) -> Option>> { - let mut pending_connections_lock = self.pending_connections.lock().unwrap(); + let mut pending_connections_lock = self.pending_connections.lock().expect("lock"); match pending_connections_lock.entry(*node_id) { hash_map::Entry::Occupied(mut entry) => { let (tx, rx) = tokio::sync::oneshot::channel(); @@ -254,7 +254,7 @@ where fn propagate_result_to_subscribers(&self, node_id: &PublicKey, res: Result<(), Error>) { // Send the result to any other tasks that might be waiting on it by now. - let mut pending_connections_lock = self.pending_connections.lock().unwrap(); + let mut pending_connections_lock = self.pending_connections.lock().expect("lock"); if let Some(connection_ready_senders) = pending_connections_lock.remove(node_id) { for sender in connection_ready_senders { let _ = sender.send(res).map_err(|e| { diff --git a/src/data_store.rs b/src/data_store.rs index ac5c78fb7..f80ec0891 100644 --- a/src/data_store.rs +++ b/src/data_store.rs @@ -65,7 +65,7 @@ where } pub(crate) fn insert(&self, object: SO) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); + let mut locked_objects = self.objects.lock().expect("lock"); self.persist(&object)?; let updated = locked_objects.insert(object.id(), object).is_some(); @@ -73,7 +73,7 @@ where } pub(crate) fn insert_or_update(&self, object: SO) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); + let mut locked_objects = self.objects.lock().expect("lock"); let updated; match locked_objects.entry(object.id()) { @@ -95,7 +95,7 @@ where } pub(crate) fn remove(&self, id: &SO::Id) -> Result<(), Error> { - let removed = self.objects.lock().unwrap().remove(id).is_some(); + let removed = self.objects.lock().expect("lock").remove(id).is_some(); if removed { let store_key = id.encode_to_hex_str(); KVStoreSync::remove( @@ -121,11 +121,11 @@ where } pub(crate) fn get(&self, id: &SO::Id) -> Option { - self.objects.lock().unwrap().get(id).cloned() + self.objects.lock().expect("lock").get(id).cloned() } pub(crate) fn update(&self, update: SO::Update) -> Result { - let mut locked_objects = self.objects.lock().unwrap(); + let mut locked_objects = self.objects.lock().expect("lock"); if let Some(object) = locked_objects.get_mut(&update.id()) { let updated = object.update(update); @@ -141,7 +141,7 @@ where } pub(crate) fn list_filter bool>(&self, f: F) -> Vec { - self.objects.lock().unwrap().values().filter(f).cloned().collect::>() + self.objects.lock().expect("lock").values().filter(f).cloned().collect::>() } fn persist(&self, object: &SO) -> Result<(), Error> { @@ -169,7 +169,7 @@ where } pub(crate) fn contains_key(&self, id: &SO::Id) -> bool { - self.objects.lock().unwrap().contains_key(id) + self.objects.lock().expect("lock").contains_key(id) } } diff --git a/src/event.rs b/src/event.rs index ebaf89dac..17dd158b2 100644 --- a/src/event.rs +++ b/src/event.rs @@ -370,21 +370,21 @@ where pub(crate) async fn add_event(&self, event: Event) -> Result<(), Error> { let data = { - let mut locked_queue = self.queue.lock().unwrap(); + let mut locked_queue = self.queue.lock().expect("lock"); locked_queue.push_back(event); EventQueueSerWrapper(&locked_queue).encode() }; self.persist_queue(data).await?; - if let Some(waker) = self.waker.lock().unwrap().take() { + if let Some(waker) = self.waker.lock().expect("lock").take() { waker.wake(); } Ok(()) } pub(crate) fn next_event(&self) -> Option { - let locked_queue = self.queue.lock().unwrap(); + let locked_queue = self.queue.lock().expect("lock"); locked_queue.front().cloned() } @@ -394,14 +394,14 @@ where pub(crate) async fn event_handled(&self) -> Result<(), Error> { let data = { - let mut locked_queue = self.queue.lock().unwrap(); + let mut locked_queue = self.queue.lock().expect("lock"); locked_queue.pop_front(); EventQueueSerWrapper(&locked_queue).encode() }; self.persist_queue(data).await?; - if let Some(waker) = self.waker.lock().unwrap().take() { + if let Some(waker) = self.waker.lock().expect("lock").take() { waker.wake(); } Ok(()) @@ -485,10 +485,10 @@ impl Future for EventFuture { fn poll( self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> core::task::Poll { - if let Some(event) = self.event_queue.lock().unwrap().front() { + if let Some(event) = self.event_queue.lock().expect("lock").front() { Poll::Ready(event.clone()) } else { - *self.waker.lock().unwrap() = Some(cx.waker().clone()); + *self.waker.lock().expect("lock") = Some(cx.waker().clone()); Poll::Pending } } @@ -1095,7 +1095,7 @@ where self.logger, "Successfully sent payment of {}msat{} from \ payment hash {:?} with preimage {:?}", - payment.amount_msat.unwrap(), + payment.amount_msat.expect("payment amount should be set"), if let Some(fee) = fee_paid_msat { format!(" (fee {} msat)", fee) } else { @@ -1256,7 +1256,9 @@ where } let user_channel_id: u128 = u128::from_ne_bytes( - self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + self.keys_manager.get_secure_random_bytes()[..16] + .try_into() + .expect("slice is exactly 16 bytes"), ); let allow_0conf = self.config.trusted_peers_0conf.contains(&counterparty_node_id); let mut channel_override_config = None; @@ -1450,7 +1452,8 @@ where let event = Event::ChannelPending { channel_id, user_channel_id: UserChannelId(user_channel_id), - former_temporary_channel_id: former_temporary_channel_id.unwrap(), + former_temporary_channel_id: former_temporary_channel_id + .expect("former temporary channel id should be set"), counterparty_node_id, funding_txo, }; diff --git a/src/fee_estimator.rs b/src/fee_estimator.rs index b787ecd33..34fe7b64c 100644 --- a/src/fee_estimator.rs +++ b/src/fee_estimator.rs @@ -48,7 +48,7 @@ impl OnchainFeeEstimator { pub(crate) fn set_fee_rate_cache( &self, fee_rate_cache_update: HashMap, ) -> bool { - let mut locked_fee_rate_cache = self.fee_rate_cache.write().unwrap(); + let mut locked_fee_rate_cache = self.fee_rate_cache.write().expect("lock"); if fee_rate_cache_update != *locked_fee_rate_cache { *locked_fee_rate_cache = fee_rate_cache_update; true @@ -60,7 +60,7 @@ impl OnchainFeeEstimator { impl FeeEstimator for OnchainFeeEstimator { fn estimate_fee_rate(&self, confirmation_target: ConfirmationTarget) -> FeeRate { - let locked_fee_rate_cache = self.fee_rate_cache.read().unwrap(); + let locked_fee_rate_cache = self.fee_rate_cache.read().expect("lock"); let fallback_sats_kwu = get_fallback_rate_for_target(confirmation_target); diff --git a/src/ffi/types.rs b/src/ffi/types.rs index 6fe95a2b3..ad293bc3e 100644 --- a/src/ffi/types.rs +++ b/src/ffi/types.rs @@ -918,7 +918,9 @@ uniffi::custom_type!(PaymentHash, String, { } }, lower: |obj| { - Sha256::from_slice(&obj.0).unwrap().to_string() + Sha256::from_slice(&obj.0) + .expect("PaymentHash should always contain exactly 32 bytes") + .to_string() }, }); diff --git a/src/io/sqlite_store/mod.rs b/src/io/sqlite_store/mod.rs index 94e8360fc..a743a2f1f 100644 --- a/src/io/sqlite_store/mod.rs +++ b/src/io/sqlite_store/mod.rs @@ -288,7 +288,8 @@ impl SqliteStoreInner { })?; let sql = format!("SELECT user_version FROM pragma_user_version"); - let version_res: u16 = connection.query_row(&sql, [], |row| row.get(0)).unwrap(); + let version_res: u16 = + connection.query_row(&sql, [], |row| row.get(0)).expect("pragma query must succeed"); if version_res == 0 { // New database, set our SCHEMA_USER_VERSION and continue @@ -364,7 +365,7 @@ impl SqliteStoreInner { } fn get_inner_lock_ref(&self, locking_key: String) -> Arc> { - let mut outer_lock = self.write_version_locks.lock().unwrap(); + let mut outer_lock = self.write_version_locks.lock().expect("lock"); Arc::clone(&outer_lock.entry(locking_key).or_default()) } @@ -373,7 +374,7 @@ impl SqliteStoreInner { ) -> io::Result> { check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "read")?; - let locked_conn = self.connection.lock().unwrap(); + let locked_conn = self.connection.lock().expect("lock"); let sql = format!("SELECT value FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace AND key=:key;", self.kv_table_name); @@ -423,7 +424,7 @@ impl SqliteStoreInner { check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "write")?; self.execute_locked_write(inner_lock_ref, locking_key, version, || { - let locked_conn = self.connection.lock().unwrap(); + let locked_conn = self.connection.lock().expect("lock"); let sort_order = self.next_sort_order.fetch_add(1, Ordering::Relaxed); @@ -467,7 +468,7 @@ impl SqliteStoreInner { check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "remove")?; self.execute_locked_write(inner_lock_ref, locking_key, version, || { - let locked_conn = self.connection.lock().unwrap(); + let locked_conn = self.connection.lock().expect("lock"); let sql = format!("DELETE FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace AND key=:key;", self.kv_table_name); @@ -500,7 +501,7 @@ impl SqliteStoreInner { ) -> io::Result> { check_namespace_key_validity(primary_namespace, secondary_namespace, None, "list")?; - let locked_conn = self.connection.lock().unwrap(); + let locked_conn = self.connection.lock().expect("lock"); let sql = format!( "SELECT key FROM {} WHERE primary_namespace=:primary_namespace AND secondary_namespace=:secondary_namespace", @@ -546,7 +547,7 @@ impl SqliteStoreInner { "list_paginated", )?; - let locked_conn = self.connection.lock().unwrap(); + let locked_conn = self.connection.lock().expect("lock"); // Fetch one extra row beyond PAGE_SIZE to determine whether a next page exists. let fetch_limit = (PAGE_SIZE + 1) as i64; @@ -644,7 +645,7 @@ impl SqliteStoreInner { &self, inner_lock_ref: Arc>, locking_key: String, version: u64, callback: F, ) -> Result<(), lightning::io::Error> { let res = { - let mut last_written_version = inner_lock_ref.lock().unwrap(); + let mut last_written_version = inner_lock_ref.lock().expect("lock"); // Check if we already have a newer version written/removed. This is used in async contexts to realize eventual // consistency. @@ -670,7 +671,7 @@ impl SqliteStoreInner { // to prevent leaking memory. The two arcs that are expected are the one in the map and the one held here in // inner_lock_ref. The outer lock is obtained first, to avoid a new arc being cloned after we've already // counted. - let mut outer_lock = self.write_version_locks.lock().unwrap(); + let mut outer_lock = self.write_version_locks.lock().expect("lock"); let strong_count = Arc::strong_count(&inner_lock_ref); debug_assert!(strong_count >= 2, "Unexpected SqliteStore strong count"); diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index 2f7a689b2..324c611f7 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -110,7 +110,7 @@ impl VssStore { .worker_threads(INTERNAL_RUNTIME_WORKERS) .max_blocking_threads(INTERNAL_RUNTIME_WORKERS) .build() - .unwrap(); + .expect("tokio runtime build must succeed"); let (data_encryption_key, obfuscation_master_key) = derive_data_encryption_and_obfuscation_keys(&vss_seed); @@ -419,7 +419,7 @@ impl VssStoreInner { } fn get_inner_lock_ref(&self, locking_key: String) -> Arc> { - let mut outer_lock = self.locks.lock().unwrap(); + let mut outer_lock = self.locks.lock().expect("lock"); Arc::clone(&outer_lock.entry(locking_key).or_default()) } @@ -526,13 +526,15 @@ impl VssStoreInner { // unwrap safety: resp.value must be always present for a non-erroneous VSS response, otherwise // it is an API-violation which is converted to [`VssError::InternalServerError`] in [`VssClient`] - let storable = Storable::decode(&resp.value.unwrap().value[..]).map_err(|e| { - let msg = format!( - "Failed to decode data read from key {}/{}/{}: {}", - primary_namespace, secondary_namespace, key, e - ); - Error::new(ErrorKind::Other, msg) - })?; + let storable = + Storable::decode(&resp.value.expect("VSS response must contain a value").value[..]) + .map_err(|e| { + let msg = format!( + "Failed to decode data read from key {}/{}/{}: {}", + primary_namespace, secondary_namespace, key, e + ); + Error::new(ErrorKind::Other, msg) + })?; let storable_builder = StorableBuilder::new(VssEntropySource(&self.entropy_source)); let aad = @@ -672,7 +674,7 @@ impl VssStoreInner { // to prevent leaking memory. The two arcs that are expected are the one in the map and the one held here in // inner_lock_ref. The outer lock is obtained first, to avoid a new arc being cloned after we've already // counted. - let mut outer_lock = self.locks.lock().unwrap(); + let mut outer_lock = self.locks.lock().expect("lock"); let strong_count = Arc::strong_count(&inner_lock_ref); debug_assert!(strong_count >= 2, "Unexpected VssStore strong count"); @@ -739,10 +741,12 @@ async fn determine_and_write_schema_version( // unwrap safety: resp.value must be always present for a non-erroneous VSS response, otherwise // it is an API-violation which is converted to [`VssError::InternalServerError`] in [`VssClient`] - let storable = Storable::decode(&resp.value.unwrap().value[..]).map_err(|e| { - let msg = format!("Failed to decode schema version: {}", e); - Error::new(ErrorKind::Other, msg) - })?; + let storable = + Storable::decode(&resp.value.expect("VSS response must contain a value").value[..]) + .map_err(|e| { + let msg = format!("Failed to decode schema version: {}", e); + Error::new(ErrorKind::Other, msg) + })?; let storable_builder = StorableBuilder::new(VssEntropySource(entropy_source)); // Schema version was added starting with V1, so if set at all, we use the key as `aad` diff --git a/src/lib.rs b/src/lib.rs index dbaffae1c..9df83b7d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,7 +254,7 @@ impl Node { /// a thread-safe manner. pub fn start(&self) -> Result<(), Error> { // Acquire a run lock and hold it until we're setup. - let mut is_running_lock = self.is_running.write().unwrap(); + let mut is_running_lock = self.is_running.write().expect("lock"); if *is_running_lock { return Err(Error::AlreadyRunning); } @@ -322,7 +322,7 @@ impl Node { now.elapsed().as_millis() ); { - let mut locked_node_metrics = gossip_node_metrics.write().unwrap(); + let mut locked_node_metrics = gossip_node_metrics.write().expect("lock"); locked_node_metrics.latest_rgs_snapshot_timestamp = Some(updated_timestamp); write_node_metrics(&*locked_node_metrics, &*gossip_sync_store, Arc::clone(&gossip_sync_logger)) .unwrap_or_else(|e| { @@ -420,13 +420,16 @@ impl Node { break; } res = listener.accept() => { + #[allow(clippy::unwrap_used)] let tcp_stream = res.unwrap().0; let peer_mgr = Arc::clone(&peer_mgr); runtime.spawn_cancellable_background_task(async move { + #[allow(clippy::unwrap_used)] + let tcp_stream = tcp_stream.into_std().unwrap(); lightning_net_tokio::setup_inbound( Arc::clone(&peer_mgr), - tcp_stream.into_std().unwrap(), - ) + tcp_stream, + ) .await; }); } @@ -498,7 +501,7 @@ impl Node { return; } _ = interval.tick() => { - let skip_broadcast = match bcast_node_metrics.read().unwrap().latest_node_announcement_broadcast_timestamp { + let skip_broadcast = match bcast_node_metrics.read().expect("lock").latest_node_announcement_broadcast_timestamp { Some(latest_bcast_time_secs) => { // Skip if the time hasn't elapsed yet. let next_bcast_unix_time = SystemTime::UNIX_EPOCH + Duration::from_secs(latest_bcast_time_secs) + NODE_ANN_BCAST_INTERVAL; @@ -539,7 +542,7 @@ impl Node { let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs()); { - let mut locked_node_metrics = bcast_node_metrics.write().unwrap(); + let mut locked_node_metrics = bcast_node_metrics.write().expect("lock"); locked_node_metrics.latest_node_announcement_broadcast_timestamp = unix_time_secs_opt; write_node_metrics(&*locked_node_metrics, &*bcast_store, Arc::clone(&bcast_logger)) .unwrap_or_else(|e| { @@ -646,7 +649,13 @@ impl Node { Some(background_scorer), sleeper, true, - || Some(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap()), + || { + Some( + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("current time should not be earlier than the Unix epoch"), + ) + }, ) .await .unwrap_or_else(|e| { @@ -684,7 +693,7 @@ impl Node { /// /// After this returns most API methods will return [`Error::NotRunning`]. pub fn stop(&self) -> Result<(), Error> { - let mut is_running_lock = self.is_running.write().unwrap(); + let mut is_running_lock = self.is_running.write().expect("lock"); if !*is_running_lock { return Err(Error::NotRunning); } @@ -748,9 +757,9 @@ impl Node { /// Returns the status of the [`Node`]. pub fn status(&self) -> NodeStatus { - let is_running = *self.is_running.read().unwrap(); + let is_running = *self.is_running.read().expect("lock"); let current_best_block = self.channel_manager.current_best_block().into(); - let locked_node_metrics = self.node_metrics.read().unwrap(); + let locked_node_metrics = self.node_metrics.read().expect("lock"); let latest_lightning_wallet_sync_timestamp = locked_node_metrics.latest_lightning_wallet_sync_timestamp; let latest_onchain_wallet_sync_timestamp = @@ -1079,7 +1088,7 @@ impl Node { pub fn connect( &self, node_id: PublicKey, address: SocketAddress, persist: bool, ) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -1109,7 +1118,7 @@ impl Node { /// Will also remove the peer from the peer store, i.e., after this has been called we won't /// try to reconnect on restart. pub fn disconnect(&self, counterparty_node_id: PublicKey) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -1131,7 +1140,7 @@ impl Node { push_to_counterparty_msat: Option, channel_config: Option, announce_for_forwarding: bool, set_0reserve: bool, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -1194,7 +1203,9 @@ impl Node { let push_msat = push_to_counterparty_msat.unwrap_or(0); let user_channel_id: u128 = u128::from_ne_bytes( - self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + self.keys_manager.get_secure_random_bytes()[..16] + .try_into() + .expect("a 16-byte slice should convert into a [u8; 16]"), ); let result = if set_0reserve { @@ -1727,7 +1738,7 @@ impl Node { /// /// [`EsploraSyncConfig::background_sync_config`]: crate::config::EsploraSyncConfig::background_sync_config pub fn sync_wallets(&self) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } diff --git a/src/liquidity.rs b/src/liquidity.rs index ca859286f..5dbd35b62 100644 --- a/src/liquidity.rs +++ b/src/liquidity.rs @@ -306,7 +306,7 @@ where L::Target: LdkLogger, { pub(crate) fn set_peer_manager(&self, peer_manager: Weak) { - *self.peer_manager.write().unwrap() = Some(peer_manager); + *self.peer_manager.write().expect("lock") = Some(peer_manager); } pub(crate) fn liquidity_manager(&self) -> Arc { @@ -411,7 +411,7 @@ where if let Some(sender) = lsps1_client .pending_opening_params_requests .lock() - .unwrap() + .expect("lock") .remove(&request_id) { let response = LSPS1OpeningParamsResponse { supported_options }; @@ -467,7 +467,7 @@ where if let Some(sender) = lsps1_client .pending_create_order_requests .lock() - .unwrap() + .expect("lock") .remove(&request_id) { let response = LSPS1OrderStatus { @@ -525,7 +525,7 @@ where if let Some(sender) = lsps1_client .pending_check_order_status_requests .lock() - .unwrap() + .expect("lock") .remove(&request_id) { let response = LSPS1OrderStatus { @@ -646,7 +646,9 @@ where }; let user_channel_id: u128 = u128::from_ne_bytes( - self.keys_manager.get_secure_random_bytes()[..16].try_into().unwrap(), + self.keys_manager.get_secure_random_bytes()[..16] + .try_into() + .expect("a 16-byte slice should convert into a [u8; 16]"), ); let intercept_scid = self.channel_manager.get_intercept_scid(); @@ -721,7 +723,7 @@ where }; let init_features = if let Some(Some(peer_manager)) = - self.peer_manager.read().unwrap().as_ref().map(|weak| weak.upgrade()) + self.peer_manager.read().expect("lock").as_ref().map(|weak| weak.upgrade()) { // Fail if we're not connected to the prospective channel partner. if let Some(peer) = peer_manager.peer_by_node_id(&their_network_key) { @@ -848,7 +850,7 @@ where } if let Some(sender) = - lsps2_client.pending_fee_requests.lock().unwrap().remove(&request_id) + lsps2_client.pending_fee_requests.lock().expect("lock").remove(&request_id) { let response = LSPS2FeeResponse { opening_fee_params_menu }; @@ -900,7 +902,7 @@ where } if let Some(sender) = - lsps2_client.pending_buy_requests.lock().unwrap().remove(&request_id) + lsps2_client.pending_buy_requests.lock().expect("lock").remove(&request_id) { let response = LSPS2BuyResponse { intercept_scid, cltv_expiry_delta }; @@ -950,7 +952,7 @@ where let (request_sender, request_receiver) = oneshot::channel(); { let mut pending_opening_params_requests_lock = - lsps1_client.pending_opening_params_requests.lock().unwrap(); + lsps1_client.pending_opening_params_requests.lock().expect("lock"); let request_id = client_handler.request_supported_options(lsps1_client.lsp_node_id); pending_opening_params_requests_lock.insert(request_id, request_sender); } @@ -1033,7 +1035,7 @@ where let request_id; { let mut pending_create_order_requests_lock = - lsps1_client.pending_create_order_requests.lock().unwrap(); + lsps1_client.pending_create_order_requests.lock().expect("lock"); request_id = client_handler.create_order( &lsps1_client.lsp_node_id, order_params.clone(), @@ -1079,7 +1081,7 @@ where let (request_sender, request_receiver) = oneshot::channel(); { let mut pending_check_order_status_requests_lock = - lsps1_client.pending_check_order_status_requests.lock().unwrap(); + lsps1_client.pending_check_order_status_requests.lock().expect("lock"); let request_id = client_handler.check_order_status(&lsps1_client.lsp_node_id, order_id); pending_check_order_status_requests_lock.insert(request_id, request_sender); } @@ -1220,7 +1222,8 @@ where let (fee_request_sender, fee_request_receiver) = oneshot::channel(); { - let mut pending_fee_requests_lock = lsps2_client.pending_fee_requests.lock().unwrap(); + let mut pending_fee_requests_lock = + lsps2_client.pending_fee_requests.lock().expect("lock"); let request_id = client_handler .request_opening_params(lsps2_client.lsp_node_id, lsps2_client.token.clone()); pending_fee_requests_lock.insert(request_id, fee_request_sender); @@ -1253,7 +1256,8 @@ where let (buy_request_sender, buy_request_receiver) = oneshot::channel(); { - let mut pending_buy_requests_lock = lsps2_client.pending_buy_requests.lock().unwrap(); + let mut pending_buy_requests_lock = + lsps2_client.pending_buy_requests.lock().expect("lock"); let request_id = client_handler .select_opening_params(lsps2_client.lsp_node_id, amount_msat, opening_fee_params) .map_err(|e| { diff --git a/src/lnurl_auth.rs b/src/lnurl_auth.rs index 1f95b77b1..1ce44a7c3 100644 --- a/src/lnurl_auth.rs +++ b/src/lnurl_auth.rs @@ -189,7 +189,9 @@ fn linking_key_path(hashing_key: &[u8; 32], domain_name: &str) -> Vec= Self::MAX_MESSAGES_PER_PEER { @@ -27,8 +27,11 @@ impl OnionMessageMailbox { // Enforce a peers limit. If exceeded, evict the peer with the longest queue. if map.len() > Self::MAX_PEERS { - let peer_to_remove = - map.iter().max_by_key(|(_, queue)| queue.len()).map(|(peer, _)| *peer).unwrap(); + let peer_to_remove = map + .iter() + .max_by_key(|(_, queue)| queue.len()) + .map(|(peer, _)| *peer) + .expect("map is non-empty"); map.remove(&peer_to_remove); } @@ -37,7 +40,7 @@ impl OnionMessageMailbox { pub(crate) fn onion_message_peer_connected( &self, peer_node_id: PublicKey, ) -> Vec { - let mut map = self.map.lock().unwrap(); + let mut map = self.map.lock().expect("lock"); if let Some(queue) = map.remove(&peer_node_id) { queue.into() @@ -48,7 +51,7 @@ impl OnionMessageMailbox { #[cfg(test)] pub(crate) fn is_empty(&self) -> bool { - let map = self.map.lock().unwrap(); + let map = self.map.lock().expect("lock"); map.is_empty() } } diff --git a/src/payment/asynchronous/static_invoice_store.rs b/src/payment/asynchronous/static_invoice_store.rs index cd0e2ebd2..6fb406334 100644 --- a/src/payment/asynchronous/static_invoice_store.rs +++ b/src/payment/asynchronous/static_invoice_store.rs @@ -63,7 +63,7 @@ impl StaticInvoiceStore { fn check_rate_limit( limiter: &Mutex, recipient_id: &[u8], ) -> Result<(), lightning::io::Error> { - let mut limiter = limiter.lock().unwrap(); + let mut limiter = limiter.lock().expect("lock"); if !limiter.allow(recipient_id) { Err(lightning::io::Error::new(lightning::io::ErrorKind::Other, "Rate limit exceeded")) } else { diff --git a/src/payment/bolt11.rs b/src/payment/bolt11.rs index f2857e814..18c489e27 100644 --- a/src/payment/bolt11.rs +++ b/src/payment/bolt11.rs @@ -241,7 +241,7 @@ impl Bolt11Payment { pub fn send( &self, invoice: &Bolt11Invoice, route_parameters: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -275,7 +275,8 @@ impl Bolt11Payment { ) { Ok(()) => { let payee_pubkey = invoice.recover_payee_pub_key(); - let amt_msat = invoice.amount_milli_satoshis().unwrap(); + let amt_msat = + invoice.amount_milli_satoshis().expect("invoice amount should be set"); log_info!(self.logger, "Initiated sending {}msat to {}", amt_msat, payee_pubkey); let kind = PaymentKind::Bolt11 { @@ -342,7 +343,7 @@ impl Bolt11Payment { &self, invoice: &Bolt11Invoice, amount_msat: u64, route_parameters: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -776,7 +777,7 @@ impl Bolt11Payment { pub fn send_probes( &self, invoice: &Bolt11Invoice, route_parameters: Option, ) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -831,7 +832,7 @@ impl Bolt11Payment { &self, invoice: &Bolt11Invoice, amount_msat: u64, route_parameters: Option, ) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } diff --git a/src/payment/bolt12.rs b/src/payment/bolt12.rs index 980e20696..2e5a5fb45 100644 --- a/src/payment/bolt12.rs +++ b/src/payment/bolt12.rs @@ -89,7 +89,7 @@ impl Bolt12Payment { &self, offer: &Offer, amount_msat: u64, quantity: Option, payer_note: Option, route_parameters: Option, hrn: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -207,7 +207,7 @@ impl Bolt12Payment { if let Some(expiry_secs) = expiry_secs { let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64)) .duration_since(UNIX_EPOCH) - .unwrap(); + .expect("system time must be after Unix epoch"); offer_builder = offer_builder.absolute_expiry(absolute_expiry); } @@ -219,7 +219,9 @@ impl Bolt12Payment { log_error!(self.logger, "Failed to create offer: quantity can't be zero."); return Err(Error::InvalidQuantity); } else { - offer = offer.supported_quantity(Quantity::Bounded(NonZeroU64::new(qty).unwrap())) + offer = offer.supported_quantity(Quantity::Bounded( + NonZeroU64::new(qty).expect("quantity is non-zero"), + )) }; }; @@ -262,7 +264,7 @@ impl Bolt12Payment { &self, offer: &Offer, quantity: Option, payer_note: Option, route_parameters: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -405,7 +407,7 @@ impl Bolt12Payment { if let Some(expiry_secs) = expiry_secs { let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64)) .duration_since(UNIX_EPOCH) - .unwrap(); + .expect("system time must be after Unix epoch"); offer_builder = offer_builder.absolute_expiry(absolute_expiry); } @@ -425,7 +427,7 @@ impl Bolt12Payment { /// [`Refund`]: lightning::offers::refund::Refund /// [`Bolt12Invoice`]: lightning::offers::invoice::Bolt12Invoice pub fn request_refund_payment(&self, refund: &Refund) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -474,7 +476,7 @@ impl Bolt12Payment { let absolute_expiry = (SystemTime::now() + Duration::from_secs(expiry_secs as u64)) .duration_since(UNIX_EPOCH) - .unwrap(); + .expect("system time must be after Unix epoch"); let retry_strategy = Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT); let route_parameters = route_parameters.or(self.config.route_parameters).unwrap_or_default(); diff --git a/src/payment/onchain.rs b/src/payment/onchain.rs index cc16690e2..9d00968fc 100644 --- a/src/payment/onchain.rs +++ b/src/payment/onchain.rs @@ -80,7 +80,7 @@ impl OnchainPayment { pub fn send_to_address( &self, address: &bitcoin::Address, amount_sats: u64, fee_rate: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -110,7 +110,7 @@ impl OnchainPayment { pub fn send_all_to_address( &self, address: &bitcoin::Address, retain_reserves: bool, fee_rate: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } diff --git a/src/payment/spontaneous.rs b/src/payment/spontaneous.rs index 74fa84c0e..1c819582e 100644 --- a/src/payment/spontaneous.rs +++ b/src/payment/spontaneous.rs @@ -56,7 +56,7 @@ impl SpontaneousPayment { route_parameters: Option, custom_tlvs: Option>, preimage: Option, ) -> Result { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } @@ -206,7 +206,7 @@ impl SpontaneousPayment { /// /// [`Bolt11Payment::send_probes`]: crate::payment::Bolt11Payment pub fn send_probes(&self, amount_msat: u64, node_id: PublicKey) -> Result<(), Error> { - if !*self.is_running.read().unwrap() { + if !*self.is_running.read().expect("lock") { return Err(Error::NotRunning); } diff --git a/src/peer_store.rs b/src/peer_store.rs index ce8a9810e..307fb6929 100644 --- a/src/peer_store.rs +++ b/src/peer_store.rs @@ -41,7 +41,7 @@ where } pub(crate) fn add_peer(&self, peer_info: PeerInfo) -> Result<(), Error> { - let mut locked_peers = self.peers.write().unwrap(); + let mut locked_peers = self.peers.write().expect("lock"); if locked_peers.contains_key(&peer_info.node_id) { return Ok(()); @@ -52,18 +52,18 @@ where } pub(crate) fn remove_peer(&self, node_id: &PublicKey) -> Result<(), Error> { - let mut locked_peers = self.peers.write().unwrap(); + let mut locked_peers = self.peers.write().expect("lock"); locked_peers.remove(node_id); self.persist_peers(&*locked_peers) } pub(crate) fn list_peers(&self) -> Vec { - self.peers.read().unwrap().values().cloned().collect() + self.peers.read().expect("lock").values().cloned().collect() } pub(crate) fn get_peer(&self, node_id: &PublicKey) -> Option { - self.peers.read().unwrap().get(node_id).cloned() + self.peers.read().expect("lock").get(node_id).cloned() } fn persist_peers(&self, locked_peers: &HashMap) -> Result<(), Error> { diff --git a/src/runtime.rs b/src/runtime.rs index 39a34ddfe..48d60a7bf 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -66,7 +66,7 @@ impl Runtime { where F: Future + Send + 'static, { - let mut background_tasks = self.background_tasks.lock().unwrap(); + let mut background_tasks = self.background_tasks.lock().expect("lock"); let runtime_handle = self.handle(); // Since it seems to make a difference to `tokio` (see // https://docs.rs/tokio/latest/tokio/time/fn.timeout.html#panics) we make sure the futures @@ -78,7 +78,8 @@ impl Runtime { where F: Future + Send + 'static, { - let mut cancellable_background_tasks = self.cancellable_background_tasks.lock().unwrap(); + let mut cancellable_background_tasks = + self.cancellable_background_tasks.lock().expect("lock"); let runtime_handle = self.handle(); // Since it seems to make a difference to `tokio` (see // https://docs.rs/tokio/latest/tokio/time/fn.timeout.html#panics) we make sure the futures @@ -90,7 +91,7 @@ impl Runtime { where F: Future + Send + 'static, { - let mut background_processor_task = self.background_processor_task.lock().unwrap(); + let mut background_processor_task = self.background_processor_task.lock().expect("lock"); debug_assert!(background_processor_task.is_none(), "Expected no background processor_task"); let runtime_handle = self.handle(); @@ -121,14 +122,15 @@ impl Runtime { } pub fn abort_cancellable_background_tasks(&self) { - let mut tasks = core::mem::take(&mut *self.cancellable_background_tasks.lock().unwrap()); + let mut tasks = + core::mem::take(&mut *self.cancellable_background_tasks.lock().expect("lock")); debug_assert!(tasks.len() > 0, "Expected some cancellable background_tasks"); tasks.abort_all(); self.block_on(async { while let Some(_) = tasks.join_next().await {} }) } pub fn wait_on_background_tasks(&self) { - let mut tasks = core::mem::take(&mut *self.background_tasks.lock().unwrap()); + let mut tasks = core::mem::take(&mut *self.background_tasks.lock().expect("lock")); debug_assert!(tasks.len() > 0, "Expected some background_tasks"); self.block_on(async { loop { @@ -161,7 +163,7 @@ impl Runtime { pub fn wait_on_background_processor_task(&self) { if let Some(background_processor_task) = - self.background_processor_task.lock().unwrap().take() + self.background_processor_task.lock().expect("lock").take() { let abort_handle = background_processor_task.abort_handle(); // Since it seems to make a difference to `tokio` (see diff --git a/src/scoring.rs b/src/scoring.rs index 3ed7b9d1e..f2099cca9 100644 --- a/src/scoring.rs +++ b/src/scoring.rs @@ -82,10 +82,11 @@ async fn sync_external_scores( log_error!(logger, "Failed to persist external scores to cache: {}", e); } - let duration_since_epoch = - SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); - scorer.lock().unwrap().merge(liquidities, duration_since_epoch); - let mut locked_node_metrics = node_metrics.write().unwrap(); + let duration_since_epoch = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("system time must be after Unix epoch"); + scorer.lock().expect("lock").merge(liquidities, duration_since_epoch); + let mut locked_node_metrics = node_metrics.write().expect("lock"); locked_node_metrics.latest_pathfinding_scores_sync_timestamp = Some(duration_since_epoch.as_secs()); write_node_metrics(&*locked_node_metrics, &*kv_store, logger).unwrap_or_else(|e| { diff --git a/src/types.rs b/src/types.rs index a36639808..644a1911b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -565,6 +565,7 @@ pub struct ChannelDetails { } impl From for ChannelDetails { + #[allow(clippy::unwrap_used)] fn from(value: LdkChannelDetails) -> Self { ChannelDetails { channel_id: value.channel_id, diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 0e80a46db..cb982e303 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -115,21 +115,21 @@ impl Wallet { } pub(crate) fn get_full_scan_request(&self) -> FullScanRequest { - self.inner.lock().unwrap().start_full_scan().build() + self.inner.lock().expect("lock").start_full_scan().build() } pub(crate) fn get_incremental_sync_request(&self) -> SyncRequest<(KeychainKind, u32)> { - self.inner.lock().unwrap().start_sync_with_revealed_spks().build() + self.inner.lock().expect("lock").start_sync_with_revealed_spks().build() } pub(crate) fn get_cached_txs(&self) -> Vec> { - self.inner.lock().unwrap().tx_graph().full_txs().map(|tx_node| tx_node.tx).collect() + self.inner.lock().expect("lock").tx_graph().full_txs().map(|tx_node| tx_node.tx).collect() } pub(crate) fn get_unconfirmed_txids(&self) -> Vec { self.inner .lock() - .unwrap() + .expect("lock") .transactions() .filter(|t| t.chain_position.is_unconfirmed()) .map(|t| t.tx_node.txid) @@ -137,12 +137,12 @@ impl Wallet { } pub(crate) fn current_best_block(&self) -> BestBlock { - let checkpoint = self.inner.lock().unwrap().latest_checkpoint(); + let checkpoint = self.inner.lock().expect("lock").latest_checkpoint(); BestBlock { block_hash: checkpoint.hash(), height: checkpoint.height() } } pub(crate) fn apply_update(&self, update: impl Into) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); match locked_wallet.apply_update_events(update) { Ok(events) => { self.update_payment_store(&mut *locked_wallet, events).map_err(|e| { @@ -150,7 +150,7 @@ impl Wallet { Error::PersistenceFailed })?; - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed @@ -172,7 +172,7 @@ impl Wallet { return Ok(()); } - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); let chain_tip1 = locked_wallet.latest_checkpoint().block_id(); let wallet_txs1 = locked_wallet @@ -203,7 +203,7 @@ impl Wallet { Error::PersistenceFailed })?; - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed @@ -426,7 +426,7 @@ impl Wallet { ) -> Result { let fee_rate = self.fee_estimator.estimate_fee_rate(confirmation_target); - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); let mut tx_builder = locked_wallet.build_tx(); tx_builder.add_recipient(output_script, amount).fee_rate(fee_rate).nlocktime(locktime); @@ -454,7 +454,7 @@ impl Wallet { }, } - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed @@ -469,8 +469,8 @@ impl Wallet { } pub(crate) fn get_new_address(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut locked_persister = self.persister.lock().expect("lock"); let address_info = locked_wallet.reveal_next_address(KeychainKind::External); locked_wallet.persist(&mut locked_persister).map_err(|e| { @@ -481,8 +481,8 @@ impl Wallet { } pub(crate) fn get_new_internal_address(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut locked_persister = self.persister.lock().expect("lock"); let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); locked_wallet.persist(&mut locked_persister).map_err(|e| { @@ -493,8 +493,8 @@ impl Wallet { } pub(crate) fn cancel_tx(&self, tx: &Transaction) -> Result<(), Error> { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.cancel_tx(tx); locked_wallet.persist(&mut locked_persister).map_err(|e| { @@ -508,7 +508,7 @@ impl Wallet { pub(crate) fn get_balances( &self, total_anchor_channels_reserve_sats: u64, ) -> Result<(u64, u64), Error> { - let balance = self.inner.lock().unwrap().balance(); + let balance = self.inner.lock().expect("lock").balance(); // Make sure `list_confirmed_utxos` returns at least one `Utxo` we could use to spend/bump // Anchors if we have any confirmed amounts. @@ -644,7 +644,7 @@ impl Wallet { pub(crate) fn get_max_funding_amount( &self, cur_anchor_reserve_sats: u64, fee_rate: FeeRate, ) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); // Use a dummy P2WSH script (34 bytes) to match the size of a real funding output. let dummy_p2wsh_script = ScriptBuf::new().to_p2wsh(); @@ -668,7 +668,7 @@ impl Wallet { &self, shared_input: Input, shared_output_script: ScriptBuf, cur_anchor_reserve_sats: u64, fee_rate: FeeRate, ) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); debug_assert!(matches!( locked_wallet.public_descriptor(KeychainKind::External), @@ -712,7 +712,7 @@ impl Wallet { fee_rate.unwrap_or_else(|| self.fee_estimator.estimate_fee_rate(confirmation_target)); let tx = { - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); // Prepare the tx_builder. We properly check the reserve requirements (again) further down. let tx_builder = match send_amount { @@ -834,7 +834,7 @@ impl Wallet { }, } - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet: {}", e); Error::PersistenceFailed @@ -888,8 +888,8 @@ impl Wallet { pub(crate) fn select_confirmed_utxos( &self, must_spend: Vec, must_pay_to: &[TxOut], fee_rate: FeeRate, ) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut locked_persister = self.persister.lock().expect("lock"); debug_assert!(matches!( locked_wallet.public_descriptor(KeychainKind::External), @@ -964,7 +964,7 @@ impl Wallet { } fn list_confirmed_utxos_inner(&self) -> Result, ()> { - let locked_wallet = self.inner.lock().unwrap(); + let locked_wallet = self.inner.lock().expect("lock"); let mut utxos = Vec::new(); let confirmed_txs: Vec = locked_wallet .transactions() @@ -1058,8 +1058,8 @@ impl Wallet { #[allow(deprecated)] fn get_change_script_inner(&self) -> Result { - let mut locked_wallet = self.inner.lock().unwrap(); - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); + let mut locked_persister = self.persister.lock().expect("lock"); let address_info = locked_wallet.next_unused_address(KeychainKind::Internal); locked_wallet.persist(&mut locked_persister).map_err(|e| { @@ -1071,7 +1071,7 @@ impl Wallet { #[allow(deprecated)] pub(crate) fn sign_owned_inputs(&self, unsigned_tx: Transaction) -> Result { - let locked_wallet = self.inner.lock().unwrap(); + let locked_wallet = self.inner.lock().expect("lock"); let mut psbt = Psbt::from_unsigned_tx(unsigned_tx).map_err(|e| { log_error!(self.logger, "Failed to construct PSBT: {}", e); @@ -1108,7 +1108,7 @@ impl Wallet { #[allow(deprecated)] fn sign_psbt_inner(&self, mut psbt: Psbt) -> Result { - let locked_wallet = self.inner.lock().unwrap(); + let locked_wallet = self.inner.lock().expect("lock"); // While BDK populates both `witness_utxo` and `non_witness_utxo` fields, LDK does not. As // BDK by default doesn't trust the witness UTXO to account for the Segwit bug, we must @@ -1256,7 +1256,7 @@ impl Wallet { }, }; - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); debug_assert!( locked_wallet.tx_details(txid).is_some(), @@ -1319,7 +1319,7 @@ impl Wallet { log_error!( self.logger, "Provided fee rate {} is too low for RBF fee bump of txid {}, required minimum fee rate: {}", - fee_rate.unwrap(), + fee_rate.expect("fee rate is set"), txid, required_fee_rate ); @@ -1380,7 +1380,7 @@ impl Wallet { }, } - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); locked_wallet.persist(&mut locked_persister).map_err(|e| { log_error!(self.logger, "Failed to persist wallet after fee bump of {}: {}", txid, e); Error::PersistenceFailed @@ -1431,7 +1431,7 @@ impl Listen for Wallet { } fn block_connected(&self, block: &bitcoin::Block, height: u32) { - let mut locked_wallet = self.inner.lock().unwrap(); + let mut locked_wallet = self.inner.lock().expect("lock"); let pre_checkpoint = locked_wallet.latest_checkpoint(); if pre_checkpoint.height() != height - 1 @@ -1481,7 +1481,7 @@ impl Listen for Wallet { }, }; - let mut locked_persister = self.persister.lock().unwrap(); + let mut locked_persister = self.persister.lock().expect("lock"); match locked_wallet.persist(&mut locked_persister) { Ok(_) => (), Err(e) => { @@ -1513,7 +1513,7 @@ impl WalletSource for Wallet { &'a self, outpoint: OutPoint, ) -> impl Future> + Send + 'a { async move { - let locked_wallet = self.inner.lock().unwrap(); + let locked_wallet = self.inner.lock().expect("lock"); locked_wallet .tx_details(outpoint.txid) .map(|tx_details| tx_details.tx.deref().clone()) diff --git a/src/wallet/ser.rs b/src/wallet/ser.rs index c1ad984e6..c6a707bcd 100644 --- a/src/wallet/ser.rs +++ b/src/wallet/ser.rs @@ -94,7 +94,9 @@ impl Readable for ChangeSetDeserWrapper { decode_tlv_stream!(reader, { (0, blocks, required), }); - Ok(Self(BdkLocalChainChangeSet { blocks: blocks.0.unwrap() })) + Ok(Self(BdkLocalChainChangeSet { + blocks: blocks.0.expect("required blocks TLV field should be present"), + })) } } @@ -141,10 +143,10 @@ impl Readable for ChangeSetDeserWrapper> (0, time, required), (2, txid, required), }); - set.insert((time.0.unwrap().0, txid.0.unwrap())); + set.insert(( + time.0.expect("required confirmation time TLV field should be present").0, + txid.0.expect("required txid TLV field should be present"), + )); } Ok(Self(set)) } @@ -205,7 +210,7 @@ impl Readable for ChangeSetDeserWrapper>> { read_tlv_fields!(reader, { (0, tx, required), }); - set.insert(Arc::new(tx.0.unwrap())); + set.insert(Arc::new(tx.0.expect("required transaction TLV field should be present"))); } Ok(Self(set)) } @@ -232,8 +237,10 @@ impl Readable for ChangeSetDeserWrapper { }); Ok(Self(ConfirmationBlockTime { - block_id: block_id.0.unwrap().0, - confirmation_time: confirmation_time.0.unwrap(), + block_id: block_id.0.expect("required block_id TLV field should be present").0, + confirmation_time: confirmation_time + .0 + .expect("required confirmation_time TLV field should be present"), })) } } @@ -257,7 +264,10 @@ impl Readable for ChangeSetDeserWrapper { (2, hash, required), }); - Ok(Self(BlockId { height: height.0.unwrap(), hash: hash.0.unwrap() })) + Ok(Self(BlockId { + height: height.0.expect("required height TLV field should be present"), + hash: hash.0.expect("required hash TLV field should be present"), + })) } } @@ -285,7 +295,10 @@ impl Readable for ChangeSetDeserWrapper { decode_tlv_stream!(reader, { (0, last_revealed, required) }); Ok(Self(BdkIndexerChangeSet { - last_revealed: last_revealed.0.unwrap().0, + last_revealed: last_revealed + .0 + .expect("required last_revealed TLV field should be present") + .0, spk_cache: Default::default(), })) } @@ -317,7 +330,10 @@ impl Readable for ChangeSetDeserWrapper> { (0, descriptor_id, required), (2, last_index, required), }); - set.insert(descriptor_id.0.unwrap().0, last_index.0.unwrap()); + set.insert( + descriptor_id.0.expect("required descriptor_id TLV field should be present").0, + last_index.0.expect("required last_index TLV field should be present"), + ); } Ok(Self(set)) } @@ -336,7 +352,9 @@ impl Readable for ChangeSetDeserWrapper { decode_tlv_stream!(reader, { (0, hash, required) }); - Ok(Self(DescriptorId(hash.0.unwrap().0))) + Ok(Self(DescriptorId( + hash.0.expect("required descriptor hash TLV field should be present").0, + ))) } } @@ -351,6 +369,9 @@ impl Readable for ChangeSetDeserWrapper { use bitcoin::hashes::Hash; let buf: [u8; 32] = Readable::read(reader)?; - Ok(Self(Sha256Hash::from_slice(&buf[..]).unwrap())) + Ok(Self( + Sha256Hash::from_slice(&buf[..]) + .expect("a 32-byte buffer should decode into a sha256 hash"), + )) } } From f229b10abe42f8ee19dd5bc515553ac280b13692 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:02:03 +0200 Subject: [PATCH 2/9] Handle inbound connection setup errors Log and skip runtime listener failures instead of panicking when accepting inbound connections or converting accepted sockets. These errors can happen in normal operation, so keeping the node running is safer than treating them as unreachable. Co-Authored-By: HAL 9000 --- src/lib.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9df83b7d4..187600610 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -420,12 +420,23 @@ impl Node { break; } res = listener.accept() => { - #[allow(clippy::unwrap_used)] - let tcp_stream = res.unwrap().0; + let tcp_stream = match res { + Ok((tcp_stream, _)) => tcp_stream, + Err(e) => { + log_error!(logger, "Failed to accept inbound connection: {}", e); + continue; + }, + }; let peer_mgr = Arc::clone(&peer_mgr); + let logger = Arc::clone(&logger); runtime.spawn_cancellable_background_task(async move { - #[allow(clippy::unwrap_used)] - let tcp_stream = tcp_stream.into_std().unwrap(); + let tcp_stream = match tcp_stream.into_std() { + Ok(tcp_stream) => tcp_stream, + Err(e) => { + log_error!(logger, "Failed to convert inbound connection: {}", e); + return; + }, + }; lightning_net_tokio::setup_inbound( Arc::clone(&peer_mgr), tcp_stream, From caf0b25de22aa61996481015eb94bf38a5ab771a Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:04:20 +0200 Subject: [PATCH 3/9] Document outbound payment amount invariant Replace the success-path unwrap on payment amounts with an expect that explains why outbound payments must already have a recorded amount by the time LDK reports them as sent. Co-Authored-By: HAL 9000 --- src/event.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/event.rs b/src/event.rs index 17dd158b2..41da61bbf 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1091,11 +1091,14 @@ where }; self.payment_store.get(&payment_id).map(|payment| { + let amount_msat = payment.amount_msat.expect( + "outbound payments should record their amount before they can succeed", + ); log_info!( self.logger, "Successfully sent payment of {}msat{} from \ payment hash {:?} with preimage {:?}", - payment.amount_msat.expect("payment amount should be set"), + amount_msat, if let Some(fee) = fee_paid_msat { format!(" (fee {} msat)", fee) } else { From 769dee4f28c22788423b4ea696d8f3d41c83b22f Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:08:38 +0200 Subject: [PATCH 4/9] Document ChannelPending temporary id invariant Replace the pending-channel unwrap with an expect that records why supported LDK Node state should always include the former temporary channel id. Older rust-lightning state could omit it, but LDK Node never shipped before that field existed. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer --- src/event.rs | 7 +++++-- src/types.rs | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/event.rs b/src/event.rs index 41da61bbf..3161daa2a 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1452,11 +1452,14 @@ where counterparty_node_id, ); + let former_temporary_channel_id = former_temporary_channel_id.expect( + "LDK Node has only ever persisted ChannelPending events from rust-lightning 0.0.115 or later", + ); + let event = Event::ChannelPending { channel_id, user_channel_id: UserChannelId(user_channel_id), - former_temporary_channel_id: former_temporary_channel_id - .expect("former temporary channel id should be set"), + former_temporary_channel_id, counterparty_node_id, funding_txo, }; diff --git a/src/types.rs b/src/types.rs index 644a1911b..3424d2779 100644 --- a/src/types.rs +++ b/src/types.rs @@ -565,7 +565,6 @@ pub struct ChannelDetails { } impl From for ChannelDetails { - #[allow(clippy::unwrap_used)] fn from(value: LdkChannelDetails) -> Self { ChannelDetails { channel_id: value.channel_id, @@ -578,9 +577,9 @@ impl From for ChannelDetails { channel_value_sats: value.channel_value_satoshis, unspendable_punishment_reserve: value.unspendable_punishment_reserve, user_channel_id: UserChannelId(value.user_channel_id), - // unwrap safety: This value will be `None` for objects serialized with LDK versions - // prior to 0.0.115. - feerate_sat_per_1000_weight: value.feerate_sat_per_1000_weight.unwrap(), + feerate_sat_per_1000_weight: value + .feerate_sat_per_1000_weight + .expect("value is set for objects serialized with LDK v0.0.115+"), outbound_capacity_msat: value.outbound_capacity_msat, inbound_capacity_msat: value.inbound_capacity_msat, confirmations_required: value.confirmations_required, @@ -613,11 +612,14 @@ impl From for ChannelDetails { next_outbound_htlc_limit_msat: value.next_outbound_htlc_limit_msat, next_outbound_htlc_minimum_msat: value.next_outbound_htlc_minimum_msat, force_close_spend_delay: value.force_close_spend_delay, - // unwrap safety: This field is only `None` for objects serialized prior to LDK 0.0.107 - inbound_htlc_minimum_msat: value.inbound_htlc_minimum_msat.unwrap_or(0), + inbound_htlc_minimum_msat: value + .inbound_htlc_minimum_msat + .expect("value is set for objects serialized with LDK v0.0.107+"), inbound_htlc_maximum_msat: value.inbound_htlc_maximum_msat, - // unwrap safety: `config` is only `None` for LDK objects serialized prior to 0.0.109. - config: value.config.map(|c| c.into()).unwrap(), + config: value + .config + .map(|c| c.into()) + .expect("value is set for objects serialized with LDK v0.0.109+"), channel_shutdown_state: value.channel_shutdown_state, } } From 0102fddddc78637d6d3f43530d8370ffb6fbed5a Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:15:19 +0200 Subject: [PATCH 5/9] Propagate sqlite schema version read errors Replace the user_version query unwrap with normal io::Error propagation so database initialization failures are reported cleanly instead of panicking. Co-Authored-By: HAL 9000 --- src/io/sqlite_store/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/io/sqlite_store/mod.rs b/src/io/sqlite_store/mod.rs index a743a2f1f..84af03adc 100644 --- a/src/io/sqlite_store/mod.rs +++ b/src/io/sqlite_store/mod.rs @@ -288,8 +288,10 @@ impl SqliteStoreInner { })?; let sql = format!("SELECT user_version FROM pragma_user_version"); - let version_res: u16 = - connection.query_row(&sql, [], |row| row.get(0)).expect("pragma query must succeed"); + let version_res: u16 = connection.query_row(&sql, [], |row| row.get(0)).map_err(|e| { + let msg = format!("Failed to read PRAGMA user_version: {}", e); + io::Error::new(io::ErrorKind::Other, msg) + })?; if version_res == 0 { // New database, set our SCHEMA_USER_VERSION and continue From a7579f43f49ceeeb52d0fd62449be848a5cc3908 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:17:26 +0200 Subject: [PATCH 6/9] Propagate VSS runtime construction errors Replace the Tokio runtime builder unwrap with io::Error propagation so VSS startup failures surface through the constructor instead of panicking. Co-Authored-By: HAL 9000 --- src/io/vss_store.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index 324c611f7..97883b5d5 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -110,7 +110,9 @@ impl VssStore { .worker_threads(INTERNAL_RUNTIME_WORKERS) .max_blocking_threads(INTERNAL_RUNTIME_WORKERS) .build() - .expect("tokio runtime build must succeed"); + .map_err(|e| { + io::Error::new(io::ErrorKind::Other, format!("Failed to build VSS runtime: {}", e)) + })?; let (data_encryption_key, obfuscation_master_key) = derive_data_encryption_and_obfuscation_keys(&vss_seed); From 0958a3316b4894a7aa73c3f1b66dc9c3f4a4e38e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:19:48 +0200 Subject: [PATCH 7/9] Tolerate clock skew in bitcoind timing logs Use a zero-millisecond fallback for elapsed-time logging so clock adjustments do not panic the chain polling loop. Co-Authored-By: HAL 9000 --- src/chain/bitcoind.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/chain/bitcoind.rs b/src/chain/bitcoind.rs index 86266d543..cbe094462 100644 --- a/src/chain/bitcoind.rs +++ b/src/chain/bitcoind.rs @@ -194,10 +194,11 @@ impl BitcoindChainSource { { Ok(chain_tip) => { { + let elapsed_ms = now.elapsed().map(|d| d.as_millis()).unwrap_or(0); log_info!( self.logger, "Finished synchronizing listeners in {}ms", - now.elapsed().expect("system time must not go backwards").as_millis() + elapsed_ms, ); *self.latest_chain_tip.write().expect("lock") = Some(chain_tip); let unix_time_secs_opt = @@ -410,11 +411,8 @@ impl BitcoindChainSource { let now = SystemTime::now(); match spv_client.poll_best_tip().await { Ok((ChainTip::Better(tip), true)) => { - log_trace!( - self.logger, - "Finished polling best tip in {}ms", - now.elapsed().expect("system time must not go backwards").as_millis() - ); + let elapsed_ms = now.elapsed().map(|d| d.as_millis()).unwrap_or(0); + log_trace!(self.logger, "Finished polling best tip in {}ms", elapsed_ms); *self.latest_chain_tip.write().expect("lock") = Some(tip); }, Ok(_) => {}, @@ -434,12 +432,13 @@ impl BitcoindChainSource { .await { Ok((unconfirmed_txs, evicted_txids)) => { + let elapsed_ms = now.elapsed().map(|d| d.as_millis()).unwrap_or(0); log_trace!( self.logger, "Finished polling mempool of size {} and {} evicted transactions in {}ms", unconfirmed_txs.len(), evicted_txids.len(), - now.elapsed().expect("system time must not go backwards").as_millis() + elapsed_ms, ); onchain_wallet.apply_mempool_txs(unconfirmed_txs, evicted_txids).unwrap_or_else( |e| { From 1ce86acbf640bcd74ccfb7abe76d855987633e79 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 12:31:02 +0200 Subject: [PATCH 8/9] Propagate Esplora client setup failures Return Esplora client construction failures through build-time error handling instead of panicking so invalid headers or reqwest setup errors fail node construction cleanly. Co-Authored-By: HAL 9000 --- src/builder.rs | 5 +++++ src/chain/esplora.rs | 11 ++++++----- src/chain/mod.rs | 6 +++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index c1c56ff78..3d12ee103 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -189,6 +189,8 @@ pub enum BuildError { WalletSetupFailed, /// We failed to setup the logger. LoggerSetupFailed, + /// We failed to setup the configured chain source. + ChainSourceSetupFailed, /// The given network does not match the node's previously configured network. NetworkMismatch, /// The role of the node in an asynchronous payments context is not compatible with the current configuration. @@ -216,6 +218,7 @@ impl fmt::Display for BuildError { Self::KVStoreSetupFailed => write!(f, "Failed to setup KVStore."), Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."), Self::LoggerSetupFailed => write!(f, "Failed to setup the logger."), + Self::ChainSourceSetupFailed => write!(f, "Failed to setup the chain source."), Self::InvalidNodeAlias => write!(f, "Given node alias is invalid."), Self::NetworkMismatch => { write!(f, "Given network does not match the node's previously configured network.") @@ -1314,6 +1317,7 @@ fn build_with_store_internal( Arc::clone(&logger), Arc::clone(&node_metrics), ) + .map_err(|()| BuildError::ChainSourceSetupFailed)? }, Some(ChainDataSourceConfig::Electrum { server_url, sync_config }) => { let sync_config = sync_config.unwrap_or(ElectrumSyncConfig::default()); @@ -1383,6 +1387,7 @@ fn build_with_store_internal( Arc::clone(&logger), Arc::clone(&node_metrics), ) + .map_err(|()| BuildError::ChainSourceSetupFailed)? }, }; let chain_source = Arc::new(chain_source); diff --git a/src/chain/esplora.rs b/src/chain/esplora.rs index 0b91fb606..d0c683c74 100644 --- a/src/chain/esplora.rs +++ b/src/chain/esplora.rs @@ -45,7 +45,7 @@ impl EsploraChainSource { server_url: String, headers: HashMap, sync_config: EsploraSyncConfig, fee_estimator: Arc, kv_store: Arc, config: Arc, logger: Arc, node_metrics: Arc>, - ) -> Self { + ) -> Result { let mut client_builder = esplora_client::Builder::new(&server_url); client_builder = client_builder.timeout(sync_config.timeouts_config.per_request_timeout_secs as u64); @@ -54,14 +54,15 @@ impl EsploraChainSource { client_builder = client_builder.header(header_name, header_value); } - let esplora_client = - client_builder.build_async().expect("esplora client build must succeed"); + let esplora_client = client_builder.build_async().map_err(|e| { + log_error!(logger, "Failed to build Esplora client: {}", e); + })?; let tx_sync = Arc::new(EsploraSyncClient::from_client(esplora_client.clone(), Arc::clone(&logger))); let onchain_wallet_sync_status = Mutex::new(WalletSyncStatus::Completed); let lightning_wallet_sync_status = Mutex::new(WalletSyncStatus::Completed); - Self { + Ok(Self { sync_config, esplora_client, onchain_wallet_sync_status, @@ -72,7 +73,7 @@ impl EsploraChainSource { config, logger, node_metrics, - } + }) } pub(super) async fn sync_onchain_wallet( diff --git a/src/chain/mod.rs b/src/chain/mod.rs index e1cbf08ac..537ee04d3 100644 --- a/src/chain/mod.rs +++ b/src/chain/mod.rs @@ -101,7 +101,7 @@ impl ChainSource { fee_estimator: Arc, tx_broadcaster: Arc, kv_store: Arc, config: Arc, logger: Arc, node_metrics: Arc>, - ) -> (Self, Option) { + ) -> Result<(Self, Option), ()> { let esplora_chain_source = EsploraChainSource::new( server_url, headers, @@ -111,10 +111,10 @@ impl ChainSource { config, Arc::clone(&logger), node_metrics, - ); + )?; let kind = ChainSourceKind::Esplora(esplora_chain_source); let registered_txids = Mutex::new(Vec::new()); - (Self { kind, registered_txids, tx_broadcaster, logger }, None) + Ok((Self { kind, registered_txids, tx_broadcaster, logger }, None)) } pub(crate) fn new_electrum( From ad04cfce7a766ab0f2ec7f768267d6f5c8faea4e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 3 Apr 2026 11:55:56 +0200 Subject: [PATCH 9/9] Ban new library unwraps in CI Fail library clippy runs when new unwrap calls are introduced so the unwrap policy stays enforced without pulling tests, benches, or docs into the restriction. Co-Authored-By: HAL 9000 Signed-off-by: Elias Rohrer --- .github/workflows/rust.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dfa952b2b..00bf196d1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -90,6 +90,21 @@ jobs: run: | RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test --features uniffi + linting: + name: Linting + runs-on: ubuntu-latest + steps: + - name: Checkout source code + uses: actions/checkout@v6 + - name: Install Rust and clippy + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable + rustup component add clippy + - name: Ban `unwrap` in library code + run: | + cargo clippy --lib --verbose --color always -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments + cargo clippy --lib --features uniffi --verbose --color always -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments + doc: name: Documentation runs-on: ubuntu-latest