Description:
Summary
When a client node accepts an inbound LSPS2 JIT channel, it uses TrustedChannelFeatures::ZeroConf in accept_inbound_channel_from_trusted_peer. All channel acceptors send a non-zero channel_reserve_satoshis by default, with a hard minimum of 1000 sats (MIN_THEIR_CHAN_RESERVE_SATOSHIS). For LSPS2 JIT channels this means the LSP (the funder) must keep 1000 sats locked as reserve, reducing its usable outbound capacity. On small JIT channels this is enough to push the first HTLC forward over the limit, causing it to fail immediately after the channel opens.
Failure
Freeing holding cell resulted in 0 HTLCs added, 0 HTLCs fulfilled, and 1 HTLCs failed. Followed by PaymentFailed on the payer side, even though the JIT channel opened successfully.
Root Cause
The available outbound capacity the LSP has after opening the channel is: usable = channel_capacity - 1000 (default reserve) - fee_spike_buffer (~270 sats)
For a small JIT channel (e.g. 6250 sats for a 5000 sat payment with 25% over-provisioning), the LSP only has ~4980 sats usable to forward a 4950 sat HTLC — barely enough, and any tighter configuration tips it over.
Fix
Use TrustedChannelFeatures::ZeroConfZeroReserve instead of ZeroConf when accepting inbound channels from the configured LSPS2 LSP. This sets accept_channel.channel_reserve_satoshis = 0, giving the LSP its full channel capacity as usable outbound.
This is safe because the LSP is already explicitly trusted — it was configured via set_liquidity_source_lsps2 and auto-added to trusted_peers_0conf. The reserve exists to protect against a malicious counterparty; that concern doesn't apply here.
The detection in event.rs is straightforward — channel_override_config.is_some() is true exclusively for channels coming from the configured LSPS2 LSP:
let is_lsps2_channel = channel_override_config.is_some();
let trusted_features = if is_lsps2_channel {
TrustedChannelFeatures::ZeroConfZeroReserve
} else {
TrustedChannelFeatures::ZeroConf
};
self.channel_manager.accept_inbound_channel_from_trusted_peer(
&temporary_channel_id,
&counterparty_node_id,
user_channel_id,
trusted_features,
channel_override_config,
)
Alternatively, this could be generalized to apply ZeroConfZeroReserve for all peers in trusted_peers_0conf, not just the LSPS2 LSP.
Note on API Design
As discussed in the LDK Discord, the current API intentionally shipped without a public way for the channel acceptor to set 0-reserve for the opener — the combination of 0-conf and 0-reserve was deliberately deferred. This fix implements the path that was already planned: automatically apply ZeroConfZeroReserve when the inbound channel comes from a peer in the trusted list.
Impact
Any LSPS2 JIT channel payment where the channel capacity is close to the payment amount will fail on the first HTLC forward. Larger channels (e.g. well-funded LSPs with high over-provisioning) mask the bug because the 1000-sat reserve is a smaller fraction of capacity.
Description:
Summary
When a client node accepts an inbound LSPS2 JIT channel, it uses TrustedChannelFeatures::ZeroConf in
accept_inbound_channel_from_trusted_peer. All channelacceptors send a non-zero channel_reserve_satoshisby default, with a hard minimum of 1000 sats (MIN_THEIR_CHAN_RESERVE_SATOSHIS). For LSPS2 JIT channels this means the LSP (the funder) must keep 1000 sats locked as reserve, reducing its usable outbound capacity. On small JIT channels this is enough to push the first HTLC forward over the limit, causing it to fail immediately after the channel opens.Failure
Freeing holding cell resulted in 0 HTLCs added, 0 HTLCs fulfilled, and 1 HTLCs failed. Followed by PaymentFailed on the payer side, even though the JIT channel opened successfully.
Root Cause
The available outbound capacity the LSP has after opening the channel is:
usable = channel_capacity - 1000 (default reserve) - fee_spike_buffer (~270 sats)For a small JIT channel (e.g. 6250 sats for a 5000 sat payment with 25% over-provisioning), the LSP only has ~4980 sats usable to forward a 4950 sat HTLC — barely enough, and any tighter configuration tips it over.
Fix
Use
TrustedChannelFeatures::ZeroConfZeroReserveinstead ofZeroConfwhen accepting inbound channels from the configured LSPS2 LSP. This sets accept_channel.channel_reserve_satoshis = 0, giving the LSP its full channel capacity as usable outbound.This is safe because the LSP is already explicitly trusted — it was configured via
set_liquidity_source_lsps2and auto-added to trusted_peers_0conf. The reserve exists to protect against a malicious counterparty; that concern doesn't apply here.The detection in event.rs is straightforward — channel_override_config.is_some() is true exclusively for channels coming from the configured LSPS2 LSP:
Alternatively, this could be generalized to apply ZeroConfZeroReserve for all peers in trusted_peers_0conf, not just the LSPS2 LSP.
Note on API Design
As discussed in the LDK Discord, the current API intentionally shipped without a public way for the channel acceptor to set 0-reserve for the opener — the combination of 0-conf and 0-reserve was deliberately deferred. This fix implements the path that was already planned: automatically apply ZeroConfZeroReserve when the inbound channel comes from a peer in the trusted list.
Impact
Any LSPS2 JIT channel payment where the channel capacity is close to the payment amount will fail on the first HTLC forward. Larger channels (e.g. well-funded LSPs with high over-provisioning) mask the bug because the 1000-sat reserve is a smaller fraction of capacity.