Skip to content

Commit ca5e79c

Browse files
authored
Merge pull request #868 from tnull/2026-04-disallow-unwrap
Linting: Disallow `unwrap`s, replace with `expect` or error propagation
2 parents fe692f3 + ad04cfc commit ca5e79c

31 files changed

+396
-278
lines changed

.github/workflows/rust.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ jobs:
9090
run: |
9191
RUSTFLAGS="--cfg no_download --cfg cycle_tests" cargo test --features uniffi
9292
93+
linting:
94+
name: Linting
95+
runs-on: ubuntu-latest
96+
steps:
97+
- name: Checkout source code
98+
uses: actions/checkout@v6
99+
- name: Install Rust and clippy
100+
run: |
101+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
102+
rustup component add clippy
103+
- name: Ban `unwrap` in library code
104+
run: |
105+
cargo clippy --lib --verbose --color always -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
106+
cargo clippy --lib --features uniffi --verbose --color always -- -A warnings -D clippy::unwrap_used -A clippy::tabs_in_doc_comments
107+
93108
doc:
94109
name: Documentation
95110
runs-on: ubuntu-latest

build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77

88
fn main() {
99
#[cfg(feature = "uniffi")]
10-
uniffi::generate_scaffolding("bindings/ldk_node.udl").unwrap();
10+
uniffi::generate_scaffolding("bindings/ldk_node.udl")
11+
.expect("the checked-in UniFFI UDL should always generate scaffolding");
1112
}

