Consider probe events for stuck payments check in chanmon_consistency
What changed, and why it matters
This commit fixes a fuzz testing harness so it correctly tracks payments that the fuzzer accidentally generates as probe-like payments. It is a test-only change and does not affect real Lightning node behavior or user funds.
No security action required; this is a test/fuzzing correctness fix. Reviewers can treat it as a normal code-quality change.
Security signals we found
No production code changed
Test-only fuzz harness adjustment
No cryptographic, network, or consensus changes
No memory safety or input validation changes in runtime code
Evidence from the diff
The change is in fuzz/src/chanmon_consistency.rs, a fuzz target. Previously, ProbeSuccessful and ProbeFailed events were ignored in the stuck-payments check. Because the fuzzer can randomly construct payment hashes/preimages that hash-match the probe detection logic, those events can fire for otherwise ordinary fuzz payments. The patch treats ProbeSuccessful like PaymentSent (moving the payment_id from pending to resolved) and ProbeFailed like PaymentFailed (removing from pending or asserting already resolved). This prevents false-positive ‘stuck payment’ assertions during fuzzing. No production code is modified.
Changed components
fuzz/src/chanmon_consistency.rsInspect captured patch +16 / −8
diff --git a/fuzz/src/chanmon_consistency.rs b/fuzz/src/chanmon_consistency.rs
index 69af660..abaa92d 100644
--- a/fuzz/src/chanmon_consistency.rs
+++ b/fuzz/src/chanmon_consistency.rs
@@ -1774,7 +1774,22 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(
assert!(resolved_payments[$node].contains(&sent_id));
}
},
- events::Event::PaymentFailed { payment_id, .. } => {
+ // Even though we don't explicitly send probes, because probes are
+ // detected based on hashing the payment hash+preimage, its rather
+ // trivial for the fuzzer to build payments that accidentally end up
+ // looking like probes.
+ events::Event::ProbeSuccessful { payment_id, .. } => {
+ let idx_opt =
+ pending_payments[$node].iter().position(|id| *id == payment_id);
+ if let Some(idx) = idx_opt {
+ pending_payments[$node].remove(idx);
+ resolved_payments[$node].push(payment_id);
+ } else {
+ assert!(resolved_payments[$node].contains(&payment_id));
+ }
+ },
+ events::Event::PaymentFailed { payment_id, .. }
+ | events::Event::ProbeFailed { payment_id, .. } => {
let idx_opt =
pending_payments[$node].iter().position(|id| *id == payment_id);
if let Some(idx) = idx_opt {
@@ -1789,13 +1804,6 @@ pub fn do_test<Out: Output + MaybeSend + MaybeSync>(
events::Event::PaymentClaimed { .. } => {},
events::Event::PaymentPathSuccessful { .. } => {},
events::Event::PaymentPathFailed { .. } => {},
- events::Event::ProbeSuccessful { .. }
- | events::Event::ProbeFailed { .. } => {
- // Even though we don't explicitly send probes, because probes are
- // detected based on hashing the payment hash+preimage, its rather
- // trivial for the fuzzer to build payments that accidentally end up
- // looking like probes.
- },
events::Event::PaymentForwarded { .. } if $node == 1 => {},
events::Event::ChannelReady { .. } => {},
events::Event::HTLCHandlingFailed { .. } => {},
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.