Skip to content

fix: refresh lightning activity on retry - #1282

Merged
jvsena42 merged 3 commits into
masterfrom
fix/1042-refresh-ln-activity-amount
Sep 20, 2026
Merged

jvsena42 merged 3 commits into
masterfrom
fix/1042-refresh-ln-activity-amount

Conversation

@jvsena42

@jvsena42 jvsena42 commented Sep 16, 2026

Copy link
Copy Markdown
Member

Fixes #1042
Related to #1275

This PR fixes Lightning activity rows keeping the failed attempt's amount after a retry of the same invoice succeeds.

Description

  • Refreshes the amount, fee and preimage of an existing Lightning activity when LDK reports an update for its payment hash, so a retry with a different amount shows what was actually sent. Before, only the status, update time and contact were refreshed.
  • Keeps the stored amount, fee and preimage when the update reports none. A pending retry has no fee yet, so it shows the earlier fee until the payment succeeds.
  • Keeps the stored message on every update. LDK reports a description-hash invoice's hash as its description, so copying it would overwrite an LNURL comment.
  • Keeps one activity row per payment hash, overwritten with the final attempt, which matches iOS.

Out of Scope

  • Activity sync: rows created for the first time still take LDK's description as the message, which for description-hash invoices is the hex hash. This is unchanged from master.
  • Activity sync: the guard that decides whether an existing row is updated is unchanged and has no dedicated test.
  • Activity type, timestamp, invoice, seen/created times and contact attribution of an existing row are not changed.
  • iOS: no change needed. The issue says iOS already shows the final attempt.

Design

N/A — no UI changes.

Preview

N/A. The activity screens do not change. The fix changes which amount is stored, and the repro needs a second node to issue an amountless invoice plus a forced asynchronous route failure, so no recording was made.

QA Notes

Manual Tests

  • 1a. Amountless LN invoice from another node → Send 300,000 sats so routing fails (ROUTE_NOT_FOUND) → Activity: row shows Failed with 300,000 sats.
    • 1b. Pay the same invoice again with a small amount (e.g. 22 sats) → Activity: the same row shows Sent with 22 sats and the fee of the successful attempt, and no second row appears.
  • 2. regression: Send to an LNURL-pay address with a comment → Activity Detail after it succeeds: the comment is still shown.
  • 3. regression: Receive an LN payment → Activity: amount and status are correct.

These steps were not run on a device. The fix is covered by unit tests only, and the call from the activity sync into the new update step was checked by code review.

Automated Checks

  • Unit tests added in CoreServiceTest.kt: a failed 300,000 sat row retried as a succeeded 22 sat payment takes the new amount, fee (1,999 msat rounds down to 1 sat), preimage, status, update time and contact; untouched fields stay the same even when the retry reports a different invoice; a missing fee, amount or preimage keeps the stored value; the stored message survives a description hash and a different description.
  • just test file CoreServiceTest is rejected by an existing guard in app/build.gradle.kts, so the class was run with just test file to.bitkit.services.CoreServiceTest.
  • Local runs of just compile, just test and just lint passed.

jvsena42 and others added 2 commits September 16, 2026 17:27
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jvsena42 jvsena42 self-assigned this Sep 16, 2026
@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no actionable correctness, security, or repository-rule violations identified.

Summary

This PR updates existing Lightning activity rows with the latest available payment amount, fee, and preimage while preserving stored values when an update omits them.

  • Keeps one activity row per payment hash so a successful retry replaces the failed attempt’s displayed details.
  • Preserves user-facing messages and other immutable activity fields.
  • Adds focused unit coverage for replacement and preservation behavior.
  • Adds a changelog entry for the retry correction.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[LDK Bolt11 payment update] --> B{Existing Lightning activity?}
    B -- No --> C[Create activity from payment details]
    B -- Yes --> D[Copy latest status, timestamp, and contact]
    D --> E{New amount, fee, or preimage present?}
    E -- Yes --> F[Replace corresponding stored fields]
    E -- No --> G[Preserve corresponding stored fields]
    F --> H[Persist one row for payment hash]
    G --> H
    C --> H
Loading

Reviews (1) · Last reviewed commit: "fix: keep stored message on lightning ac..."

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Regtest APK

Built from 8c250db (run).

Download bitkit-dev-debug universal APK (expires in 30 days).

@jvsena42
jvsena42 added this pull request to stack #1311 September 17, 2026 21:44

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — no HIGH/MEDIUM findings at head da30e5580. (Posted as a comment because GitHub does not allow approving your own PR.)

Checked: Traced LightningActivity.withPaymentUpdate (app/src/main/java/to/bitkit/services/CoreService.kt:334-346) and its only caller processBolt11 (CoreService.kt:769-775): the fields now overwritten on an existing row are value, fee and preimage, each guarded with ?: so a null LDK value keeps the stored one; status/updatedAt/contact behave as on master and message/invoice/timestamp/createdAt/seenAt are untouched. Enumerated every writer of Lightning rows at the PR head: LDK sync (this path), setContact/clearContact (copy contact + updatedAt=now, so the pre-existing guard at CoreService.kt:756 skips the next sync), markAsSeen (seenAt only), gift SuccessWithoutLiquidity (id is the funding txid/order id, never a payment hash, so sync cannot match it; the with-liquidity gift path inserts no row and relies on LDK), CJIT insert (funding txid id) and RN migration upserts (ldk-node's payment store is not populated by migrated channel monitors, so no hash overlap). No writer stores an LNURL comment or any value/fee/preimage that LDK's PaymentDetails could degrade. Checked received payments (inbound PENDING skipped; SUCCEEDED carries the claimed amount and preimage, fee null keeps 0), pending->failed (amount unchanged, fee/preimage null keep stored), a retry that fails synchronously (ldk-node records a FAILED row with the new amount, guard passes on newer timestamp, value updated to the new attempt), and zero-amount invoices (outbound amount always set by ldk-node at send; inbound amount set on claim). Rounding is consistent with row creation (amountSats ceil, msatFloorOf fee). ServiceQueue.CORE is a single-thread dispatcher, so the only interleaving is the pre-existing stale-list status regression between a full sync and handlePaymentEvent, which this PR does not worsen and the next sync repairs. The linked issue's retry path (FAILED 300k -> PENDING/SUCCEEDED 22 sats via handlePaymentEvent or full sync) now lands the final attempt's amount, fee and preimage on the same row.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM

@ovi-reviewer ovi-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: ✅ Approve


Review: diff 3 files.

Findings:
N/A

Audit:
Audited - no findings.

Coverage:
QA: journeys and manual tests await all reviewers to approve, author can run it now via comment: @ovi-reviewer test


Reviewed by claude-opus-5-xhigh via gh-pr-review-loop skill
Commands: @ovi-reviewer test · retest · audit (author or owner)

@ovitrif ovitrif left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utAck

@piotr-iohk

Copy link
Copy Markdown
Collaborator

QA reviewed on 8c250db.

Retried a failed Lightning send with a smaller amount on the same invoice. The activity row now shows the new amount; the old failed amount is gone.

No findings.

Checked and clean

  • Retry updates amount, fee, and preimage on the existing row and leaves the payment message alone.
  • Unit tests cover that retry path.

QA LGTM

@jvsena42
jvsena42 merged commit 7e49f54 into master Sep 20, 2026
19 checks passed
@jvsena42
jvsena42 deleted the fix/1042-refresh-ln-activity-amount branch September 20, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: LN activity keeps failed attempt amount after successful retry (same invoice)

3 participants