lnworker: allow overwriting amount of sent payment info
What changed, and why it matters
This commit loosens the rules for saving Lightning payment details when a user is retrying a failed or unpaid outgoing payment. It allows the wallet to overwrite the payment amount and status for a previously saved sent payment, specifically so users can retry paying an invoice that has no fixed amount (a '0 amount invoice') with a different amount. There is no direct evidence in the commit that this fixes a security vulnerability; it appears to be a usability improvement for retrying payments.
Review as a normal functional/usability change. Verify that the status and amount overwrite cannot be triggered by an attacker through a malicious invoice or payment flow, and that the PR_UNPAID/PR_FAILED gating prevents unintended mutation of completed payments. No immediate security action is indicated by the diff alone.
Security signals we found
Logic change in payment state handling for Lightning Network sent payments
Relaxation of overwrite restrictions, but limited to PR_UNPAID/PR_FAILED statuses
No input validation, cryptographic, or authorization changes visible in diff
Evidence from the diff
In electrum/lnworker.py, the LNWallet.save_payment_info() method is modified. Previously, when a new PaymentInfo matched an existing sent-direction payment hash, only the creation timestamp was allowed to be updated. Now, if the existing sent payment is unpaid or failed, the new PaymentInfo may also overwrite the status and amount_msat fields. The change is gated by checking old_info.status is PR_UNPAID or PR_FAILED. The final comparison still raises an exception if any other fields differ. This enables retrying 0-amount invoices with different amounts.
Changed components
electrum/lnworker.pyLNWallet.save_payment_info()Lightning Network payment info storageInspect captured patch +9 / −3
diff --git a/electrum/lnworker.py b/electrum/lnworker.py
index f96640c..9a37fef 100644
--- a/electrum/lnworker.py
+++ b/electrum/lnworker.py
@@ -2553,9 +2553,15 @@ class LNWallet(LNWorker):
if old_info := self.get_payment_info(payment_hash=info.payment_hash, direction=info.direction):
if info == old_info:
return # already saved
- if info.direction == SENT:
- # allow saving of newer PaymentInfo if it is a sending attempt
- old_info = dataclasses.replace(old_info, creation_ts=info.creation_ts)
+ if info.direction == SENT and old_info.status in (PR_UNPAID, PR_FAILED):
+ # allow saving of newer PaymentInfo if it is a sending attempt and the previous
+ # payment failed or was not yet attempted
+ old_info = dataclasses.replace(
+ old_info,
+ creation_ts=info.creation_ts,
+ status=info.status,
+ amount_msat=info.amount_msat, # might retrying to pay 0 amount invoice
+ )
if info != dataclasses.replace(old_info, status=info.status):
# differs more than in status. let's fail
raise Exception(f"payment_hash already in use: {info=} != {old_info=}")
Why this scored 29/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.