Skip to content

Add payment metadata store#811

Draft
tnull wants to merge 8 commits intolightningdevkit:mainfrom
tnull:2026-02-payment-metadata-store
Draft

Add payment metadata store#811
tnull wants to merge 8 commits intolightningdevkit:mainfrom
tnull:2026-02-payment-metadata-store

Conversation

@tnull
Copy link
Collaborator

@tnull tnull commented Feb 26, 2026

Draft for now.

Switch all `lightning*` dependencies to `tnull/rust-lightning` branch
`2025-11-lsps1-refactor` which adds `Writeable`/`Readable` impls for
`Bolt11Invoice`, `Offer`, and `Refund`.

Also adapt to `LiquidityManager` API changes: the `ChainSource` generic
parameter was removed and `LiquidityServiceConfig` gained an
`lsps1_service_config` field.

Generated with the help of AI (Claude Code).

Co-Authored-By: HAL 9000
Introduce `MetadataId`, `PaymentMetadataKind`, `PaymentMetadataEntry`,
and `PaymentMetadataEntryUpdate` in a new `payment/metadata_store`
module. These types form the foundation of a persistence-backed metadata
store keyed by an opaque `MetadataId` that decouples metadata lifecycle
from `PaymentId`.

`PaymentMetadataKind` can hold `Bolt11Invoice`s, BOLT12 `Offer`s/
`Refund`s, and `LSPFeeLimits`. All variants use
`impl_writeable_tlv_based_enum!` for serialization, leveraging the new
`Writeable`/`Readable` impls added in the previous dependency bump.

Generated with the help of AI (Claude Code).

Co-Authored-By: HAL 9000
Add the `PaymentMetadataStore` struct that wraps a `DataStore` with a
reverse index from `PaymentId` to `MetadataId`. This enables efficient
lookup of metadata entries by payment ID.

API methods:
- `insert` / `get` / `remove` — basic CRUD with reverse index upkeep
- `add_payment_id` — associate additional payment IDs with an entry
- `get_for_payment_id` — reverse index lookup
- `get_lsp_fee_limits_for_payment_id` — convenience for LSP fee checks
- `remove_payment_id` — clean up reverse index when payments are removed

Also add the `payment_metadata` namespace constants in `io/mod.rs`.

Generated with the help of AI (Claude Code).

Co-Authored-By: HAL 9000
Add comprehensive tests covering:
- Serialization roundtrip for `LSPFeeLimits` variant
- Basic insert / get / remove lifecycle
- Reverse index correctness with multiple payment IDs
- `add_payment_id` adds associations correctly
- `get_lsp_fee_limits_for_payment_id` returns correct limits
- `remove_payment_id` cleans up the reverse index
- Persistence roundtrip (insert, reconstruct from same KVStore)

Generated with the help of AI (Claude Code).

Co-Authored-By: HAL 9000
…and `EventHandler`

Add `read_payment_metadata` to the startup `tokio::join!` call in the
builder to load persisted metadata entries. Construct an
`Arc<PaymentMetadataStore>` and thread it through to `Node`,
`Bolt11Payment` (along with `KeysManager`), and `EventHandler`.

Update `Node::remove_payment` to also call
`payment_metadata_store.remove_payment_id` so the reverse index stays
consistent when payment store entries are removed.

Generated with the assistance of AI tools.

Co-Authored-By: HAL 9000
In `receive_via_jit_channel_inner`, insert a
`PaymentMetadataKind::LSPFeeLimits` entry into the
`PaymentMetadataStore` so JIT channel fee limits are persisted
independently of the `PaymentStore` entry.

In the `PaymentClaimable` event handler, add a fallback path that
checks the metadata store for LSP fee limits when the `PaymentKind`
doesn't carry them directly.

Generated with the assistance of AI tools.

Co-Authored-By: HAL 9000
…ethods

Remove the eager `PaymentStore` insertions from `receive_inner` and
`receive_via_jit_channel_inner`.  Inbound payment entries will instead
be created on demand by the `EventHandler` when the corresponding LDK
events arrive.

Outbound payment entries (created by `send` / `send_using_amount`) are
kept as before so the sender always has a store record immediately after
initiating a payment.

Generated with the assistance of AI tools.

Co-Authored-By: HAL 9000
With receive-side pre-creation removed, the event handler must now
create `PaymentStore` entries when it first encounters an inbound
payment.

In the `PaymentClaimable` handler, when a `Bolt11InvoicePayment` is not
found in the store:
- Manual-claim path (`preimage == None`): check the metadata store for
  `LSPFeeLimits`, validate counterparty-skimmed fees, create a
  `Bolt11Jit` or `Bolt11` entry, and emit `PaymentClaimable`.
- Auto-claim path (`preimage == Some`): same fee-limit check and entry
  creation, then fall through to `claim_funds`.

In the `PaymentClaimed` handler, when the update returns `NotFound`,
insert a new entry using the payment purpose to determine the kind.

Outbound payment handlers (`PaymentSent`, `PaymentFailed`) are
unchanged since entries are still pre-created by `send()` /
`send_using_amount()`.

Generated with the assistance of AI tools.

Co-Authored-By: HAL 9000
@tnull tnull self-assigned this Feb 26, 2026
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Feb 26, 2026

👋 Hi! This PR is now in draft status.
I'll wait to assign reviewers until you mark it as ready for review.
Just convert it out of draft status when you're ready for review!

@tnull tnull marked this pull request as draft February 26, 2026 14:08
@tnull tnull moved this to Goal: Merge in Weekly Goals Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Goal: Merge

Development

Successfully merging this pull request may close these issues.

2 participants