Skip to content

Remove invoice_id from static invoice server protocol #4009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

valentinewallace
Copy link
Contributor

In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometime based on (recipient_id, invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

Here we transition to the server always finding a specific invoice based on
(recipient_id, invoice_slot) and get rid of the invoice_id concept.

Missed removing some _ prefixes from vars that were previously cfg-gated.
In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometimes based on (recipient_id,
invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

We'd like to transition to the server always finding a specific invoice based on
(recipient_id, invoice_slot) and get rid of the invoice_id concept.

Now that the invoice_slot is in the initial paths request, the server will be
able to include the slot in the offer paths that they create in response,
allowing the slot to be surfaced instead of the invoice_id when the invoice
request comes in, in upcoming commits.
When we as an async recipient receive offer paths from the static invoice server,
we create an offer and cache it, retrying persisting a corresponding invoice with
the server until it succeeds.

In the initially-merged version of this protocol, we would put this cached
offer in any slot in the cache that needed an offer at the time the offer paths
were received. However, in the last commit we started requesting offer paths
for a specific slot in the cache, as part of eliminating the use of the
invoice_id field in the overall protocol.

As a result, here we put the cached offer in the specific cache slot that the
original OfferPathsRequest indicated, rather than any slot that could use a new
offer.
In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometime based on (recipient_id,
invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

We'd like to transition to the server always finding a specific invoice based on
(recipient_id, invoice_slot) and get rid of the invoice_id concept.

As part of this series of commits, include the invoice_slot in the
ServeStaticInvoice blinded path context that the server creates when sending an
offer_paths message. This is possible due to a previous commit including the
invoice_slot in the initial offer_paths_request from the recipient, and lays
the groundwork for removing the invoice_id field from this blinded path
context.
@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Aug 13, 2025

👋 Thanks for assigning @tnull as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

Copy link

codecov bot commented Aug 13, 2025

Codecov Report

❌ Patch coverage is 89.10891% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.83%. Comparing base (df9232b) to head (713b5fb).

Files with missing lines Patch % Lines
lightning/src/offers/async_receive_offer_cache.rs 81.57% 6 Missing and 1 partial ⚠️
lightning/src/offers/flow.rs 93.10% 1 Missing and 1 partial ⚠️
lightning/src/ln/async_payments_tests.rs 87.50% 1 Missing ⚠️
lightning/src/ln/channelmanager.rs 96.15% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4009      +/-   ##
==========================================
- Coverage   88.85%   88.83%   -0.02%     
==========================================
  Files         175      175              
  Lines      127710   127706       -4     
  Branches   127710   127706       -4     
==========================================
- Hits       113475   113454      -21     
- Misses      11672    11696      +24     
+ Partials     2563     2556       -7     
Flag Coverage Δ
fuzzing 21.86% <0.00%> (+<0.01%) ⬆️
tests 88.67% <89.10%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometime based on (recipient_id,
invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

We'd like to transition to the server always finding a specific invoice based on
(recipient_id, invoice_slot) and get rid of the invoice_id concept.

In the previous commit the server began including the invoice_slot in the
ServeStaticInvoice blinded path context that gets provided back to themselves.
Therefore there is no need for the recipient to redundantly include it in the
ServeStaticInvoice onion message itself.
In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometime based on (recipient_id,
invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

We'd like to transition to the server always finding a specific invoice based on
(recipient_id, invoice_slot) and get rid of the invoice_id concept.

Previously, when an invoice request would come in for the server on behalf of
the often-offline recipient, they would need to find the static invoice based
on the recipient_id and invoice_id. However, previous commits have now led to
the server being able to use the invoice_slot in the initial offer paths that
they create, which we do here, obviating the need for them to create their own
randomly-generated invoice_id. The final dangling references to the invoice_id
will be removed in the next commit.
In the initially-merged version of the static invoice server protocol, the
static invoice server would sometimes have to find a specific static invoice
based on (recipient_id, invoice_slot) and sometimetimes based on (recipient_id,
invoice_id). This made the API harder to use in terms of how the server would
index into the KVStore.

Over the course of the previous commits we transitioned to the server always
finding a specific invoice based on (recipient_id, invoice_slot). We still have
a few dangling references to invoice_id in some messages and events though, so
remove those here.
@valentinewallace valentinewallace force-pushed the 2025-08-simplify-static-inv-server branch from 713b5fb to d3c9a03 Compare August 14, 2025 15:10
@tnull tnull self-requested a review August 14, 2025 19:09
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Did a first high-level path.

IMO it would be preferable if we could refactor the offers field to be a helper type that wraps Vec<Option<AsyncReceiveOffer>>, as the internal API otherwise get's a bit hard to follow.

if !needs_new_offers {
return Ok(());
}
let needs_new_offer_idx: u16 =
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than having to convert at the call sites, would it make sense for the internal APIs to use/return the invoice slot number as a u16 where possible?

});
},
None => return Err(()),
let slot_needs_new_offer = self.offers.get(cache_slot as usize) == Some(&None)
Copy link
Contributor

Choose a reason for hiding this comment

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

These option-in-option semantics get a bit hard to follow tbh. IMO it would be much more readable if we encapsulated self.offers into a dedicated helper object encapsulating the Vec and exposing 'proper' internal API methods that never leak the actual index usize.

Such a wrapper type could also more clearly assert the invariants, i.e., that we can never get more than u16::MAX/MAX_CACHED_OFFERS_TARGET entries?

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

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.

3 participants