Skip to content

Static invoice server #3628

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

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use lightning::ln::peer_handler::IgnoringMessageHandler;
use lightning::ln::script::ShutdownScript;
use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::onion_message::async_payments::{
AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc,
AsyncPaymentsMessageHandler, HeldHtlcAvailable, OfferPaths, OfferPathsRequest, ReleaseHeldHtlc,
ServeStaticInvoice, StaticInvoicePersisted,
};
use lightning::onion_message::messenger::{
CustomOnionMessageHandler, Destination, MessageRouter, MessageSendInstructions,
Expand Down Expand Up @@ -124,6 +125,30 @@ impl OffersMessageHandler for TestOffersMessageHandler {
struct TestAsyncPaymentsMessageHandler {}

impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
fn handle_offer_paths_request(
&self, _message: OfferPathsRequest, _context: AsyncPaymentsContext,
responder: Option<Responder>,
) -> Option<(OfferPaths, ResponseInstruction)> {
let responder = match responder {
Some(resp) => resp,
None => return None,
};
Some((OfferPaths { paths: Vec::new(), paths_absolute_expiry: None }, responder.respond()))
}
fn handle_offer_paths(
&self, _message: OfferPaths, _context: AsyncPaymentsContext, _responder: Option<Responder>,
) -> Option<(ServeStaticInvoice, ResponseInstruction)> {
None
}
fn handle_serve_static_invoice(
&self, _message: ServeStaticInvoice, _context: AsyncPaymentsContext,
_responder: Option<Responder>,
) {
}
fn handle_static_invoice_persisted(
&self, _message: StaticInvoicePersisted, _context: AsyncPaymentsContext,
) {
}
fn handle_held_htlc_available(
&self, _message: HeldHtlcAvailable, _context: AsyncPaymentsContext,
responder: Option<Responder>,
Expand Down
218 changes: 218 additions & 0 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use crate::ln::channelmanager::PaymentId;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
use crate::offers::nonce::Nonce;
use crate::offers::offer::Offer;
use crate::onion_message::messenger::Responder;
use crate::onion_message::packet::ControlTlvs;
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
use crate::sign::{EntropySource, NodeSigner, Recipient};
Expand All @@ -34,6 +36,7 @@ use bitcoin::hashes::sha256::Hash as Sha256;

use core::mem;
use core::ops::Deref;
use core::time::Duration;

/// A blinded path to be used for sending or receiving a message, hiding the identity of the
/// recipient.
Expand Down Expand Up @@ -341,6 +344,47 @@ pub enum OffersContext {
/// [`Offer`]: crate::offers::offer::Offer
nonce: Nonce,
},
/// Context used by a [`BlindedMessagePath`] within the [`Offer`] of an async recipient.
///
/// This variant is received by the static invoice server when handling an [`InvoiceRequest`] on
/// behalf of said async recipient.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
StaticInvoiceRequested {
/// An identifier for the async recipient for whom the static invoice server is serving
/// [`StaticInvoice`]s. Used to look up a corresponding [`StaticInvoice`] to return to the payer
/// if the recipient is offline.
///
/// Also useful for the server to rate limit the number of [`InvoiceRequest`]s it will respond
/// to on recipient's behalf.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
recipient_id_nonce: Nonce,

/// A nonce used for authenticating that a received [`InvoiceRequest`] is valid for a preceding
/// [`OfferPaths`] message sent by the static invoice server.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
nonce: Nonce,

/// Authentication code for the [`InvoiceRequest`].
///
/// Prevents nodes from creating their own blinded path to the static invoice server and causing
/// them to unintentionally hit their database looking for a [`StaticInvoice`] to return.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
hmac: Hmac<Sha256>,

/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
///
/// Useful to timeout async recipients that are no longer supported as clients.
path_absolute_expiry: Duration,
},
/// Context used by a [`BlindedMessagePath`] within a [`Refund`] or as a reply path for an
/// [`InvoiceRequest`].
///
Expand Down Expand Up @@ -404,6 +448,148 @@ pub enum OffersContext {
/// [`AsyncPaymentsMessage`]: crate::onion_message::async_payments::AsyncPaymentsMessage
#[derive(Clone, Debug)]
pub enum AsyncPaymentsContext {
/// Context used by a [`BlindedMessagePath`] that an async recipient is configured with in
/// [`UserConfig::paths_to_static_invoice_server`], provided back to the static invoice server in
/// corresponding [`OfferPathsRequest`]s.
///
/// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
OfferPathsRequest {
/// An identifier for the async recipient that is requesting blinded paths to include in their
/// [`Offer::paths`]. This ID will be surfaced when the async recipient eventually sends a
/// corresponding [`ServeStaticInvoice`] message, and can be used to rate limit the recipient.
///
/// [`Offer::paths`]: crate::offers::offer::Offer::paths
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
recipient_id_nonce: Nonce,
/// Authentication code for the [`OfferPathsRequest`].
///
/// Prevents nodes from requesting offer paths from the static invoice server without having
/// been previously configured with a [`BlindedMessagePath`] that the server generated.
///
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
hmac: Hmac<Sha256>,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
///
/// Useful to timeout async recipients that are no longer supported as clients.
path_absolute_expiry: core::time::Duration,
},
/// Context used by a reply path to an [`OfferPathsRequest`], provided back to us in corresponding
/// [`OfferPaths`] messages.
///
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
OfferPaths {
/// A nonce used for authenticating that an [`OfferPaths`] message is valid for a preceding
/// [`OfferPathsRequest`].
///
/// [`OfferPathsRequest`]: crate::onion_message::async_payments::OfferPathsRequest
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
nonce: Nonce,
/// Authentication code for the [`OfferPaths`] message.
///
/// Prevents nodes from creating their own blinded path to us and causing us to cache an
/// unintended async receive offer.
///
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
hmac: Hmac<Sha256>,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
///
/// Used to time out a static invoice server from providing offer paths if the async recipient
/// is no longer configured to accept paths from them.
path_absolute_expiry: core::time::Duration,
},
/// Context used by a reply path to an [`OfferPaths`] message, provided back to the static invoice
/// server in corresponding [`ServeStaticInvoice`] messages.
///
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
ServeStaticInvoice {
/// An identifier for the async recipient that is requesting that a [`StaticInvoice`] be served
/// on their behalf.
///
/// Useful as a key to retrieve the invoice when payers send an [`InvoiceRequest`] to the static
/// invoice server. Also useful to rate limit the invoices being persisted on behalf of a
/// particular recipient.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Offer::paths`]: crate::offers::offer::Offer::paths
recipient_id_nonce: Nonce,
/// A nonce used for authenticating that a [`ServeStaticInvoice`] message is valid for a preceding
/// [`OfferPaths`] message.
///
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
/// [`OfferPaths`]: crate::onion_message::async_payments::OfferPaths
nonce: Nonce,
/// Authentication code for the [`ServeStaticInvoice`] message.
///
/// Prevents nodes from creating their own blinded path to the static invoice server and causing
/// them to persist an unintended [`StaticInvoice`].
///
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
hmac: Hmac<Sha256>,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
///
/// Useful to timeout async recipients that are no longer supported as clients.
path_absolute_expiry: core::time::Duration,
},
/// Context used by a reply path to a [`ServeStaticInvoice`] message, provided back to us in
/// corresponding [`StaticInvoicePersisted`] messages.
///
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
/// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted
StaticInvoicePersisted {
/// The offer corresponding to the [`StaticInvoice`] that has been persisted. This invoice is
/// now ready to be provided by the static invoice server in response to [`InvoiceRequest`]s.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
offer: Offer,
/// A [`Nonce`] useful for updating the [`StaticInvoice`] that corresponds to the
/// [`AsyncPaymentsContext::StaticInvoicePersisted::offer`], since the offer may be much longer
/// lived than the invoice.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
offer_nonce: Nonce,
/// Useful to determine how far an offer is into its lifespan, to decide whether the offer is
/// expiring soon and we should start building a new one.
offer_created_at: core::time::Duration,
/// A [`Responder`] useful for updating the [`StaticInvoice`] that corresponds to the
/// [`AsyncPaymentsContext::StaticInvoicePersisted::offer`], since the offer may be much longer
/// lived than the invoice.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
update_static_invoice_path: Responder,
/// The time as duration since the Unix epoch at which the [`StaticInvoice`] expires, used to track
/// when we need to generate and persist a new invoice with the static invoice server.
///
/// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice
static_invoice_absolute_expiry: core::time::Duration,
/// A nonce used for authenticating that a [`StaticInvoicePersisted`] message is valid for a
/// preceding [`ServeStaticInvoice`] message.
///
/// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted
/// [`ServeStaticInvoice`]: crate::onion_message::async_payments::ServeStaticInvoice
nonce: Nonce,
/// Authentication code for the [`StaticInvoicePersisted`] message.
///
/// Prevents nodes from creating their own blinded path to us and causing us to cache an
/// unintended async receive offer.
///
/// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted
hmac: Hmac<Sha256>,
/// The time as duration since the Unix epoch at which this path expires and messages sent over
/// it should be ignored.
///
/// Prevents a static invoice server from causing an async recipient to cache an old offer if
/// the recipient is no longer configured to use that server.
path_absolute_expiry: core::time::Duration,
},
/// Context contained within the reply [`BlindedMessagePath`] we put in outbound
/// [`HeldHtlcAvailable`] messages, provided back to us in corresponding [`ReleaseHeldHtlc`]
/// messages.
Expand Down Expand Up @@ -473,6 +659,12 @@ impl_writeable_tlv_based_enum!(OffersContext,
(1, nonce, required),
(2, hmac, required)
},
(3, StaticInvoiceRequested) => {
(0, recipient_id_nonce, required),
(2, nonce, required),
(4, hmac, required),
(6, path_absolute_expiry, required),
},
);

impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
Expand All @@ -486,6 +678,32 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext,
(2, hmac, required),
(4, path_absolute_expiry, required),
},
(2, OfferPaths) => {
(0, nonce, required),
(2, hmac, required),
(4, path_absolute_expiry, required),
},
(3, StaticInvoicePersisted) => {
(0, offer, required),
(2, offer_nonce, required),
(4, offer_created_at, required),
(6, update_static_invoice_path, required),
(8, static_invoice_absolute_expiry, required),
(10, nonce, required),
(12, hmac, required),
(14, path_absolute_expiry, required),
},
(4, OfferPathsRequest) => {
(0, recipient_id_nonce, required),
(2, hmac, required),
(4, path_absolute_expiry, required),
},
(5, ServeStaticInvoice) => {
(0, recipient_id_nonce, required),
(2, nonce, required),
(4, hmac, required),
(6, path_absolute_expiry, required),
},
);

/// Contains a simple nonce for use in a blinded path's context.
Expand Down
Loading
Loading