Add chanmon stuck HTLC invariant
What changed, and why it matters
This commit adds a new safety check inside a fuzz-testing harness, not in production code. After simulated Lightning payments settle, it now asserts that no payment promises (HTLCs) are left stuck in any test channel. It does not change how real users' funds are handled and does not fix an active bug by itself.
No immediate action required for production deployments. Treat as a normal test-hardening commit. If the new assertion starts failing in fuzz runs, investigate the underlying state-machine behavior it exposes.
Security signals we found
Adds an invariant assertion in a fuzz harness for stuck HTLCs
Test-only change; no production code path altered
No functional fix or behavioral change to channel state machine
Evidence from the diff
The patch adds an assertion to fuzz/src/chanmon_consistency.rs in the Harness quiescence routine. After all state is flushed and claims are reported, it iterates every test node and channel and requires pending_inbound_htlcs and pending_outbound_htlcs to be empty. If the invariant is violated, the fuzz harness panics with diagnostic output. This is a test-only invariant; no production logic is modified.
Changed components
fuzz/src/chanmon_consistency.rsInspect captured patch +17 / −0
diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs
index 8a90dc9..c37808d 100644
--- a/fuzz/src/chanmon_consistency.rs
+++ b/fuzz/src/chanmon_consistency.rs
@@ -2726,6 +2726,23 @@ impl<'a, Out: Output + MaybeSend + MaybeSync> Harness<'a, Out> {
// PaymentSent event at the sender.
self.payments.assert_claims_reported();
+ // All HTLCs should have been claimed or failed once we reach quiescence.
+ for (idx, node) in self.nodes.iter().enumerate() {
+ for chan in node.list_channels() {
+ assert!(
+ chan.pending_inbound_htlcs.is_empty() && chan.pending_outbound_htlcs.is_empty(),
+ "Node {} channel {:?} has stuck HTLCs after settling all state: \
+ {} inbound {:?}, {} outbound {:?}",
+ idx,
+ chan.channel_id,
+ chan.pending_inbound_htlcs.len(),
+ chan.pending_inbound_htlcs,
+ chan.pending_outbound_htlcs.len(),
+ chan.pending_outbound_htlcs
+ );
+ }
+ }
+
// Finally, make sure that at least one end of each channel can make a substantial payment.
let chan_ab_ids = self.ab_link.channel_ids().clone();
let chan_bc_ids = self.bc_link.channel_ids().clone();
Why this scored 12/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.