From 87c554dda05f282efe954ec15fdfb01451295109 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Wed, 10 Sep 2025 11:36:25 +0200 Subject: [PATCH 1/5] Bump MSRV to rustc 1.75.0 We generally align our MSRV with Debian's stable channel. Debian 13 'Trixie' was just released, shipping rustc 1.85. However, as 1.85.0 is only about ~7months old at this point, we opt to bump to the more conservative 1.75.0, which approaches two years of age. --- .github/workflows/build.yml | 16 ++++------ CONTRIBUTING.md | 4 +-- ci/check-lint.sh | 3 -- ci/ci-tests.sh | 29 +++---------------- lightning-invoice/src/de.rs | 2 +- lightning-invoice/src/ser.rs | 2 +- lightning-liquidity/src/lsps2/service.rs | 2 +- lightning-liquidity/src/lsps5/service.rs | 2 +- lightning-liquidity/src/manager.rs | 8 ++--- lightning-persister/src/utils.rs | 10 +++---- lightning-types/src/features.rs | 2 +- lightning/src/blinded_path/utils.rs | 3 +- lightning/src/chain/chaininterface.rs | 2 +- lightning/src/ln/channel.rs | 19 +++++------- lightning/src/ln/functional_test_utils.rs | 2 +- lightning/src/ln/msgs.rs | 6 ++-- .../src/offers/async_receive_offer_cache.rs | 4 +-- lightning/src/routing/router.rs | 17 +++++------ lightning/src/sign/mod.rs | 2 +- lightning/src/util/base32.rs | 6 ++-- lightning/src/util/sweep.rs | 2 +- rustfmt.toml | 2 +- 22 files changed, 55 insertions(+), 90 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbc50ba581f..b643058a746 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,10 +31,10 @@ jobs: fail-fast: false matrix: platform: [ self-hosted, windows-latest, macos-latest ] - toolchain: [ stable, beta, 1.63.0 ] # 1.63.0 is the MSRV for all crates but `lightning-transaction-sync`. + toolchain: [ stable, beta, 1.75.0 ] # 1.75.0 is the MSRV for all crates exclude: - platform: windows-latest - toolchain: 1.63.0 + toolchain: 1.75.0 - platform: windows-latest toolchain: beta - platform: macos-latest @@ -60,7 +60,7 @@ jobs: shellcheck ci/*.sh -aP ci shellcheck contrib/*.sh -aP contrib - name: Set RUSTFLAGS to deny warnings - if: "matrix.toolchain == '1.63.0'" + if: "matrix.toolchain == '1.75.0'" run: echo "RUSTFLAGS=-D warnings" >> "$GITHUB_ENV" - name: Run CI script shell: bash # Default on Winblows is powershell @@ -71,7 +71,7 @@ jobs: fail-fast: false matrix: platform: [ ubuntu-latest, macos-latest ] - toolchain: [ stable, beta, 1.75.0 ] # 1.75.0 is the MSRV for `lightning-transaction-sync`. + toolchain: [ stable, beta, 1.75.0 ] runs-on: ${{ matrix.platform }} steps: - name: Checkout source code @@ -254,17 +254,13 @@ jobs: fuzz: runs-on: self-hosted env: - TOOLCHAIN: 1.63 + TOOLCHAIN: 1.75 steps: - name: Checkout source code uses: actions/checkout@v4 - name: Install Rust ${{ env.TOOLCHAIN }} toolchain run: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain ${{ env.TOOLCHAIN }} - - name: Pin the regex dependency - run: | - cd fuzz && cargo update -p regex --precise "1.9.6" --verbose - cd write-seeds && cargo update -p regex --precise "1.9.6" --verbose - name: Sanity check fuzz targets on Rust ${{ env.TOOLCHAIN }} run: | cd fuzz @@ -293,7 +289,7 @@ jobs: rustfmt: runs-on: ubuntu-latest env: - TOOLCHAIN: 1.63.0 + TOOLCHAIN: 1.75.0 steps: - name: Checkout source code uses: actions/checkout@v4 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0fcae82e50..1bc431a3110 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,7 +88,7 @@ be covered by functional tests. When refactoring, structure your PR to make it easy to review and don't hesitate to split it into multiple small, focused PRs. -The Minimum Supported Rust Version (MSRV) currently is 1.63.0 (enforced by +The Minimum Supported Rust Version (MSRV) currently is 1.75.0 (enforced by our GitHub Actions). We support reading serialized LDK objects written by any version of LDK 0.0.99 and above. We support LDK versions 0.0.113 and above reading serialized LDK objects written by modern LDK. Any expected issues with @@ -124,7 +124,7 @@ display fine at any tab-length display setting. We use `rustfmt` to establish uniform coding standards throughout the codebase. Please run ```bash -cargo +1.63.0 fmt +cargo +1.75.0 fmt ``` before committing and pushing any changes, as compliance will also be checked diff --git a/ci/check-lint.sh b/ci/check-lint.sh index bd4df3b85b8..39c10692310 100755 --- a/ci/check-lint.sh +++ b/ci/check-lint.sh @@ -47,7 +47,6 @@ CLIPPY() { -A clippy::len_without_is_empty \ -A clippy::len_zero \ -A clippy::let_and_return \ - -A clippy::manual_div_ceil `# to be removed once we hit MSRV 1.73.0` \ -A clippy::manual_filter \ -A clippy::manual_map \ -A clippy::manual_memcpy \ @@ -106,9 +105,7 @@ CLIPPY() { -A clippy::unnecessary_unwrap \ -A clippy::unused_unit \ -A clippy::useless_conversion \ - -A clippy::unnecessary_map_or `# to be removed once we hit MSRV 1.70` \ -A clippy::manual_repeat_n `# to be removed once we hit MSRV 1.86` \ - -A clippy::io_other_error `# to be removed once we hit MSRV 1.74` \ -A clippy::manual_is_multiple_of `# to be removed once we hit MSRV 1.87` \ -A clippy::uninlined-format-args } diff --git a/ci/ci-tests.sh b/ci/ci-tests.sh index 41faed6488b..c423921032d 100755 --- a/ci/ci-tests.sh +++ b/ci/ci-tests.sh @@ -2,40 +2,20 @@ #shellcheck disable=SC2002,SC2207 set -eox pipefail -RUSTC_MINOR_VERSION=$(rustc --version | awk '{ split($2,a,"."); print a[2] }') +# Currently unused as we don't have to pin anything for MSRV: +#RUSTC_MINOR_VERSION=$(rustc --version | awk '{ split($2,a,"."); print a[2] }') # Some crates require pinning to meet our MSRV even for our downstream users, # which we do here. # Further crates which appear only as dev-dependencies are pinned further down. function PIN_RELEASE_DEPS { - # Starting with version 1.39.0, the `tokio` crate has an MSRV of rustc 1.70.0 - [ "$RUSTC_MINOR_VERSION" -lt 70 ] && cargo update -p tokio --precise "1.38.1" --verbose - return 0 # Don't fail the script if our rustc is higher than the last check } PIN_RELEASE_DEPS # pin the release dependencies in our main workspace -# Starting with version 1.10.0, the `regex` crate has an MSRV of rustc 1.65.0. -[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose - -# The addr2line v0.21 crate (a dependency of `backtrace` starting with 0.3.69) relies on rustc 1.65 -[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p backtrace --precise "0.3.68" --verbose - -# The once_cell v1.21.0 crate (a dependency of `proptest`) relies on rustc 1.70 -[ "$RUSTC_MINOR_VERSION" -lt 70 ] && cargo update -p once_cell --precise "1.20.3" --verbose - -# proptest 1.3.0 requires rustc 1.64.0 -[ "$RUSTC_MINOR_VERSION" -lt 64 ] && cargo update -p proptest --precise "1.2.0" --verbose - -# parking_lot 0.12.4 requires rustc 1.64.0 -[ "$RUSTC_MINOR_VERSION" -lt 64 ] && cargo update -p parking_lot --precise "0.12.3" --verbose - -# parking_lot_core 0.9.11 requires rustc 1.64.0 -[ "$RUSTC_MINOR_VERSION" -lt 64 ] && cargo update -p parking_lot_core --precise "0.9.10" --verbose - -# lock_api 0.4.13 requires rustc 1.64.0 -[ "$RUSTC_MINOR_VERSION" -lt 64 ] && cargo update -p lock_api --precise "0.4.12" --verbose +# The backtrace v0.3.75 crate relies on rustc 1.82 +[ "$RUSTC_MINOR_VERSION" -lt 82 ] && cargo update -p backtrace --precise "0.3.74" --verbose export RUST_BACKTRACE=1 @@ -49,7 +29,6 @@ cargo test --verbose --color always echo -e "\n\nTesting upgrade from prior versions of LDK" pushd lightning-tests -[ "$RUSTC_MINOR_VERSION" -lt 65 ] && cargo update -p regex --precise "1.9.6" --verbose cargo test popd diff --git a/lightning-invoice/src/de.rs b/lightning-invoice/src/de.rs index a4e3cf72c9b..0747015a457 100644 --- a/lightning-invoice/src/de.rs +++ b/lightning-invoice/src/de.rs @@ -102,7 +102,7 @@ impl FromBase32 for Bolt11InvoiceFeatures { // Carry bits, 0, 1, 2, 3, or 4 bits let mut carry_bits = 0; let mut carry = 0u8; - let expected_raw_length = (field_data.len() * 5 + 7) / 8; + let expected_raw_length = (field_data.len() * 5).div_ceil(8); let mut output = Vec::::with_capacity(expected_raw_length); // Iterate over input in reverse diff --git a/lightning-invoice/src/ser.rs b/lightning-invoice/src/ser.rs index 6da172c7220..5c93fa84ae0 100644 --- a/lightning-invoice/src/ser.rs +++ b/lightning-invoice/src/ser.rs @@ -237,7 +237,7 @@ fn encode_int_be_base32(int: u64) -> impl ExactSizeIterator { /// The length of the output of `encode_int_be_base32`. fn encoded_int_be_base32_size(int: u64) -> usize { let bit_len = 64 - int.leading_zeros() as usize; // cast ok as value is in 0..=64. - (bit_len + 4) / 5 + bit_len.div_ceil(5) } impl Base32Iterable for RawDataPart { diff --git a/lightning-liquidity/src/lsps2/service.rs b/lightning-liquidity/src/lsps2/service.rs index 7d7d75b0c1b..c715d405025 100644 --- a/lightning-liquidity/src/lsps2/service.rs +++ b/lightning-liquidity/src/lsps2/service.rs @@ -781,7 +781,7 @@ where /// Returns whether the peer has any active LSPS2 requests. pub(crate) fn has_active_requests(&self, counterparty_node_id: &PublicKey) -> bool { let outer_state_lock = self.per_peer_state.read().unwrap(); - outer_state_lock.get(counterparty_node_id).map_or(false, |inner| { + outer_state_lock.get(counterparty_node_id).is_some_and(|inner| { let peer_state = inner.lock().unwrap(); !peer_state.outbound_channels_by_intercept_scid.is_empty() }) diff --git a/lightning-liquidity/src/lsps5/service.rs b/lightning-liquidity/src/lsps5/service.rs index 1111c682fbc..f7f5e06a2c5 100644 --- a/lightning-liquidity/src/lsps5/service.rs +++ b/lightning-liquidity/src/lsps5/service.rs @@ -596,7 +596,7 @@ where // (other than lsps5.webhook_registered) close in time. if notification.method != WebhookNotificationMethod::LSPS5WebhookRegistered { let rate_limit_applies = peer_state.webhooks().iter().any(|(_, webhook)| { - webhook.last_notification_sent.as_ref().map_or(false, |last_sent| { + webhook.last_notification_sent.as_ref().is_some_and(|last_sent| { now.duration_since(&last_sent) < NOTIFICATION_COOLDOWN_TIME }) }); diff --git a/lightning-liquidity/src/manager.rs b/lightning-liquidity/src/manager.rs index 5d95d32d540..f0143fc624f 100644 --- a/lightning-liquidity/src/manager.rs +++ b/lightning-liquidity/src/manager.rs @@ -770,12 +770,12 @@ where let lsps2_has_active_requests = self .lsps2_service_handler .as_ref() - .map_or(false, |h| h.has_active_requests(sender_node_id)); + .is_some_and(|h| h.has_active_requests(sender_node_id)); #[cfg(lsps1_service)] let lsps1_has_active_requests = self .lsps1_service_handler .as_ref() - .map_or(false, |h| h.has_active_requests(sender_node_id)); + .is_some_and(|h| h.has_active_requests(sender_node_id)); #[cfg(not(lsps1_service))] let lsps1_has_active_requests = false; @@ -918,7 +918,7 @@ where fn provided_node_features(&self) -> NodeFeatures { let mut features = NodeFeatures::empty(); - let advertise_service = self.service_config.as_ref().map_or(false, |c| c.advertise_service); + let advertise_service = self.service_config.as_ref().is_some_and(|c| c.advertise_service); if advertise_service { features @@ -932,7 +932,7 @@ where fn provided_init_features(&self, _their_node_id: PublicKey) -> InitFeatures { let mut features = InitFeatures::empty(); - let advertise_service = self.service_config.as_ref().map_or(false, |c| c.advertise_service); + let advertise_service = self.service_config.as_ref().is_some_and(|c| c.advertise_service); if advertise_service { features .set_optional_custom_bit(LSPS_FEATURE_BIT) diff --git a/lightning-persister/src/utils.rs b/lightning-persister/src/utils.rs index 74fa0d0c231..e8e7be5ce5d 100644 --- a/lightning-persister/src/utils.rs +++ b/lightning-persister/src/utils.rs @@ -26,7 +26,7 @@ pub(crate) fn check_namespace_key_validity( PrintableString(secondary_namespace), PrintableString(key) ); - return Err(std::io::Error::new(std::io::ErrorKind::Other, msg)); + return Err(std::io::Error::other(msg)); } if primary_namespace.is_empty() && !secondary_namespace.is_empty() { @@ -37,7 +37,7 @@ pub(crate) fn check_namespace_key_validity( let msg = format!( "Failed to {} {}/{}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.", operation, PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key)); - return Err(std::io::Error::new(std::io::ErrorKind::Other, msg)); + return Err(std::io::Error::other(msg)); } if !is_valid_kvstore_str(primary_namespace) @@ -50,7 +50,7 @@ pub(crate) fn check_namespace_key_validity( let msg = format!("Failed to {} {}/{}/{}: primary namespace, secondary namespace, and key must be valid.", operation, PrintableString(primary_namespace), PrintableString(secondary_namespace), PrintableString(key)); - return Err(std::io::Error::new(std::io::ErrorKind::Other, msg)); + return Err(std::io::Error::other(msg)); } } else { if primary_namespace.is_empty() && !secondary_namespace.is_empty() { @@ -60,7 +60,7 @@ pub(crate) fn check_namespace_key_validity( let msg = format!( "Failed to {} {}/{}: primary namespace may not be empty if a non-empty secondary namespace is given.", operation, PrintableString(primary_namespace), PrintableString(secondary_namespace)); - return Err(std::io::Error::new(std::io::ErrorKind::Other, msg)); + return Err(std::io::Error::other(msg)); } if !is_valid_kvstore_str(primary_namespace) || !is_valid_kvstore_str(secondary_namespace) { debug_assert!( @@ -76,7 +76,7 @@ pub(crate) fn check_namespace_key_validity( PrintableString(primary_namespace), PrintableString(secondary_namespace) ); - return Err(std::io::Error::new(std::io::ErrorKind::Other, msg)); + return Err(std::io::Error::other(msg)); } } diff --git a/lightning-types/src/features.rs b/lightning-types/src/features.rs index 54f1d9e44f0..05a504ab8ca 100644 --- a/lightning-types/src/features.rs +++ b/lightning-types/src/features.rs @@ -698,7 +698,7 @@ mod sealed { ); // By default, allocate enough bytes to cover up to Splice. Update this as new features are // added which we expect to appear commonly across contexts. - pub(super) const MIN_FEATURES_ALLOCATION_BYTES: usize = (63 + 7) / 8; + pub(super) const MIN_FEATURES_ALLOCATION_BYTES: usize = 63_usize.div_ceil(8); define_feature!( 141, // The BOLTs PR uses feature bit 40/41, so add +100 for the experimental bit AnchorZeroFeeCommitmentsStaging, diff --git a/lightning/src/blinded_path/utils.rs b/lightning/src/blinded_path/utils.rs index 5fc359a47d9..8894f37ad33 100644 --- a/lightning/src/blinded_path/utils.rs +++ b/lightning/src/blinded_path/utils.rs @@ -256,8 +256,7 @@ impl Writeable for BlindedPathWithPadding { let tlv_length = self.tlvs.serialized_length(); let total_length = tlv_length + TLV_OVERHEAD; - let padding_length = - (total_length + self.round_off - 1) / self.round_off * self.round_off - total_length; + let padding_length = total_length.div_ceil(self.round_off) * self.round_off - total_length; let padding = Some(BlindedPathPadding::new(padding_length)); diff --git a/lightning/src/chain/chaininterface.rs b/lightning/src/chain/chaininterface.rs index 1f945a5c5d1..6c92c864dcb 100644 --- a/lightning/src/chain/chaininterface.rs +++ b/lightning/src/chain/chaininterface.rs @@ -24,7 +24,7 @@ pub(crate) fn compute_feerate_sat_per_1000_weight(fee_sat: u64, weight: u64) -> (fee_sat * 1000 / weight).try_into().unwrap_or(u32::max_value()) } pub(crate) const fn fee_for_weight(feerate_sat_per_1000_weight: u32, weight: u64) -> u64 { - ((feerate_sat_per_1000_weight as u64 * weight) + 1000 - 1) / 1000 + (feerate_sat_per_1000_weight as u64 * weight).div_ceil(1000) } /// An interface to send a transaction to the Bitcoin network. diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 8f74dc24e71..5c12a755996 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -3303,15 +3303,12 @@ where // This will return false if `counterparty_parameters` is `None`, but for a `FundedChannel`, it // should never be `None`. debug_assert!(channel_parameters.counterparty_parameters.is_some()); - channel_parameters.counterparty_parameters.as_ref().map_or( - false, - |counterparty_parameters| { - self.context().channel_id().is_v2_channel_id( - &channel_parameters.holder_pubkeys.revocation_basepoint, - &counterparty_parameters.pubkeys.revocation_basepoint, - ) - }, - ) + channel_parameters.counterparty_parameters.as_ref().is_some_and(|counterparty_parameters| { + self.context().channel_id().is_v2_channel_id( + &channel_parameters.holder_pubkeys.revocation_basepoint, + &counterparty_parameters.pubkeys.revocation_basepoint, + ) + }) } } @@ -10178,7 +10175,7 @@ where proposed_max_feerate as u64 * tx_weight / 1000, ) } else { - self.funding.get_value_satoshis() - (self.funding.value_to_self_msat + 999) / 1000 + self.funding.get_value_satoshis() - self.funding.value_to_self_msat.div_ceil(1000) }; self.context.closing_fee_limits = @@ -10686,7 +10683,7 @@ where debug_assert_eq!( our_max_fee, self.funding.get_value_satoshis() - - (self.funding.value_to_self_msat + 999) / 1000 + - self.funding.value_to_self_msat.div_ceil(1000) ); propose_fee!(cmp::min(max_fee_satoshis, our_max_fee)); } else { diff --git a/lightning/src/ln/functional_test_utils.rs b/lightning/src/ln/functional_test_utils.rs index d8e59dde166..2f3c9a14f68 100644 --- a/lightning/src/ln/functional_test_utils.rs +++ b/lightning/src/ln/functional_test_utils.rs @@ -2004,7 +2004,7 @@ pub fn do_check_spends Option>( // 0FC commitment transactions will have their fee bumped by a child, so don't require that // they meet the 1sat/vB minimum. } else if !has_p2a_output { - let min_fee = (tx.weight().to_wu() as u64 + 3) / 4; // One sat per vbyte (ie per weight/4, rounded up) + let min_fee = (tx.weight().to_wu() as u64).div_ceil(4); // One sat per vbyte (ie per weight/4, rounded up) assert!(total_value_out + min_fee <= total_value_in); } tx.verify(get_output).unwrap(); diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 354273f7170..c0c8239f621 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1255,12 +1255,10 @@ impl std::net::ToSocketAddrs for SocketAddress { SocketAddress::Hostname { ref hostname, port } => { (hostname.as_str(), *port).to_socket_addrs() }, - SocketAddress::OnionV2(..) => Err(std::io::Error::new( - std::io::ErrorKind::Other, + SocketAddress::OnionV2(..) => Err(std::io::Error::other( "Resolution of OnionV2 addresses is currently unsupported.", )), - SocketAddress::OnionV3 { .. } => Err(std::io::Error::new( - std::io::ErrorKind::Other, + SocketAddress::OnionV3 { .. } => Err(std::io::Error::other( "Resolution of OnionV3 addresses is currently unsupported.", )), } diff --git a/lightning/src/offers/async_receive_offer_cache.rs b/lightning/src/offers/async_receive_offer_cache.rs index 8c1887ad139..c4442b4dd8f 100644 --- a/lightning/src/offers/async_receive_offer_cache.rs +++ b/lightning/src/offers/async_receive_offer_cache.rs @@ -268,7 +268,7 @@ impl AsyncReceiveOfferCache { for offer_opt in self.offers.iter_mut() { let offer_is_expired = offer_opt .as_ref() - .map_or(false, |offer| offer.offer.is_expired_no_std(duration_since_epoch)); + .is_some_and(|offer| offer.offer.is_expired_no_std(duration_since_epoch)); if offer_is_expired { offer_opt.take(); offer_was_removed = true; @@ -486,7 +486,7 @@ impl AsyncReceiveOfferCache { }; let mut offers = self.offers.iter_mut(); - let offer_entry = offers.find(|o| o.as_ref().map_or(false, |o| o.offer.id() == offer_id)); + let offer_entry = offers.find(|o| o.as_ref().is_some_and(|o| o.offer.id() == offer_id)); if let Some(Some(ref mut offer)) = offer_entry { match offer.status { OfferStatus::Used { invoice_created_at: ref mut inv_created_at } diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index e3443b5e45a..9b68fdf5c59 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -573,7 +573,7 @@ impl Path { /// True if this [`Path`] has at least one Trampoline hop. pub fn has_trampoline_hops(&self) -> bool { - self.blinded_tail.as_ref().map_or(false, |bt| !bt.trampoline_hops.is_empty()) + self.blinded_tail.as_ref().is_some_and(|bt| !bt.trampoline_hops.is_empty()) } } @@ -1278,8 +1278,8 @@ impl Payee { #[rustfmt::skip] fn supports_basic_mpp(&self) -> bool { match self { - Self::Clear { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()), - Self::Blinded { features, .. } => features.as_ref().map_or(false, |f| f.supports_basic_mpp()), + Self::Clear { features, .. } => features.as_ref().is_some_and(|f| f.supports_basic_mpp()), + Self::Blinded { features, .. } => features.as_ref().is_some_and(|f| f.supports_basic_mpp()), } } fn features(&self) -> Option> { @@ -2450,7 +2450,7 @@ where L::Target: Logger { let maybe_dummy_payee_node_id = NodeId::from_pubkey(&maybe_dummy_payee_pk); let our_node_id = NodeId::from_pubkey(&our_node_pubkey); - if payee_node_id_opt.map_or(false, |payee| payee == our_node_id) { + if payee_node_id_opt.is_some_and(|payee| payee == our_node_id) { return Err("Cannot generate a route to ourselves"); } if our_node_id == maybe_dummy_payee_node_id { @@ -2541,8 +2541,7 @@ where L::Target: Logger { } else if payment_params.payee.supports_basic_mpp() { true } else if let Some(payee) = payee_node_id_opt { - network_nodes.get(&payee).map_or(false, |node| node.announcement_info.as_ref().map_or(false, - |info| info.features().supports_basic_mpp())) + network_nodes.get(&payee).is_some_and(|node| node.announcement_info.as_ref().is_some_and(|info| info.features().supports_basic_mpp())) } else { false }; let max_total_routing_fee_msat = route_params.max_total_routing_fee_msat.unwrap_or(u64::max_value()); @@ -2701,7 +2700,7 @@ where L::Target: Logger { // This requirement is currently set to be 1/max_path_count of the payment // value to ensure we only ever return routes that do not violate this limit. let minimal_value_contribution_msat: u64 = if allow_mpp { - (final_value_msat + (payment_params.max_path_count as u64 - 1)) / payment_params.max_path_count as u64 + final_value_msat.div_ceil(payment_params.max_path_count as u64) } else { final_value_msat }; @@ -3665,9 +3664,9 @@ where L::Target: Logger { // there are announced channels between the endpoints. If so, the hop might be // referring to any of the announced channels, as its `short_channel_id` might be // an alias, in which case we don't take any chances here. - network_graph.node(&target).map_or(false, |hop_node| + network_graph.node(&target).is_some_and(|hop_node| hop_node.channels.iter().any(|scid| network_graph.channel(*scid) - .map_or(false, |c| c.as_directed_from(&hop.candidate.source()).is_some())) + .is_some_and(|c| c.as_directed_from(&hop.candidate.source()).is_some())) ) }; diff --git a/lightning/src/sign/mod.rs b/lightning/src/sign/mod.rs index 5bd24d8e2e1..03f36882b0d 100644 --- a/lightning/src/sign/mod.rs +++ b/lightning/src/sign/mod.rs @@ -205,7 +205,7 @@ impl StaticPaymentOutputDescriptor { /// value of at least 1. pub fn needs_csv_1_for_spend(&self) -> bool { let chan_params = self.channel_transaction_parameters.as_ref(); - chan_params.map_or(false, |p| p.channel_type_features.supports_anchors_zero_fee_htlc_tx()) + chan_params.is_some_and(|p| p.channel_type_features.supports_anchors_zero_fee_htlc_tx()) } } impl_writeable_tlv_based!(StaticPaymentOutputDescriptor, { diff --git a/lightning/src/util/base32.rs b/lightning/src/util/base32.rs index f3d7a10c553..31e1885496e 100644 --- a/lightning/src/util/base32.rs +++ b/lightning/src/util/base32.rs @@ -46,7 +46,7 @@ impl Alphabet { // / 5 divides the data length by the number of bits per chunk (5), // * 8 multiplies the result by the number of characters per chunk (8). // + 4 rounds up to the nearest character. - let output_length = (data.len() * 8 + 4) / 5; + let output_length = (data.len() * 8).div_ceil(5); let mut ret = match self { Self::RFC4648 { padding } => { let mut ret = Self::encode_data(data, RFC4648_ALPHABET); @@ -105,7 +105,7 @@ impl Alphabet { // / 5 divides the data length by the number of bits per chunk (5), // * 8 multiplies the result by the number of characters per chunk (8). // + 4 rounds up to the nearest character. - let cap = (data.len() + 4) / 5 * 8; + let cap = data.len().div_ceil(5) * 8; let mut ret = Vec::with_capacity(cap); for chunk in data.chunks(5) { let mut buf = [0u8; 5]; @@ -132,7 +132,7 @@ impl Alphabet { // / 8 divides the data length by the number of characters per chunk (8), // * 5 multiplies the result by the number of bits per chunk (5), // + 7 rounds up to the nearest byte. - let cap = (data.len() + 7) / 8 * 5; + let cap = data.len().div_ceil(8) * 5; let mut ret = Vec::with_capacity(cap); for chunk in data.chunks(8) { let mut buf = [0u8; 8]; diff --git a/lightning/src/util/sweep.rs b/lightning/src/util/sweep.rs index efeb059b436..99a62968896 100644 --- a/lightning/src/util/sweep.rs +++ b/lightning/src/util/sweep.rs @@ -239,7 +239,7 @@ impl OutputSpendStatus { fn is_delayed(&self, cur_height: u32) -> bool { match self { Self::PendingInitialBroadcast { delayed_until_height } => { - delayed_until_height.map_or(false, |req_height| cur_height < req_height) + delayed_until_height.is_some_and(|req_height| cur_height < req_height) }, Self::PendingFirstConfirmation { .. } => false, Self::PendingThresholdConfirmations { .. } => false, diff --git a/rustfmt.toml b/rustfmt.toml index 027fcfc651b..4f88472bec5 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,5 @@ use_small_heuristics = "Max" -fn_args_layout = "Compressed" +fn_params_layout = "Compressed" hard_tabs = true use_field_init_shorthand = true max_width = 100 From 2f8687200d899418afd48096556a9083f4b7a2c4 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 11 Aug 2025 10:51:51 +0200 Subject: [PATCH 2/5] Drop `AddSigned` trait .. now that we can, addressing a TODO. --- lightning/src/ln/channel.rs | 46 +++++++------------------------------ 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index 5c12a755996..1e3c45a58ef 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -2482,10 +2482,9 @@ impl FundingScope { their_funding_contribution.to_sat(), ); - let post_value_to_self_msat = AddSigned::checked_add_signed( - prev_funding.value_to_self_msat, - our_funding_contribution.to_sat() * 1000, - ); + let post_value_to_self_msat = prev_funding + .value_to_self_msat + .checked_add_signed(our_funding_contribution.to_sat() * 1000); debug_assert!(post_value_to_self_msat.is_some()); let post_value_to_self_msat = post_value_to_self_msat.unwrap(); @@ -2551,8 +2550,7 @@ impl FundingScope { pub(super) fn compute_post_splice_value( &self, our_funding_contribution: i64, their_funding_contribution: i64, ) -> u64 { - AddSigned::saturating_add_signed( - self.get_value_satoshis(), + self.get_value_satoshis().saturating_add_signed( our_funding_contribution.saturating_add(their_funding_contribution), ) } @@ -2586,30 +2584,6 @@ impl FundingScope { } } -// TODO: Remove once MSRV is at least 1.66 -trait AddSigned { - fn checked_add_signed(self, rhs: i64) -> Option; - fn saturating_add_signed(self, rhs: i64) -> u64; -} - -impl AddSigned for u64 { - fn checked_add_signed(self, rhs: i64) -> Option { - if rhs >= 0 { - self.checked_add(rhs as u64) - } else { - self.checked_sub(rhs.unsigned_abs()) - } - } - - fn saturating_add_signed(self, rhs: i64) -> u64 { - if rhs >= 0 { - self.saturating_add(rhs as u64) - } else { - self.saturating_sub(rhs.unsigned_abs()) - } - } -} - /// Information about pending attempts at funding a channel. This includes funding currently under /// negotiation and any negotiated attempts waiting enough on-chain confirmations. More than one /// such attempt indicates use of RBF to increase the chances of confirmation. @@ -12102,10 +12076,8 @@ where if our_funding_contribution != SignedAmount::ZERO { let post_splice_holder_balance = Amount::from_sat( - AddSigned::checked_add_signed( - holder_balance_remaining.to_sat(), - our_funding_contribution.to_sat(), - ) + holder_balance_remaining.to_sat() + .checked_add_signed(our_funding_contribution.to_sat()) .ok_or(format!( "Channel {} cannot be spliced out; our remaining balance {} does not cover our negative funding contribution {}", self.context.channel_id(), @@ -12126,10 +12098,8 @@ where if their_funding_contribution != SignedAmount::ZERO { let post_splice_counterparty_balance = Amount::from_sat( - AddSigned::checked_add_signed( - counterparty_balance_remaining.to_sat(), - their_funding_contribution.to_sat(), - ) + counterparty_balance_remaining.to_sat() + .checked_add_signed(their_funding_contribution.to_sat()) .ok_or(format!( "Channel {} cannot be spliced out; their remaining balance {} does not cover their negative funding contribution {}", self.context.channel_id(), From 935d75a2fbf94f607aba2481546681a5c6cb61b2 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 11 Aug 2025 13:21:34 +0200 Subject: [PATCH 3/5] Add expected cfgs to `lightning-tests` lints --- lightning-tests/Cargo.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lightning-tests/Cargo.toml b/lightning-tests/Cargo.toml index 23c81fae4a3..1bae68b27be 100644 --- a/lightning-tests/Cargo.toml +++ b/lightning-tests/Cargo.toml @@ -20,3 +20,13 @@ lightning_0_0_125 = { package = "lightning", version = "0.0.125", features = ["_ bitcoin = { version = "0.32.2", default-features = false } [dev-dependencies] + +[lints.rust.unexpected_cfgs] +level = "forbid" +# When adding a new cfg attribute, ensure that it is added to this list. +# +# Note that Cargo automatically declares corresponding cfgs for every feature +# defined in the member-level [features] tables as "expected". +check-cfg = [ + "cfg(taproot)", +] From 1d11f2d48838d569d2cabba0e8713a48a62a4714 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Mon, 11 Aug 2025 13:43:30 +0200 Subject: [PATCH 4/5] Drop unused fields from `lightning-transaction-sync` test utils --- .../tests/integration_tests.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lightning-transaction-sync/tests/integration_tests.rs b/lightning-transaction-sync/tests/integration_tests.rs index b83d27c36ba..07b190ad30b 100644 --- a/lightning-transaction-sync/tests/integration_tests.rs +++ b/lightning-transaction-sync/tests/integration_tests.rs @@ -109,9 +109,9 @@ where #[derive(Debug)] enum TestConfirmableEvent { - Confirmed(Txid, BlockHash, u32), + Confirmed(Txid), Unconfirmed(Txid), - BestBlockUpdated(BlockHash, u32), + BestBlockUpdated, } struct TestConfirmable { @@ -140,7 +140,7 @@ impl Confirm for TestConfirmable { let block_hash = header.block_hash(); self.confirmed_txs.lock().unwrap().insert(txid, (block_hash, height)); self.unconfirmed_txs.lock().unwrap().remove(&txid); - let event = TestConfirmableEvent::Confirmed(txid, block_hash, height); + let event = TestConfirmableEvent::Confirmed(txid); self.events.lock().unwrap().push(event); } } @@ -154,7 +154,7 @@ impl Confirm for TestConfirmable { fn best_block_updated(&self, header: &Header, height: u32) { let block_hash = header.block_hash(); *self.best_block.lock().unwrap() = (block_hash, height); - let event = TestConfirmableEvent::BestBlockUpdated(block_hash, height); + let event = TestConfirmableEvent::BestBlockUpdated; self.events.lock().unwrap().push(event); } @@ -281,12 +281,12 @@ macro_rules! test_syncing { } match events[2] { - TestConfirmableEvent::BestBlockUpdated(..) => {}, + TestConfirmableEvent::BestBlockUpdated => {}, _ => panic!("Unexpected event"), } match events[3] { - TestConfirmableEvent::Confirmed(t, _, _) => { + TestConfirmableEvent::Confirmed(t) => { assert!(t == txid || t == second_txid); assert!(seen_txids.remove(&t)); }, @@ -294,7 +294,7 @@ macro_rules! test_syncing { } match events[4] { - TestConfirmableEvent::Confirmed(t, _, _) => { + TestConfirmableEvent::Confirmed(t) => { assert!(t == txid || t == second_txid); assert!(seen_txids.remove(&t)); }, From 165a6b3a35f9fb15b88e74b89d81c7232eee53d2 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Wed, 22 Oct 2025 14:37:46 -0500 Subject: [PATCH 5/5] Add `rust-version` to all crates' `Cargo.toml`s --- lightning-background-processor/Cargo.toml | 1 + lightning-block-sync/Cargo.toml | 1 + lightning-custom-message/Cargo.toml | 1 + lightning-dns-resolver/Cargo.toml | 1 + lightning-invoice/Cargo.toml | 1 + lightning-liquidity/Cargo.toml | 1 + lightning-macros/Cargo.toml | 1 + lightning-net-tokio/Cargo.toml | 1 + lightning-persister/Cargo.toml | 1 + lightning-rapid-gossip-sync/Cargo.toml | 1 + lightning-tests/Cargo.toml | 1 + lightning-transaction-sync/Cargo.toml | 1 + lightning-types/Cargo.toml | 1 + lightning/Cargo.toml | 1 + 14 files changed, 14 insertions(+) diff --git a/lightning-background-processor/Cargo.toml b/lightning-background-processor/Cargo.toml index 415676f4ea1..44edb97a34a 100644 --- a/lightning-background-processor/Cargo.toml +++ b/lightning-background-processor/Cargo.toml @@ -8,6 +8,7 @@ description = """ Utilities to perform required background tasks for Rust Lightning. """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] all-features = true diff --git a/lightning-block-sync/Cargo.toml b/lightning-block-sync/Cargo.toml index b001b47585a..2b42e3cfc31 100644 --- a/lightning-block-sync/Cargo.toml +++ b/lightning-block-sync/Cargo.toml @@ -8,6 +8,7 @@ description = """ Utilities to fetch the chain data from a block source and feed them into Rust Lightning. """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] all-features = true diff --git a/lightning-custom-message/Cargo.toml b/lightning-custom-message/Cargo.toml index fd0d302bf56..202edc64aea 100644 --- a/lightning-custom-message/Cargo.toml +++ b/lightning-custom-message/Cargo.toml @@ -8,6 +8,7 @@ description = """ Utilities for supporting custom peer-to-peer messages in LDK. """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] all-features = true diff --git a/lightning-dns-resolver/Cargo.toml b/lightning-dns-resolver/Cargo.toml index 19ffe44094e..3a3c5f36045 100644 --- a/lightning-dns-resolver/Cargo.toml +++ b/lightning-dns-resolver/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/lightningdevkit/rust-lightning/" description = "A crate which implements DNSSEC resolution for lightning clients over bLIP 32 using `tokio` and the `dnssec-prover` crate." edition = "2021" +rust-version = "1.75" [dependencies] lightning = { version = "0.2.0", path = "../lightning", default-features = false } diff --git a/lightning-invoice/Cargo.toml b/lightning-invoice/Cargo.toml index 8e0c7587f4f..46b62ed709b 100644 --- a/lightning-invoice/Cargo.toml +++ b/lightning-invoice/Cargo.toml @@ -9,6 +9,7 @@ keywords = [ "lightning", "bitcoin", "invoice", "BOLT11" ] readme = "README.md" repository = "https://github.com/lightningdevkit/rust-lightning/" edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] all-features = true diff --git a/lightning-liquidity/Cargo.toml b/lightning-liquidity/Cargo.toml index a29fc36043a..3def0798436 100644 --- a/lightning-liquidity/Cargo.toml +++ b/lightning-liquidity/Cargo.toml @@ -5,6 +5,7 @@ authors = ["John Cantrell ", "Elias Rohrer "] license = "MIT OR Apache-2.0" repository = "https://github.com/lightningdevkit/rust-lightning" edition = "2021" +rust-version = "1.75" description = """ Utility to process gossip routing data from Rapid Gossip Sync Server. """ diff --git a/lightning-tests/Cargo.toml b/lightning-tests/Cargo.toml index 1bae68b27be..9e0294be6ab 100644 --- a/lightning-tests/Cargo.toml +++ b/lightning-tests/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/lightningdevkit/rust-lightning/" description = "Tests for LDK crates" edition = "2021" +rust-version = "1.75" [features] diff --git a/lightning-transaction-sync/Cargo.toml b/lightning-transaction-sync/Cargo.toml index 48100144d01..50e48af60ce 100644 --- a/lightning-transaction-sync/Cargo.toml +++ b/lightning-transaction-sync/Cargo.toml @@ -8,6 +8,7 @@ description = """ Utilities for syncing LDK via the transaction-based `Confirm` interface. """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] all-features = true diff --git a/lightning-types/Cargo.toml b/lightning-types/Cargo.toml index 15b40aa669c..2abaaa98a1a 100644 --- a/lightning-types/Cargo.toml +++ b/lightning-types/Cargo.toml @@ -8,6 +8,7 @@ description = """ Basic types which are used in the lightning network """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/lightning/Cargo.toml b/lightning/Cargo.toml index 94919efe66d..47ae12f9e1e 100644 --- a/lightning/Cargo.toml +++ b/lightning/Cargo.toml @@ -9,6 +9,7 @@ A Complete Bitcoin Lightning Library in Rust. Handles the core functionality of the Lightning Network, allowing clients to implement custom wallet, chain interactions, storage and network logic without enforcing a specific runtime. """ edition = "2021" +rust-version = "1.75" [package.metadata.docs.rs] features = ["std", "dnssec"]