Manual capture: decide held payments that arrived while no event stream was open #5

Closed
opened 2026-10-09 11:20:30 +00:00 by thesimplekid · 0 comments
Owner

Summary

LDK Server does not replay PaymentClaimable events. The README says a payment that arrives while the listener is disconnected "never becomes an authorization", and the node fails its HTLC at the claim deadline. For a payer, that means funds are locked for hours and then refunded, even though the service could have decided immediately. This is realistic: a restart or deploy while a payment is in flight is enough.

Suggestion

After every (re)subscription, page through ListPayments and treat each inbound BOLT11 payment with status Pending whose hash matches a stored hold as a Claimable:

  • payment_id: the record's payment_id;
  • hash: the hash from payment_kind::Bolt11;
  • amount: the record's amount_msat.

Then pass it through the same observe() path. At the pinned LDK Node revision, a hold-invoice payment record is created only when the HTLCs arrive (event.rs, PaymentClaimable handling), so a pending inbound record for one of our hashes means the payment is held now.

Notes:

  • ListPayments is not filtered by status, so cap the number of pages and document that a very large node may not be scanned completely.
  • observe() must stay idempotent for a payment that already has an authorization.
  • One payment that cannot be decided must not stop the scan of the rest.

npub.cash does this on every reconnect. It was checked on regtest with two LDK Server nodes: a payment made while the service was stopped was claimed on restart.

## Summary LDK Server does not replay `PaymentClaimable` events. The README says a payment that arrives while the listener is disconnected "never becomes an authorization", and the node fails its HTLC at the claim deadline. For a payer, that means funds are locked for hours and then refunded, even though the service could have decided immediately. This is realistic: a restart or deploy while a payment is in flight is enough. ## Suggestion After every (re)subscription, page through `ListPayments` and treat each **inbound BOLT11 payment with status `Pending`** whose hash matches a stored hold as a `Claimable`: - `payment_id`: the record's `payment_id`; - `hash`: the hash from `payment_kind::Bolt11`; - amount: the record's `amount_msat`. Then pass it through the same `observe()` path. At the pinned LDK Node revision, a hold-invoice payment record is created only when the HTLCs arrive (`event.rs`, `PaymentClaimable` handling), so a pending inbound record for one of our hashes means the payment is held now. Notes: - `ListPayments` is not filtered by status, so cap the number of pages and document that a very large node may not be scanned completely. - `observe()` must stay idempotent for a payment that already has an authorization. - One payment that cannot be decided must not stop the scan of the rest. npub.cash does this on every reconnect. It was checked on regtest with two LDK Server nodes: a payment made while the service was stopped was claimed on restart.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
thesimplekid/satsettle#5
No description provided.