Add config flag for held outbound HTLCs
What changed, and why it matters
This commit adds a new optional configuration flag to the Lightning Dev Kit (LDK) library. When enabled, a paying node can ask its next-hop peers to hold outgoing payment contracts (HTLCs) for a while, which is useful for nodes that are often offline. The feature is off by default and is described by the project as intended only for private, often-offline nodes. There is no direct evidence in the commit that this fixes a security vulnerability; it appears to be a new feature.
Treat as a feature commit rather than a security patch. If deploying this flag, review the full HTLC-hold implementation (not shown here) and ensure it is only enabled on private, often-offline nodes as documented. Monitor for future commits that implement the actual hold behavior.
Security signals we found
New config flag changes payment/HTLC behavior
Feature intended for often-offline private nodes, with explicit misuse warning
Default is false, requiring explicit opt-in
No actual HTLC-holding logic visible in this commit; flag plumbing only
Evidence from the diff
The change introduces UserConfig::hold_outbound_htlcs_at_next_hop, a boolean defaulting to false. When set to true, an LDK node paying a BOLT12 static invoice will attempt to negotiate holding outbound HTLCs at the next hop for peers that advertise the htlc_hold feature. Serialization/deserialization and fuzzing seeds are updated to account for the new config byte. The commit explicitly warns that this is for private/offline nodes and should not be used by always-online announced nodes. No logic for actually holding HTLCs is present in this diff; it only exposes the flag and its serialization.
Changed components
lightning/src/util/config.rsfuzz/src/full_stack.rsInspect captured patch +18 / −2
diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs
index 3fb1e4a..0d6768e 100644
--- a/fuzz/src/full_stack.rs
+++ b/fuzz/src/full_stack.rs
@@ -1048,7 +1048,7 @@ fn two_peer_forwarding_seed() -> Vec<u8> {
// our network key
ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test);
// config
- ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff000100000000", &mut test);
+ ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff00010000000000", &mut test);
// new outbound connection with id 0
ext_from_hex("00", &mut test);
@@ -1502,7 +1502,7 @@ fn gossip_exchange_seed() -> Vec<u8> {
// our network key
ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test);
// config
- ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff000100000000", &mut test);
+ ext_from_hex("000000000090000000000000000064000100000000000100ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff00010000000000", &mut test);
// new outbound connection with id 0
ext_from_hex("00", &mut test);
diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs
index 656cfc4..e52a9c3 100644
--- a/lightning/src/util/config.rs
+++ b/lightning/src/util/config.rs
@@ -943,6 +943,20 @@ pub struct UserConfig {
/// Default value: `false`
#[cfg(test)]
pub enable_htlc_hold: bool,
+ /// If this is set to true, then if we as an often-offline payer receive a [`StaticInvoice`] to
+ /// pay, we will attempt to hold the corresponding outbound HTLCs with our next-hop channel
+ /// counterparty(s) that support the `htlc_hold` feature. This allows our node to go offline once
+ /// the HTLCs are locked in even though the recipient may not yet be online to receive them.
+ ///
+ /// This option is intended for usage by private nodes, and should NOT be set if we are an
+ /// announced node that is expected to be online at all times.
+ ///
+ /// Setting this to `true` may lead to HTLC failures if downgrading to LDK versions < 0.2.
+ ///
+ /// Default value: `false`
+ ///
+ /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
+ pub hold_outbound_htlcs_at_next_hop: bool,
}
impl Default for UserConfig {
@@ -959,6 +973,7 @@ impl Default for UserConfig {
enable_dual_funded_channels: false,
#[cfg(test)]
enable_htlc_hold: false,
+ hold_outbound_htlcs_at_next_hop: false,
}
}
}
@@ -979,6 +994,7 @@ impl Readable for UserConfig {
accept_intercept_htlcs: Readable::read(reader)?,
manually_handle_bolt12_invoices: Readable::read(reader)?,
enable_dual_funded_channels: Readable::read(reader)?,
+ hold_outbound_htlcs_at_next_hop: Readable::read(reader)?,
})
}
}
Why this scored 18/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.