Replace StaticInvoiceReq::invoice_id with ::inv_slot
What changed, and why it matters
This commit is a straightforward internal API cleanup in the experimental static-invoice-server protocol. It replaces a randomly-generated 128-bit invoice_id with a simpler 16-bit invoice_slot when looking up stored invoices. There is no security fix here—just making the database lookup key consistent and easier to use.
No security action required. Treat as normal refactoring/API evolution of the async/static invoice feature.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch renames StaticInvoiceReq::invoice_id to ::inv_slot and changes the type from u128 to u16 across OffersContext, Event::StaticInvoiceRequested, InvreqResponseInstructions, and ChannelManager wiring. AsyncPaymentsContext still carries both invoice_id and invoice_slot fields, but the server-side lookup now uses only invoice_slot. Serialization TLV field index is preserved (2). This is a protocol/API simplification, not a vulnerability remediation.
Changed components
lightning/src/blinded_path/message.rslightning/src/events/mod.rslightning/src/ln/async_payments_tests.rslightning/src/ln/channelmanager.rslightning/src/offers/flow.rsInspect captured patch +24 / −24
diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs
index 64cc57a..4e58f66 100644
--- a/lightning/src/blinded_path/message.rs
+++ b/lightning/src/blinded_path/message.rs
@@ -353,7 +353,7 @@ pub enum OffersContext {
StaticInvoiceRequested {
/// An identifier for the async recipient for whom we as a static invoice server are serving
/// [`StaticInvoice`]s. Used paired with the
- /// [`OffersContext::StaticInvoiceRequested::invoice_id`] when looking up a corresponding
+ /// [`OffersContext::StaticInvoiceRequested::invoice_slot`] when looking up a corresponding
/// [`StaticInvoice`] to return to the payer if the recipient is offline. This id was previously
/// provided via [`AsyncPaymentsContext::ServeStaticInvoice::recipient_id`].
///
@@ -364,15 +364,15 @@ pub enum OffersContext {
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
recipient_id: Vec<u8>,
- /// A random unique identifier for a specific [`StaticInvoice`] that the recipient previously
+ /// The slot number for a specific [`StaticInvoice`] that the recipient previously
/// requested be served on their behalf. Useful when paired with the
/// [`OffersContext::StaticInvoiceRequested::recipient_id`] to pull that specific invoice from
/// the database when payers send an [`InvoiceRequest`]. This id was previously
- /// provided via [`AsyncPaymentsContext::ServeStaticInvoice::invoice_id`].
+ /// provided via [`AsyncPaymentsContext::ServeStaticInvoice::invoice_slot`].
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
- invoice_id: u128,
+ invoice_slot: u16,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
@@ -487,16 +487,16 @@ pub enum AsyncPaymentsContext {
recipient_id: Vec<u8>,
/// A random identifier for the specific [`StaticInvoice`] that the recipient is requesting be
/// served on their behalf. Useful when surfaced alongside the above `recipient_id` when payers
- /// send an [`InvoiceRequest`], to pull the specific static invoice from the database. This id
- /// will be provided back to us as the static invoice server via
- /// [`OffersContext::StaticInvoiceRequested::invoice_id`].
+ /// send an [`InvoiceRequest`], to pull the specific static invoice from the database.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
invoice_id: u128,
/// The slot number for the specific [`StaticInvoice`] that the recipient is requesting be
/// served on their behalf. Useful when surfaced alongside the above `recipient_id` when payers
- /// send an [`InvoiceRequest`], to pull the specific static invoice from the database.
+ /// send an [`InvoiceRequest`], to pull the specific static invoice from the database. This id
+ /// will be provided back to us as the static invoice server via
+ /// [`OffersContext::StaticInvoiceRequested::invoice_slot`].
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
@@ -574,7 +574,7 @@ impl_writeable_tlv_based_enum!(OffersContext,
},
(3, StaticInvoiceRequested) => {
(0, recipient_id, required),
- (2, invoice_id, required),
+ (2, invoice_slot, required),
(4, path_absolute_expiry, required),
},
);
diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index 6642498..eaf9e0d 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -1688,7 +1688,7 @@ pub enum Event {
/// them via [`ChannelManager::set_paths_to_static_invoice_server`].
///
/// If we previously persisted a [`StaticInvoice`] from an [`Event::PersistStaticInvoice`] that
- /// matches the below `recipient_id` and `invoice_id`, that invoice should be retrieved now
+ /// matches the below `recipient_id` and `invoice_slot`, that invoice should be retrieved now
/// and forwarded to the payer via [`ChannelManager::send_static_invoice`].
///
/// [`ChannelManager::blinded_paths_for_async_recipient`]: crate::ln::channelmanager::ChannelManager::blinded_paths_for_async_recipient
@@ -1697,13 +1697,13 @@ pub enum Event {
/// [`ChannelManager::send_static_invoice`]: crate::ln::channelmanager::ChannelManager::send_static_invoice
StaticInvoiceRequested {
/// An identifier for the recipient previously surfaced in
- /// [`Event::PersistStaticInvoice::recipient_id`]. Useful when paired with the `invoice_id` to
+ /// [`Event::PersistStaticInvoice::recipient_id`]. Useful when paired with the `invoice_slot` to
/// retrieve the [`StaticInvoice`] requested by the payer.
recipient_id: Vec<u8>,
- /// A random identifier for the invoice being requested, previously surfaced in
- /// [`Event::PersistStaticInvoice::invoice_id`]. Useful when paired with the `recipient_id` to
+ /// The slot number for the invoice being requested, previously surfaced in
+ /// [`Event::PersistStaticInvoice::invoice_slot`]. Useful when paired with the `recipient_id` to
/// retrieve the [`StaticInvoice`] requested by the payer.
- invoice_id: u128,
+ invoice_slot: u16,
/// The path over which the [`StaticInvoice`] will be sent to the payer, which should be
/// provided to [`ChannelManager::send_static_invoice`] along with the invoice.
///
diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs
index d868eee..2903476 100644
--- a/lightning/src/ln/async_payments_tests.rs
+++ b/lightning/src/ln/async_payments_tests.rs
@@ -209,7 +209,7 @@ fn pass_async_payments_oms(
let mut events = always_online_recipient_counterparty.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
let reply_path = match events.pop().unwrap() {
- Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_id: _, reply_path } => {
+ Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_slot: _, reply_path } => {
assert_eq!(recipient_id, ev_id);
reply_path
},
@@ -573,7 +573,7 @@ fn ignore_unexpected_static_invoice() {
let mut events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
let reply_path = match events.pop().unwrap() {
- Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_id: _, reply_path } => {
+ Event::StaticInvoiceRequested { recipient_id: ev_id, invoice_slot: _, reply_path } => {
assert_eq!(recipient_id, ev_id);
reply_path
},
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 857da8b..abd0716 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -14317,9 +14317,9 @@ where
let invoice_request = match self.flow.verify_invoice_request(invoice_request, context) {
Ok(InvreqResponseInstructions::SendInvoice(invoice_request)) => invoice_request,
- Ok(InvreqResponseInstructions::SendStaticInvoice { recipient_id, invoice_id }) => {
+ Ok(InvreqResponseInstructions::SendStaticInvoice { recipient_id, invoice_slot }) => {
self.pending_events.lock().unwrap().push_back((Event::StaticInvoiceRequested {
- recipient_id, invoice_id, reply_path: responder
+ recipient_id, invoice_slot, reply_path: responder
}, None));
return None
diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs
index 60615f7..519e459 100644
--- a/lightning/src/offers/flow.rs
+++ b/lightning/src/offers/flow.rs
@@ -395,7 +395,7 @@ pub enum InvreqResponseInstructions {
/// the invoice request since it is now verified.
SendInvoice(VerifiedInvoiceRequest),
/// We are a static invoice server and should respond to this invoice request by retrieving the
- /// [`StaticInvoice`] corresponding to the `recipient_id` and `invoice_id` and calling
+ /// [`StaticInvoice`] corresponding to the `recipient_id` and `invoice_slot` and calling
/// `OffersMessageFlow::enqueue_static_invoice`.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
@@ -404,8 +404,8 @@ pub enum InvreqResponseInstructions {
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
recipient_id: Vec<u8>,
- /// An identifier for the specific invoice being requested by the payer.
- invoice_id: u128,
+ /// The slot number for the specific invoice being requested by the payer.
+ invoice_slot: u16,
},
}
@@ -435,7 +435,7 @@ where
Some(OffersContext::InvoiceRequest { nonce }) => Some(nonce),
Some(OffersContext::StaticInvoiceRequested {
recipient_id,
- invoice_id,
+ invoice_slot,
path_absolute_expiry,
}) => {
if path_absolute_expiry < self.duration_since_epoch() {
@@ -444,7 +444,7 @@ where
return Ok(InvreqResponseInstructions::SendStaticInvoice {
recipient_id,
- invoice_id,
+ invoice_slot,
});
},
_ => return Err(()),
@@ -1401,7 +1401,7 @@ where
let context = MessageContext::Offers(OffersContext::StaticInvoiceRequested {
recipient_id: recipient_id.clone(),
path_absolute_expiry,
- invoice_id,
+ invoice_slot: request.invoice_slot,
});
match self.create_blinded_paths(peers, context) {
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.