Expose probe status in recent payments
What changed, and why it matters
This commit adds a small informational flag to a public API so that users of the Lightning library can tell whether a recent payment was a 'probe' (a test payment used to check network liquidity) rather than a real payment. It is a feature/API improvement, not a security fix, and does not change how funds are handled.
No security action required. Treat as a normal API/feature change; review downstream consumers of `RecentPaymentDetails` for compilation breaks due to the new struct field.
Security signals we found
No security-relevant behavior change: only metadata exposure in an existing API
No cryptographic, authorization, or consensus changes
No memory-safety, panic, or resource-exhaustion signals in diff
Evidence from the diff
The patch extends the RecentPaymentDetails::Pending and RecentPaymentDetails::Abandoned enum variants with a new is_probe: bool field. The value is computed by calling outbound_payment::payment_is_probe using the existing probing_cookie_secret. The #[cfg(test)] restriction on the outbound_payment import is removed so the function can be used in non-test builds. Tests are updated to assert that probes are correctly flagged in list_recent_payments.
Changed components
lightning/src/ln/channelmanager.rslightning/src/ln/payment_tests.rsPublic API: `RecentPaymentDetails` enumPublic API: `ChannelManager::list_recent_payments`Inspect captured patch +41 / −11
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 6448659..f5a2bb0 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -84,7 +84,6 @@ use crate::ln::onion_utils::{
};
use crate::ln::onion_utils::{process_fulfill_attribution_data, AttributionData};
use crate::ln::our_peer_storage::{EncryptedOurPeerStorage, PeerStorageMonitorHolder};
-#[cfg(test)]
use crate::ln::outbound_payment;
#[cfg(any(test, feature = "_externalize_tests"))]
use crate::ln::outbound_payment::PaymentSendFailure;
@@ -3289,6 +3288,8 @@ pub enum RecentPaymentDetails {
/// Total amount (in msat, excluding fees) across all paths for this payment,
/// not just the amount currently inflight.
total_msat: u64,
+ /// Whether this payment is a liquidity probe.
+ is_probe: bool,
},
/// When a pending payment is fulfilled, we continue tracking it until all pending HTLCs have
/// been resolved. Upon receiving [`Event::PaymentSent`], we delay for a few minutes before the
@@ -3316,6 +3317,8 @@ pub enum RecentPaymentDetails {
payment_id: PaymentId,
/// Hash of the payment that we have given up trying to send.
payment_hash: PaymentHash,
+ /// Whether this payment is a liquidity probe.
+ is_probe: bool,
},
}
@@ -4102,14 +4105,21 @@ impl<
Some(RecentPaymentDetails::AwaitingInvoice { payment_id: *payment_id })
},
PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
+ let is_probe = outbound_payment::payment_is_probe(payment_hash, payment_id, self.probing_cookie_secret);
Some(RecentPaymentDetails::Pending {
payment_id: *payment_id,
payment_hash: *payment_hash,
total_msat: *total_msat,
+ is_probe,
})
},
PendingOutboundPayment::Abandoned { payment_hash, .. } => {
- Some(RecentPaymentDetails::Abandoned { payment_id: *payment_id, payment_hash: *payment_hash })
+ let is_probe = outbound_payment::payment_is_probe(payment_hash, payment_id, self.probing_cookie_secret);
+ Some(RecentPaymentDetails::Abandoned {
+ payment_id: *payment_id,
+ payment_hash: *payment_hash,
+ is_probe,
+ })
},
PendingOutboundPayment::Fulfilled { payment_hash, .. } => {
Some(RecentPaymentDetails::Fulfilled { payment_id: *payment_id, payment_hash: *payment_hash })
diff --git a/lightning/src/ln/payment_tests.rs b/lightning/src/ln/payment_tests.rs
index 5b4f5f9..f2ab44f 100644
--- a/lightning/src/ln/payment_tests.rs
+++ b/lightning/src/ln/payment_tests.rs
@@ -1591,17 +1591,32 @@ fn sent_probe_is_probe_of_sending_node() {
// Then build an actual two-hop probing path
let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], 100_000);
- match nodes[0].node.send_probe(route.paths[0].clone()) {
- Ok((payment_hash, payment_id)) => {
- assert!(nodes[0].node.payment_is_probe(&payment_hash, &payment_id));
- assert!(!nodes[1].node.payment_is_probe(&payment_hash, &payment_id));
- assert!(!nodes[2].node.payment_is_probe(&payment_hash, &payment_id));
- },
- _ => panic!(),
- }
+ let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
+ assert!(nodes[0].node.payment_is_probe(&payment_hash, &payment_id));
+ assert!(!nodes[1].node.payment_is_probe(&payment_hash, &payment_id));
+ assert!(!nodes[2].node.payment_is_probe(&payment_hash, &payment_id));
+ assert!(matches!(
+ nodes[0].node.list_recent_payments().as_slice(),
+ [RecentPaymentDetails::Pending {
+ payment_id: listed_payment_id,
+ payment_hash: listed_payment_hash,
+ is_probe: true,
+ ..
+ }] if *listed_payment_id == payment_id && *listed_payment_hash == payment_hash
+ ));
get_htlc_update_msgs(&nodes[0], &node_b_id);
check_added_monitors(&nodes[0], 1);
+
+ nodes[0].node.abandon_payment(payment_id);
+ assert!(matches!(
+ nodes[0].node.list_recent_payments().as_slice(),
+ [RecentPaymentDetails::Abandoned {
+ payment_id: listed_payment_id,
+ payment_hash: listed_payment_hash,
+ is_probe: true,
+ }] if *listed_payment_id == payment_id && *listed_payment_hash == payment_hash
+ ));
}
#[test]
@@ -2142,7 +2157,12 @@ fn test_trivial_inflight_htlc_tracking() {
}
let pending_payments = nodes[0].node.list_recent_payments();
assert_eq!(pending_payments.len(), 1);
- let details = RecentPaymentDetails::Pending { payment_id, payment_hash, total_msat: 500000 };
+ let details = RecentPaymentDetails::Pending {
+ payment_id,
+ payment_hash,
+ total_msat: 500000,
+ is_probe: false,
+ };
assert_eq!(pending_payments[0], details);
// Now, let's claim the payment. This should result in the used liquidity to return `None`.
Why this scored 16/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.