src/balance.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,9 @@ impl LightningBalance {
232232
inbound_htlc_rounded_msat,
233233
} => {
234234
// unwrap safety: confirmed_balance_candidate_index is guaranteed to index into balance_candidates
235-
let balance = balance_candidates.get(confirmed_balance_candidate_index).unwrap();
235+
let balance = balance_candidates
236+
.get(confirmed_balance_candidate_index)
237+
.expect("LDK should provide a valid confirmed balance candidate index");
236238

237239
Self::ClaimableOnChannelClose {
238240
channel_id,

src/builder.rs

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ pub enum BuildError {
189189
WalletSetupFailed,
190190
/// We failed to setup the logger.
191191
LoggerSetupFailed,
192+
/// We failed to setup the configured chain source.
193+
ChainSourceSetupFailed,
192194
/// The given network does not match the node's previously configured network.
193195
NetworkMismatch,
194196
/// 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 {
216218
Self::KVStoreSetupFailed => write!(f, "Failed to setup KVStore."),
217219
Self::WalletSetupFailed => write!(f, "Failed to setup onchain wallet."),
218220
Self::LoggerSetupFailed => write!(f, "Failed to setup the logger."),
221+
Self::ChainSourceSetupFailed => write!(f, "Failed to setup the chain source."),
219222
Self::InvalidNodeAlias => write!(f, "Given node alias is invalid."),
220223
Self::NetworkMismatch => {
221224
write!(f, "Given network does not match the node's previously configured network.")
@@ -861,7 +864,7 @@ impl ArcedNodeBuilder {
861864
pub fn set_chain_source_esplora(
862865
&self, server_url: String, sync_config: Option<EsploraSyncConfig>,
863866
) {
864-
self.inner.write().unwrap().set_chain_source_esplora(server_url, sync_config);
867+
self.inner.write().expect("lock").set_chain_source_esplora(server_url, sync_config);
865868
}
866869

867870
/// Configures the [`Node`] instance to source its chain data from the given Esplora server.
@@ -875,7 +878,7 @@ impl ArcedNodeBuilder {
875878
&self, server_url: String, headers: HashMap<String, String>,
876879
sync_config: Option<EsploraSyncConfig>,
877880
) {
878-
self.inner.write().unwrap().set_chain_source_esplora_with_headers(
881+
self.inner.write().expect("lock").set_chain_source_esplora_with_headers(
879882
server_url,
880883
headers,
881884
sync_config,
@@ -889,7 +892,7 @@ impl ArcedNodeBuilder {
889892
pub fn set_chain_source_electrum(
890893
&self, server_url: String, sync_config: Option<ElectrumSyncConfig>,
891894
) {
892-
self.inner.write().unwrap().set_chain_source_electrum(server_url, sync_config);
895+
self.inner.write().expect("lock").set_chain_source_electrum(server_url, sync_config);
893896
}
894897

895898
/// Configures the [`Node`] instance to connect to a Bitcoin Core node via RPC.
@@ -903,7 +906,7 @@ impl ArcedNodeBuilder {
903906
pub fn set_chain_source_bitcoind_rpc(
904907
&self, rpc_host: String, rpc_port: u16, rpc_user: String, rpc_password: String,
905908
) {
906-
self.inner.write().unwrap().set_chain_source_bitcoind_rpc(
909+
self.inner.write().expect("lock").set_chain_source_bitcoind_rpc(
907910
rpc_host,
908911
rpc_port,
909912
rpc_user,
@@ -924,7 +927,7 @@ impl ArcedNodeBuilder {
924927
&self, rest_host: String, rest_port: u16, rpc_host: String, rpc_port: u16,
925928
rpc_user: String, rpc_password: String,
926929
) {
927-
self.inner.write().unwrap().set_chain_source_bitcoind_rest(
930+
self.inner.write().expect("lock").set_chain_source_bitcoind_rest(
928931
rest_host,
929932
rest_port,
930933
rpc_host,
@@ -937,20 +940,20 @@ impl ArcedNodeBuilder {
937940
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
938941
/// network.
939942
pub fn set_gossip_source_p2p(&self) {
940-
self.inner.write().unwrap().set_gossip_source_p2p();
943+
self.inner.write().expect("lock").set_gossip_source_p2p();
941944
}
942945

943946
/// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync
944947
/// server.
945948
pub fn set_gossip_source_rgs(&self, rgs_server_url: String) {
946-
self.inner.write().unwrap().set_gossip_source_rgs(rgs_server_url);
949+
self.inner.write().expect("lock").set_gossip_source_rgs(rgs_server_url);
947950
}
948951

949952
/// Configures the [`Node`] instance to source its external scores from the given URL.
950953
///
951954
/// The external scores are merged into the local scoring system to improve routing.
952955
pub fn set_pathfinding_scores_source(&self, url: String) {
953-
self.inner.write().unwrap().set_pathfinding_scores_source(url);
956+
self.inner.write().expect("lock").set_pathfinding_scores_source(url);
954957
}
955958

956959
/// Configures the [`Node`] instance to source inbound liquidity from the given
@@ -964,7 +967,7 @@ impl ArcedNodeBuilder {
964967
pub fn set_liquidity_source_lsps1(
965968
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
966969
) {
967-
self.inner.write().unwrap().set_liquidity_source_lsps1(node_id, address, token);
970+
self.inner.write().expect("lock").set_liquidity_source_lsps1(node_id, address, token);
968971
}
969972

970973
/// Configures the [`Node`] instance to source just-in-time inbound liquidity from the given
@@ -978,7 +981,7 @@ impl ArcedNodeBuilder {
978981
pub fn set_liquidity_source_lsps2(
979982
&self, node_id: PublicKey, address: SocketAddress, token: Option<String>,
980983
) {
981-
self.inner.write().unwrap().set_liquidity_source_lsps2(node_id, address, token);
984+
self.inner.write().expect("lock").set_liquidity_source_lsps2(node_id, address, token);
982985
}
983986

984987
/// Configures the [`Node`] instance to provide an [LSPS2] service, issuing just-in-time
@@ -988,12 +991,12 @@ impl ArcedNodeBuilder {
988991
///
989992
/// [LSPS2]: https://github.com/BitcoinAndLightningLayerSpecs/lsp/blob/main/LSPS2/README.md
990993
pub fn set_liquidity_provider_lsps2(&self, service_config: LSPS2ServiceConfig) {
991-
self.inner.write().unwrap().set_liquidity_provider_lsps2(service_config);
994+
self.inner.write().expect("lock").set_liquidity_provider_lsps2(service_config);
992995
}
993996

994997
/// Sets the used storage directory path.
995998
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
996-
self.inner.write().unwrap().set_storage_dir_path(storage_dir_path);
999+
self.inner.write().expect("lock").set_storage_dir_path(storage_dir_path);
9971000
}
9981001

9991002
/// Configures the [`Node`] instance to write logs to the filesystem.
@@ -1012,29 +1015,29 @@ impl ArcedNodeBuilder {
10121015
pub fn set_filesystem_logger(
10131016
&self, log_file_path: Option<String>, log_level: Option<LogLevel>,
10141017
) {
1015-
self.inner.write().unwrap().set_filesystem_logger(log_file_path, log_level);
1018+
self.inner.write().expect("lock").set_filesystem_logger(log_file_path, log_level);
10161019
}
10171020

10181021
/// Configures the [`Node`] instance to write logs to the [`log`](https://crates.io/crates/log) facade.
10191022
pub fn set_log_facade_logger(&self) {
1020-
self.inner.write().unwrap().set_log_facade_logger();
1023+
self.inner.write().expect("lock").set_log_facade_logger();
10211024
}
10221025

10231026
/// Configures the [`Node`] instance to write logs to the provided custom [`LogWriter`].
10241027
pub fn set_custom_logger(&self, log_writer: Arc<dyn LogWriter>) {
1025-
self.inner.write().unwrap().set_custom_logger(log_writer);
1028+
self.inner.write().expect("lock").set_custom_logger(log_writer);
10261029
}
10271030

10281031
/// Sets the Bitcoin network used.
10291032
pub fn set_network(&self, network: Network) {
1030-
self.inner.write().unwrap().set_network(network);
1033+
self.inner.write().expect("lock").set_network(network);
10311034
}
10321035

10331036
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
10341037
pub fn set_listening_addresses(
10351038
&self, listening_addresses: Vec<SocketAddress>,
10361039
) -> Result<(), BuildError> {
1037-
self.inner.write().unwrap().set_listening_addresses(listening_addresses).map(|_| ())
1040+
self.inner.write().expect("lock").set_listening_addresses(listening_addresses).map(|_| ())
10381041
}
10391042

10401043
/// Sets the IP address and TCP port which [`Node`] will announce to the gossip network that it accepts connections on.
@@ -1045,7 +1048,11 @@ impl ArcedNodeBuilder {
10451048
pub fn set_announcement_addresses(
10461049
&self, announcement_addresses: Vec<SocketAddress>,
10471050
) -> Result<(), BuildError> {
1048-
self.inner.write().unwrap().set_announcement_addresses(announcement_addresses).map(|_| ())
1051+
self.inner
1052+
.write()
1053+
.expect("lock")
1054+
.set_announcement_addresses(announcement_addresses)
1055+
.map(|_| ())
10491056
}
10501057

10511058
/// Configures the [`Node`] instance to use a Tor SOCKS proxy for outbound connections to peers with OnionV3 addresses.
@@ -1054,22 +1061,22 @@ impl ArcedNodeBuilder {
10541061
///
10551062
/// **Note**: If unset, connecting to peer OnionV3 addresses will fail.
10561063
pub fn set_tor_config(&self, tor_config: TorConfig) -> Result<(), BuildError> {
1057-
self.inner.write().unwrap().set_tor_config(tor_config).map(|_| ())
1064+
self.inner.write().expect("lock").set_tor_config(tor_config).map(|_| ())
10581065
}
10591066

10601067
/// Sets the node alias that will be used when broadcasting announcements to the gossip
10611068
/// network.
10621069
///
10631070
/// The provided alias must be a valid UTF-8 string and no longer than 32 bytes in total.
10641071
pub fn set_node_alias(&self, node_alias: String) -> Result<(), BuildError> {
1065-
self.inner.write().unwrap().set_node_alias(node_alias).map(|_| ())
1072+
self.inner.write().expect("lock").set_node_alias(node_alias).map(|_| ())
10661073
}
10671074

10681075
/// Sets the role of the node in an asynchronous payments context.
10691076
pub fn set_async_payments_role(
10701077
&self, role: Option<AsyncPaymentsRole>,
10711078
) -> Result<(), BuildError> {
1072-
self.inner.write().unwrap().set_async_payments_role(role).map(|_| ())
1079+
self.inner.write().expect("lock").set_async_payments_role(role).map(|_| ())
10731080
}
10741081

10751082
/// Configures the [`Node`] to resync chain data from genesis on first startup, recovering any
@@ -1078,21 +1085,21 @@ impl ArcedNodeBuilder {
10781085
/// This should only be set on first startup when importing an older wallet from a previously
10791086
/// used [`NodeEntropy`].
10801087
pub fn set_wallet_recovery_mode(&self) {
1081-
self.inner.write().unwrap().set_wallet_recovery_mode();
1088+
self.inner.write().expect("lock").set_wallet_recovery_mode();
10821089
}
10831090

10841091
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
10851092
/// previously configured.
10861093
pub fn build(&self, node_entropy: Arc<NodeEntropy>) -> Result<Arc<Node>, BuildError> {
1087-
self.inner.read().unwrap().build(*node_entropy).map(Arc::new)
1094+
self.inner.read().expect("lock").build(*node_entropy).map(Arc::new)
10881095
}
10891096

10901097
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
10911098
/// previously configured.
10921099
pub fn build_with_fs_store(
10931100
&self, node_entropy: Arc<NodeEntropy>,
10941101
) -> Result<Arc<Node>, BuildError> {
1095-
self.inner.read().unwrap().build_with_fs_store(*node_entropy).map(Arc::new)
1102+
self.inner.read().expect("lock").build_with_fs_store(*node_entropy).map(Arc::new)
10961103
}
10971104

10981105
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
@@ -1118,7 +1125,7 @@ impl ArcedNodeBuilder {
11181125
) -> Result<Arc<Node>, BuildError> {
11191126
self.inner
11201127
.read()
1121-
.unwrap()
1128+
.expect("lock")
11221129
.build_with_vss_store(*node_entropy, vss_url, store_id, fixed_headers)
11231130
.map(Arc::new)
11241131
}
@@ -1151,7 +1158,7 @@ impl ArcedNodeBuilder {
11511158
) -> Result<Arc<Node>, BuildError> {
11521159
self.inner
11531160
.read()
1154-
.unwrap()
1161+
.expect("lock")
11551162
.build_with_vss_store_and_lnurl_auth(
11561163
*node_entropy,
11571164
vss_url,
@@ -1180,7 +1187,7 @@ impl ArcedNodeBuilder {
11801187
) -> Result<Arc<Node>, BuildError> {
11811188
self.inner
11821189
.read()
1183-
.unwrap()
1190+
.expect("lock")
11841191
.build_with_vss_store_and_fixed_headers(*node_entropy, vss_url, store_id, fixed_headers)
11851192
.map(Arc::new)
11861193
}
@@ -1203,7 +1210,7 @@ impl ArcedNodeBuilder {
12031210
let adapter = Arc::new(crate::ffi::VssHeaderProviderAdapter::new(header_provider));
12041211
self.inner
12051212
.read()
1206-
.unwrap()
1213+
.expect("lock")
12071214
.build_with_vss_store_and_header_provider(*node_entropy, vss_url, store_id, adapter)
12081215
.map(Arc::new)
12091216
}
@@ -1214,7 +1221,7 @@ impl ArcedNodeBuilder {
12141221
pub fn build_with_store<S: SyncAndAsyncKVStore + Send + Sync + 'static>(
12151222
&self, node_entropy: Arc<NodeEntropy>, kv_store: S,
12161223
) -> Result<Arc<Node>, BuildError> {
1217-
self.inner.read().unwrap().build_with_store(*node_entropy, kv_store).map(Arc::new)
1224+
self.inner.read().expect("lock").build_with_store(*node_entropy, kv_store).map(Arc::new)
12181225
}
12191226
}
12201227

@@ -1310,6 +1317,7 @@ fn build_with_store_internal(
13101317
Arc::clone(&logger),
13111318
Arc::clone(&node_metrics),
13121319
)
1320+
.map_err(|()| BuildError::ChainSourceSetupFailed)?
13131321
},
13141322
Some(ChainDataSourceConfig::Electrum { server_url, sync_config }) => {
13151323
let sync_config = sync_config.unwrap_or(ElectrumSyncConfig::default());
@@ -1379,6 +1387,7 @@ fn build_with_store_internal(
13791387
Arc::clone(&logger),
13801388
Arc::clone(&node_metrics),
13811389
)
1390+
.map_err(|()| BuildError::ChainSourceSetupFailed)?
13821391
},
13831392
};
13841393
let chain_source = Arc::new(chain_source);
@@ -1610,7 +1619,7 @@ fn build_with_store_internal(
16101619
// Restore external pathfinding scores from cache if possible.
16111620
match external_scores_res {
16121621
Ok(external_scores) => {
1613-
scorer.lock().unwrap().merge(external_scores, cur_time);
1622+
scorer.lock().expect("lock").merge(external_scores, cur_time);
16141623
log_trace!(logger, "External scores from cache merged successfully");
16151624
},
16161625
Err(e) => {
@@ -1763,7 +1772,7 @@ fn build_with_store_internal(
17631772

17641773
// Reset the RGS sync timestamp in case we somehow switch gossip sources
17651774
{
1766-
let mut locked_node_metrics = node_metrics.write().unwrap();
1775+
let mut locked_node_metrics = node_metrics.write().expect("lock");
17671776
locked_node_metrics.latest_rgs_snapshot_timestamp = None;
17681777
write_node_metrics(&*locked_node_metrics, &*kv_store, Arc::clone(&logger))
17691778
.map_err(|e| {
@@ -1775,7 +1784,7 @@ fn build_with_store_internal(
17751784
},
17761785
GossipSourceConfig::RapidGossipSync(rgs_server) => {
17771786
let latest_sync_timestamp =
1778-
node_metrics.read().unwrap().latest_rgs_snapshot_timestamp.unwrap_or(0);
1787+
node_metrics.read().expect("lock").latest_rgs_snapshot_timestamp.unwrap_or(0);
17791788
Arc::new(GossipSource::new_rgs(
17801789
rgs_server.clone(),
17811790
latest_sync_timestamp,

0 commit comments

Comments
 (0)