Skip to content

Commit 1f4bd17

Browse files
Merge pull request #4009 from valentinewallace/2025-08-simplify-static-inv-server
Remove `invoice_id` from static invoice server protocol
2 parents f889222 + e159ae4 commit 1f4bd17

File tree

7 files changed

+171
-154
lines changed

7 files changed

+171
-154
lines changed

lightning/src/blinded_path/message.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ pub enum OffersContext {
353353
StaticInvoiceRequested {
354354
/// An identifier for the async recipient for whom we as a static invoice server are serving
355355
/// [`StaticInvoice`]s. Used paired with the
356-
/// [`OffersContext::StaticInvoiceRequested::invoice_id`] when looking up a corresponding
356+
/// [`OffersContext::StaticInvoiceRequested::invoice_slot`] when looking up a corresponding
357357
/// [`StaticInvoice`] to return to the payer if the recipient is offline. This id was previously
358358
/// provided via [`AsyncPaymentsContext::ServeStaticInvoice::recipient_id`].
359359
///
@@ -364,15 +364,15 @@ pub enum OffersContext {
364364
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
365365
recipient_id: Vec<u8>,
366366

367-
/// A random unique identifier for a specific [`StaticInvoice`] that the recipient previously
367+
/// The slot number for a specific [`StaticInvoice`] that the recipient previously
368368
/// requested be served on their behalf. Useful when paired with the
369369
/// [`OffersContext::StaticInvoiceRequested::recipient_id`] to pull that specific invoice from
370370
/// the database when payers send an [`InvoiceRequest`]. This id was previously
371-
/// provided via [`AsyncPaymentsContext::ServeStaticInvoice::invoice_id`].
371+
/// provided via [`AsyncPaymentsContext::ServeStaticInvoice::invoice_slot`].
372372
///
373373
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
374374
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
375-
invoice_id: u128,
375+
invoice_slot: u16,
376376

377377
/// The time as duration since the Unix epoch at which this path expires and messages sent over
378378
/// it should be ignored.
@@ -448,6 +448,14 @@ pub enum AsyncPaymentsContext {
448448
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
449449
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
450450
OfferPaths {
451+
/// The "slot" in the static invoice server's database that the invoice corresponding to these
452+
/// offer paths should go into, originally set by us in [`OfferPathsRequest::invoice_slot`]. This
453+
/// value allows us as the recipient to replace a specific invoice that is stored by the server,
454+
/// which is useful for limiting the number of invoices stored by the server while also keeping
455+
/// all the invoices persisted with the server fresh.
456+
///
457+
/// [`OfferPathsRequest::invoice_slot`]: crate::onion_message::async_payments::OfferPathsRequest::invoice_slot
458+
invoice_slot: u16,
451459
/// The time as duration since the Unix epoch at which this path expires and messages sent over
452460
/// it should be ignored.
453461
///
@@ -466,7 +474,7 @@ pub enum AsyncPaymentsContext {
466474
/// An identifier for the async recipient that is requesting that a [`StaticInvoice`] be served
467475
/// on their behalf.
468476
///
469-
/// Useful when surfaced alongside the below `invoice_id` when payers send an
477+
/// Useful when surfaced alongside the below `invoice_slot` when payers send an
470478
/// [`InvoiceRequest`], to pull the specific static invoice from the database.
471479
///
472480
/// Also useful to rate limit the invoices being persisted on behalf of a particular recipient.
@@ -477,15 +485,15 @@ pub enum AsyncPaymentsContext {
477485
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
478486
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
479487
recipient_id: Vec<u8>,
480-
/// A random identifier for the specific [`StaticInvoice`] that the recipient is requesting be
488+
/// The slot number for the specific [`StaticInvoice`] that the recipient is requesting be
481489
/// served on their behalf. Useful when surfaced alongside the above `recipient_id` when payers
482490
/// send an [`InvoiceRequest`], to pull the specific static invoice from the database. This id
483491
/// will be provided back to us as the static invoice server via
484-
/// [`OffersContext::StaticInvoiceRequested::invoice_id`].
492+
/// [`OffersContext::StaticInvoiceRequested::invoice_slot`].
485493
///
486494
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
487495
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
488-
invoice_id: u128,
496+
invoice_slot: u16,
489497
/// The time as duration since the Unix epoch at which this path expires and messages sent over
490498
/// it should be ignored.
491499
///
@@ -559,7 +567,7 @@ impl_writeable_tlv_based_enum!(OffersContext,
559567
},
560568
(3, StaticInvoiceRequested) => {
561569
(0, recipient_id, required),
562-
(2, invoice_id, required),
570+
(2, invoice_slot, required),
563571
(4, path_absolute_expiry, required),
564572
},
565573
);
@@ -573,6 +581,7 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
573581
},
574582
(2, OfferPaths) => {
575583
(0, path_absolute_expiry, required),
584+
(2, invoice_slot, required),
576585
},
577586
(3, StaticInvoicePersisted) => {
578587
(0, offer_id, required),
@@ -584,7 +593,7 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
584593
},
585594
(5, ServeStaticInvoice) => {
586595
(0, recipient_id, required),
587-
(2, invoice_id, required),
596+
(2, invoice_slot, required),
588597
(4, path_absolute_expiry, required),
589598
},
590599
);

lightning/src/events/mod.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,18 +1661,11 @@ pub enum Event {
16611661
/// [`ChannelManager::blinded_paths_for_async_recipient`].
16621662
///
16631663
/// When an [`Event::StaticInvoiceRequested`] comes in for the invoice, this id will be surfaced
1664-
/// and can be used alongside the `invoice_id` to retrieve the invoice from the database.
1664+
/// and can be used alongside the `invoice_slot` to retrieve the invoice from the database.
16651665
///
16661666
///[`ChannelManager::blinded_paths_for_async_recipient`]: crate::ln::channelmanager::ChannelManager::blinded_paths_for_async_recipient
16671667
recipient_id: Vec<u8>,
1668-
/// A random identifier for the invoice. When an [`Event::StaticInvoiceRequested`] comes in for
1669-
/// the invoice, this id will be surfaced and can be used alongside the `recipient_id` to
1670-
/// retrieve the invoice from the database.
1671-
///
1672-
/// Note that this id will remain the same for all invoice updates corresponding to a particular
1673-
/// offer that the recipient has cached.
1674-
invoice_id: u128,
1675-
/// Once the [`StaticInvoice`], `invoice_slot` and `invoice_id` are persisted,
1668+
/// Once the [`StaticInvoice`] and `invoice_slot` are persisted,
16761669
/// [`ChannelManager::static_invoice_persisted`] should be called with this responder to confirm
16771670
/// to the recipient that their [`Offer`] is ready to be used for async payments.
16781671
///
@@ -1688,7 +1681,7 @@ pub enum Event {
16881681
/// them via [`ChannelManager::set_paths_to_static_invoice_server`].
16891682
///
16901683
/// If we previously persisted a [`StaticInvoice`] from an [`Event::PersistStaticInvoice`] that
1691-
/// matches the below `recipient_id` and `invoice_id`, that invoice should be retrieved now
1684+
/// matches the below `recipient_id` and `invoice_slot`, that invoice should be retrieved now
16921685
/// and forwarded to the payer via [`ChannelManager::send_static_invoice`].
16931686
///
16941687
/// [`ChannelManager::blinded_paths_for_async_recipient`]: crate::ln::channelmanager::ChannelManager::blinded_paths_for_async_recipient
@@ -1697,13 +1690,13 @@ pub enum Event {
16971690
/// [`ChannelManager::send_static_invoice`]: crate::ln::channelmanager::ChannelManager::send_static_invoice
16981691
StaticInvoiceRequested {
16991692
/// An identifier for the recipient previously surfaced in
1700-
/// [`Event::PersistStaticInvoice::recipient_id`]. Useful when paired with the `invoice_id` to
1693+
/// [`Event::PersistStaticInvoice::recipient_id`]. Useful when paired with the `invoice_slot` to
17011694
/// retrieve the [`StaticInvoice`] requested by the payer.
17021695
recipient_id: Vec<u8>,
1703-
/// A random identifier for the invoice being requested, previously surfaced in
1704-
/// [`Event::PersistStaticInvoice::invoice_id`]. Useful when paired with the `recipient_id` to
1696+
/// The slot number for the invoice being requested, previously surfaced in
1697+
/// [`Event::PersistStaticInvoice::invoice_slot`]. Useful when paired with the `recipient_id` to
17051698
/// retrieve the [`StaticInvoice`] requested by the payer.
1706-
invoice_id: u128,
1699+
invoice_slot: u16,
17071700
/// The path over which the [`StaticInvoice`] will be sent to the payer, which should be
17081701
/// provided to [`ChannelManager::send_static_invoice`] along with the invoice.
17091702
///

lightning/src/ln/async_payments_tests.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ use core::time::Duration;
6666
struct StaticInvoiceServerFlowResult {
6767
invoice: StaticInvoice,
6868
invoice_slot: u16,
69-
invoice_id: u128,
7069

7170
// Returning messages that were sent along the way allows us to test handling duplicate messages.
7271
offer_paths_request: msgs::OnionMessage,
@@ -148,16 +147,15 @@ fn pass_static_invoice_server_messages(
148147
// that the static invoice should be persisted.
149148
let mut events = server.node.get_and_clear_pending_events();
150149
assert_eq!(events.len(), 1);
151-
let (invoice, invoice_slot, invoice_id, ack_path) = match events.pop().unwrap() {
150+
let (invoice, invoice_slot, ack_path) = match events.pop().unwrap() {
152151
Event::PersistStaticInvoice {
153152
invoice,
154153
invoice_persisted_path,
155154
recipient_id: ev_id,
156155
invoice_slot,
157-
invoice_id,
158156
} => {
159157
assert_eq!(recipient_id, ev_id);
160-
(invoice, invoice_slot, invoice_id, invoice_persisted_path)
158+
(invoice, invoice_slot, invoice_persisted_path)
161159
},
162160
_ => panic!(),
163161
};
@@ -183,7 +181,6 @@ fn pass_static_invoice_server_messages(
183181
static_invoice_persisted_message: invoice_persisted_om,
184182
invoice,
185183
invoice_slot,
186-
invoice_id,
187184
}
188185
}
189186

@@ -209,7 +206,7 @@ fn pass_async_payments_oms(
209206
let mut events = always_online_recipient_counterparty.node.get_and_clear_pending_events();
210207
assert_eq!(events.len(), 1);
211208
let reply_path = match events.pop().unwrap() {
212-
Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_id: _, reply_path } => {
209+
Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_slot: _, reply_path } => {
213210
assert_eq!(recipient_id, ev_id);
214211
reply_path
215212
},
@@ -573,7 +570,7 @@ fn ignore_unexpected_static_invoice() {
573570
let mut events = nodes[1].node.get_and_clear_pending_events();
574571
assert_eq!(events.len(), 1);
575572
let reply_path = match events.pop().unwrap() {
576-
Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_id: _, reply_path } => {
573+
Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_slot: _, reply_path } => {
577574
assert_eq!(recipient_id, ev_id);
578575
reply_path
579576
},
@@ -1626,13 +1623,13 @@ fn limit_serve_static_invoice_requests() {
16261623

16271624
// Build the target number of offers interactively with the static invoice server.
16281625
let mut offer_paths_req = None;
1629-
let mut invoice_ids = new_hash_set();
1626+
let mut invoice_slots = new_hash_set();
16301627
for expected_inv_slot in 0..TEST_MAX_CACHED_OFFERS_TARGET {
16311628
let flow_res = pass_static_invoice_server_messages(server, recipient, recipient_id.clone());
16321629
assert_eq!(flow_res.invoice_slot, expected_inv_slot as u16);
16331630

16341631
offer_paths_req = Some(flow_res.offer_paths_request);
1635-
invoice_ids.insert(flow_res.invoice_id);
1632+
invoice_slots.insert(flow_res.invoice_slot);
16361633

16371634
// Trigger a cache refresh
16381635
recipient.node.timer_tick_occurred();
@@ -1641,8 +1638,8 @@ fn limit_serve_static_invoice_requests() {
16411638
recipient.node.flow.test_get_async_receive_offers().len(),
16421639
TEST_MAX_CACHED_OFFERS_TARGET
16431640
);
1644-
// Check that all invoice ids are unique.
1645-
assert_eq!(invoice_ids.len(), TEST_MAX_CACHED_OFFERS_TARGET);
1641+
// Check that all invoice slot numbers are unique.
1642+
assert_eq!(invoice_slots.len(), TEST_MAX_CACHED_OFFERS_TARGET);
16461643

16471644
// Force allowing more offer paths request attempts so we can check that the recipient will not
16481645
// attempt to build any further offers.
@@ -1822,16 +1819,15 @@ fn refresh_static_invoices_for_used_offers() {
18221819
Event::PersistStaticInvoice {
18231820
invoice,
18241821
invoice_slot,
1825-
invoice_id,
18261822
invoice_persisted_path,
18271823
recipient_id: ev_id,
18281824
} => {
18291825
assert_ne!(original_invoice, invoice);
18301826
assert_eq!(recipient_id, ev_id);
18311827
assert_eq!(invoice_slot, flow_res.invoice_slot);
1832-
// When we update the invoice corresponding to a specific offer, the invoice_id stays the
1828+
// When we update the invoice corresponding to a specific offer, the invoice_slot stays the
18331829
// same.
1834-
assert_eq!(invoice_id, flow_res.invoice_id);
1830+
assert_eq!(invoice_slot, flow_res.invoice_slot);
18351831
(invoice, invoice_persisted_path)
18361832
},
18371833
_ => panic!(),

lightning/src/ln/channelmanager.rs

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14388,11 +14388,9 @@ where
1438814388

1438914389
let invoice_request = match self.flow.verify_invoice_request(invoice_request, context) {
1439014390
Ok(InvreqResponseInstructions::SendInvoice(invoice_request)) => invoice_request,
14391-
Ok(InvreqResponseInstructions::SendStaticInvoice {
14392-
recipient_id: _recipient_id, invoice_id: _invoice_id
14393-
}) => {
14391+
Ok(InvreqResponseInstructions::SendStaticInvoice { recipient_id, invoice_slot }) => {
1439414392
self.pending_events.lock().unwrap().push_back((Event::StaticInvoiceRequested {
14395-
recipient_id: _recipient_id, invoice_id: _invoice_id, reply_path: responder
14393+
recipient_id, invoice_slot, reply_path: responder
1439614394
}, None));
1439714395

1439814396
return None
@@ -14514,29 +14512,28 @@ where
1451414512
L::Target: Logger,
1451514513
{
1451614514
fn handle_offer_paths_request(
14517-
&self, _message: OfferPathsRequest, _context: AsyncPaymentsContext,
14518-
_responder: Option<Responder>,
14515+
&self, message: OfferPathsRequest, context: AsyncPaymentsContext,
14516+
responder: Option<Responder>,
1451914517
) -> Option<(OfferPaths, ResponseInstruction)> {
1452014518
let peers = self.get_peers_for_blinded_path();
14521-
let entropy = &*self.entropy_source;
1452214519
let (message, reply_path_context) =
14523-
match self.flow.handle_offer_paths_request(_context, peers, entropy) {
14520+
match self.flow.handle_offer_paths_request(&message, context, peers) {
1452414521
Some(msg) => msg,
1452514522
None => return None,
1452614523
};
14527-
_responder.map(|resp| (message, resp.respond_with_reply_path(reply_path_context)))
14524+
responder.map(|resp| (message, resp.respond_with_reply_path(reply_path_context)))
1452814525
}
1452914526

1453014527
fn handle_offer_paths(
14531-
&self, _message: OfferPaths, _context: AsyncPaymentsContext, _responder: Option<Responder>,
14528+
&self, message: OfferPaths, context: AsyncPaymentsContext, responder: Option<Responder>,
1453214529
) -> Option<(ServeStaticInvoice, ResponseInstruction)> {
14533-
let responder = match _responder {
14530+
let responder = match responder {
1453414531
Some(responder) => responder,
1453514532
None => return None,
1453614533
};
1453714534
let (serve_static_invoice, reply_context) = match self.flow.handle_offer_paths(
14538-
_message,
14539-
_context,
14535+
message,
14536+
context,
1454014537
responder.clone(),
1454114538
self.get_peers_for_blinded_path(),
1454214539
self.list_usable_channels(),
@@ -14555,52 +14552,51 @@ where
1455514552
}
1455614553

1455714554
fn handle_serve_static_invoice(
14558-
&self, _message: ServeStaticInvoice, _context: AsyncPaymentsContext,
14559-
_responder: Option<Responder>,
14555+
&self, message: ServeStaticInvoice, context: AsyncPaymentsContext,
14556+
responder: Option<Responder>,
1456014557
) {
14561-
let responder = match _responder {
14558+
let responder = match responder {
1456214559
Some(resp) => resp,
1456314560
None => return,
1456414561
};
1456514562

14566-
let (recipient_id, invoice_id) =
14567-
match self.flow.verify_serve_static_invoice_message(&_message, _context) {
14568-
Ok((recipient_id, inv_id)) => (recipient_id, inv_id),
14563+
let (recipient_id, invoice_slot) =
14564+
match self.flow.verify_serve_static_invoice_message(&message, context) {
14565+
Ok((recipient_id, inv_slot)) => (recipient_id, inv_slot),
1456914566
Err(()) => return,
1457014567
};
1457114568

1457214569
let mut pending_events = self.pending_events.lock().unwrap();
1457314570
pending_events.push_back((
1457414571
Event::PersistStaticInvoice {
14575-
invoice: _message.invoice,
14576-
invoice_slot: _message.invoice_slot,
14572+
invoice: message.invoice,
14573+
invoice_slot,
1457714574
recipient_id,
14578-
invoice_id,
1457914575
invoice_persisted_path: responder,
1458014576
},
1458114577
None,
1458214578
));
1458314579
}
1458414580

1458514581
fn handle_static_invoice_persisted(
14586-
&self, _message: StaticInvoicePersisted, _context: AsyncPaymentsContext,
14582+
&self, _message: StaticInvoicePersisted, context: AsyncPaymentsContext,
1458714583
) {
14588-
let should_persist = self.flow.handle_static_invoice_persisted(_context);
14584+
let should_persist = self.flow.handle_static_invoice_persisted(context);
1458914585
if should_persist {
1459014586
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
1459114587
}
1459214588
}
1459314589

1459414590
fn handle_held_htlc_available(
14595-
&self, _message: HeldHtlcAvailable, _context: AsyncPaymentsContext,
14596-
_responder: Option<Responder>,
14591+
&self, _message: HeldHtlcAvailable, context: AsyncPaymentsContext,
14592+
responder: Option<Responder>,
1459714593
) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> {
14598-
self.flow.verify_inbound_async_payment_context(_context).ok()?;
14599-
return _responder.map(|responder| (ReleaseHeldHtlc {}, responder.respond()));
14594+
self.flow.verify_inbound_async_payment_context(context).ok()?;
14595+
return responder.map(|responder| (ReleaseHeldHtlc {}, responder.respond()));
1460014596
}
1460114597

14602-
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) {
14603-
let payment_id = match _context {
14598+
fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, context: AsyncPaymentsContext) {
14599+
let payment_id = match context {
1460414600
AsyncPaymentsContext::OutboundPayment { payment_id } => payment_id,
1460514601
_ => return,
1460614602
};

0 commit comments

Comments
 (0)