listsendpays: discriminate ongoing payment by groupid
What changed, and why it matters
This commit fixes a bug in how the xpay plugin reports payment status. Previously, when listing ongoing payments, it only checked the payment hash, which could incorrectly mark unrelated payment attempts as 'pending' instead of 'failed'. Now it also checks the group ID, so each distinct payment attempt is judged correctly. There is no direct security exploit here, but the bug could mislead users or automated tools about whether a payment succeeded or failed.
Treat as a functional bug fix rather than a security patch. Users relying on listsendpays/listpays for payment automation should update to avoid incorrect status reporting. No urgent security action is required.
Security signals we found
Status-reporting bug in payment plugin
Same payment_hash used to incorrectly classify distinct payment attempts
Fixes flaky test assertion mismatch between pending/failed statuses
No input validation, memory safety, or cryptographic changes
Evidence from the diff
The change updates attempt_ongoing() in plugins/xpay/xpay.c to accept and compare a groupid in addition to the payment_hash. listpays.c now passes pm->sortkey.groupid when checking whether a payment is still ongoing. This prevents payments with the same hash but different group IDs from being incorrectly classified as pending. The fix is driven by a flaky test failure in test_sendpay_grouping.
Changed components
plugins/xpay/listpays.cplugins/xpay/xpay.cplugins/xpay/xpay.hInspect captured patch +8 / −4
diff --git a/plugins/xpay/listpays.c b/plugins/xpay/listpays.c
index e1c9de5a..020fb296 100644
--- a/plugins/xpay/listpays.c
+++ b/plugins/xpay/listpays.c
@@ -159,7 +159,8 @@ static void add_new_entry(struct plugin *plugin,
if (pm->state & PAYMENT_COMPLETE)
json_add_string(ret, "status", "complete");
- else if (pm->state & PAYMENT_PENDING || attempt_ongoing(plugin, pm->payment_hash))
+ else if (pm->state & PAYMENT_PENDING ||
+ attempt_ongoing(plugin, pm->payment_hash, pm->sortkey.groupid))
json_add_string(ret, "status", "pending");
else
json_add_string(ret, "status", "failed");
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index 7cf2c206..d9a3c297 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -2510,13 +2510,15 @@ static struct payment *new_payment(const tal_t *ctx,
return payment;
}
-bool attempt_ongoing(struct plugin *plugin, const struct sha256 *payment_hash)
+bool attempt_ongoing(struct plugin *plugin, const struct sha256 *payment_hash,
+ u64 groupid)
{
struct xpay *xpay = xpay_of(plugin);
const struct payment *payment;
list_for_each(&xpay->payments, payment, list) {
- if (sha256_eq(&payment->payment_hash, payment_hash))
+ if (sha256_eq(&payment->payment_hash, payment_hash) &&
+ payment->group_id == groupid)
return true;
}
return false;
diff --git a/plugins/xpay/xpay.h b/plugins/xpay/xpay.h
index 886380d5..44d4305a 100644
--- a/plugins/xpay/xpay.h
+++ b/plugins/xpay/xpay.h
@@ -7,6 +7,7 @@ struct plugin;
struct sha256;
/* Are we still attempting this payment? If so, we won't list is as failed. */
-bool attempt_ongoing(struct plugin *plugin, const struct sha256 *payment_hash);
+bool attempt_ongoing(struct plugin *plugin, const struct sha256 *payment_hash,
+ u64 groupid);
#endif /* LIGHTNING_PLUGINS_XPAY_XPAY_H */
Why this scored 20/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.