Add a `Path::total_cltv_expiry_delta` accessor
What changed, and why it matters
This commit adds a small public helper method that sums up the time-lock delays across all hops in a Lightning payment path. It also adds an internal consistency check to make sure the computed total matches what the code uses when building an onion packet. There is no security fix here—this is a routine code-quality and API-convenience change.
No security action required. Review as normal API addition.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces Path::total_cltv_expiry_delta() in router.rs, returning the sum of cltv_expiry_delta values across Path::hops. In onion_utils.rs it adds a debug_assert_eq! verifying that htlc_cltv - cur_block_height equals that total inside create_payment_onion_internal. This is a pure accessor plus an assertion; it does not alter behavior in release builds or change any trust boundary.
Changed components
lightning/src/routing/router.rslightning/src/ln/onion_utils.rsInspect captured patch +7 / −0
diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs
index a74d5fe..ffb4f4c 100644
--- a/lightning/src/ln/onion_utils.rs
+++ b/lightning/src/ln/onion_utils.rs
@@ -2696,6 +2696,7 @@ pub(crate) fn create_payment_onion_internal<T: secp256k1::Signing>(
invoice_request,
trampoline_packet_option,
)?;
+ debug_assert_eq!(htlc_cltv - cur_block_height, path.total_cltv_expiry_delta());
let onion_keys = construct_onion_keys(&secp_ctx, &path, session_priv);
let onion_packet = construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash)
diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs
index ee08f9e..97f9871 100644
--- a/lightning/src/routing/router.rs
+++ b/lightning/src/routing/router.rs
@@ -644,6 +644,12 @@ impl Path {
}
}
+ /// Gets the total CLTV expiry delta which will be added to the current block height (plus some
+ /// extra headroom) when sending the HTLC
+ pub fn total_cltv_expiry_delta(&self) -> u32 {
+ self.hops.iter().map(|hop| hop.cltv_expiry_delta).sum()
+ }
+
/// True if this [`Path`] has at least one Trampoline hop.
pub fn has_trampoline_hops(&self) -> bool {
self.blinded_tail.as_ref().is_some_and(|bt| !bt.trampoline_hops.is_empty())
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.