99
1010//! Message handling for async payments.
1111
12- use crate :: blinded_path:: message:: AsyncPaymentsContext ;
12+ use crate :: blinded_path:: message:: { AsyncPaymentsContext , BlindedMessagePath } ;
1313use crate :: io;
1414use crate :: ln:: msgs:: DecodeError ;
15+ use crate :: offers:: static_invoice:: StaticInvoice ;
1516use crate :: onion_message:: messenger:: { MessageSendInstructions , Responder , ResponseInstruction } ;
1617use crate :: onion_message:: packet:: OnionMessageContents ;
1718use crate :: prelude:: * ;
1819use crate :: util:: ser:: { Readable , ReadableArgs , Writeable , Writer } ;
1920
21+ use core:: time:: Duration ;
22+
2023// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
24+ const OFFER_PATHS_REQ_TLV_TYPE : u64 = 65538 ;
25+ const OFFER_PATHS_TLV_TYPE : u64 = 65540 ;
26+ const SERVE_INVOICE_TLV_TYPE : u64 = 65542 ;
27+ const INVOICE_PERSISTED_TLV_TYPE : u64 = 65544 ;
2128const HELD_HTLC_AVAILABLE_TLV_TYPE : u64 = 72 ;
2229const RELEASE_HELD_HTLC_TLV_TYPE : u64 = 74 ;
2330
2431/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
2532///
2633/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
2734pub trait AsyncPaymentsMessageHandler {
35+ /// Handle an [`OfferPathsRequest`] message. If we are a static invoice server and the message was
36+ /// sent over paths that we previously provided to an async recipient via
37+ /// [`UserConfig::paths_to_static_invoice_server`], an [`OfferPaths`] message should be returned.
38+ ///
39+ /// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
40+ fn handle_offer_paths_request (
41+ & self , message : OfferPathsRequest , context : AsyncPaymentsContext ,
42+ responder : Option < Responder > ,
43+ ) -> Option < ( OfferPaths , ResponseInstruction ) > ;
44+
45+ /// Handle an [`OfferPaths`] message. If this is in response to an [`OfferPathsRequest`] that
46+ /// we previously sent as an async recipient, we should build an [`Offer`] containing the
47+ /// included [`OfferPaths::paths`] and a corresponding [`StaticInvoice`], and reply with
48+ /// [`ServeStaticInvoice`].
49+ ///
50+ /// [`Offer`]: crate::offers::offer::Offer
51+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
52+ fn handle_offer_paths (
53+ & self , message : OfferPaths , context : AsyncPaymentsContext , responder : Option < Responder > ,
54+ ) -> Option < ( ServeStaticInvoice , ResponseInstruction ) > ;
55+
56+ /// Handle a [`ServeStaticInvoice`] message. If this is in response to an [`OfferPaths`] message
57+ /// we previously sent as a static invoice server, a [`StaticInvoicePersisted`] message should be
58+ /// sent once the message is handled.
59+ fn handle_serve_static_invoice (
60+ & self , message : ServeStaticInvoice , context : AsyncPaymentsContext ,
61+ responder : Option < Responder > ,
62+ ) ;
63+
64+ /// Handle a [`StaticInvoicePersisted`] message. If this is in response to a
65+ /// [`ServeStaticInvoice`] message we previously sent as an async recipient, then the offer we
66+ /// generated on receipt of a previous [`OfferPaths`] message is now ready to be used for async
67+ /// payments.
68+ fn handle_static_invoice_persisted (
69+ & self , message : StaticInvoicePersisted , context : AsyncPaymentsContext ,
70+ ) ;
71+
2872 /// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
2973 /// the held funds.
3074 fn handle_held_htlc_available (
@@ -50,6 +94,29 @@ pub trait AsyncPaymentsMessageHandler {
5094/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
5195#[ derive( Clone , Debug ) ]
5296pub enum AsyncPaymentsMessage {
97+ /// A request from an async recipient for [`BlindedMessagePath`]s, sent to a static invoice
98+ /// server.
99+ OfferPathsRequest ( OfferPathsRequest ) ,
100+
101+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
102+ /// static invoice server in response to an [`OfferPathsRequest`].
103+ ///
104+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
105+ OfferPaths ( OfferPaths ) ,
106+
107+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
108+ /// provided in response to [`InvoiceRequest`]s from payers.
109+ ///
110+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
111+ ServeStaticInvoice ( ServeStaticInvoice ) ,
112+
113+ /// Confirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
114+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
115+ /// recipient in response to a [`ServeStaticInvoice`] message.
116+ ///
117+ /// [`Offer`]: crate::offers::offer::Offer
118+ StaticInvoicePersisted ( StaticInvoicePersisted ) ,
119+
53120 /// An HTLC is being held upstream for the often-offline recipient, to be released via
54121 /// [`ReleaseHeldHtlc`].
55122 HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +125,57 @@ pub enum AsyncPaymentsMessage {
58125 ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59126}
60127
128+ /// A request from an async recipient for [`BlindedMessagePath`]s from a static invoice server.
129+ /// These paths will be used in the async recipient's [`Offer::paths`], so payers can request
130+ /// [`StaticInvoice`]s from the static invoice server.
131+ ///
132+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
133+ #[ derive( Clone , Debug ) ]
134+ pub struct OfferPathsRequest { }
135+
136+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
137+ /// static invoice server in response to an [`OfferPathsRequest`].
138+ ///
139+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
140+ #[ derive( Clone , Debug ) ]
141+ pub struct OfferPaths {
142+ /// The paths that should be included in the async recipient's [`Offer::paths`].
143+ ///
144+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
145+ pub paths : Vec < BlindedMessagePath > ,
146+ /// The time as duration since the Unix epoch at which the [`Self::paths`] expire.
147+ pub paths_absolute_expiry : Option < Duration > ,
148+ }
149+
150+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
151+ /// provided in response to [`InvoiceRequest`]s from payers.
152+ ///
153+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
154+ #[ derive( Clone , Debug ) ]
155+ pub struct ServeStaticInvoice {
156+ /// The invoice that should be served by the static invoice server. Once this invoice has been
157+ /// persisted, the [`Responder`] accompanying this message should be used to send
158+ /// [`StaticInvoicePersisted`] to the recipient to confirm that the offer corresponding to the
159+ /// invoice is ready to receive async payments.
160+ pub invoice : StaticInvoice ,
161+ /// If a static invoice server receives an [`InvoiceRequest`] for a [`StaticInvoice`], they should
162+ /// also forward the [`InvoiceRequest`] to the async recipient so they can respond with a fresh
163+ /// [`Bolt12Invoice`] if the recipient is online at the time. Use this path to forward the
164+ /// [`InvoiceRequest`] to the async recipient.
165+ ///
166+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
167+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
168+ pub forward_invoice_request_path : BlindedMessagePath ,
169+ }
170+
171+ /// Confirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
172+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
173+ /// recipient in response to a [`ServeStaticInvoice`] message.
174+ ///
175+ /// [`Offer`]: crate::offers::offer::Offer
176+ #[ derive( Clone , Debug ) ]
177+ pub struct StaticInvoicePersisted { }
178+
61179/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62180/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63181/// will cause the upstream HTLC to be released.
@@ -68,6 +186,34 @@ pub struct HeldHtlcAvailable {}
68186#[ derive( Clone , Debug ) ]
69187pub struct ReleaseHeldHtlc { }
70188
189+ impl OnionMessageContents for OfferPaths {
190+ fn tlv_type ( & self ) -> u64 {
191+ OFFER_PATHS_TLV_TYPE
192+ }
193+ #[ cfg( c_bindings) ]
194+ fn msg_type ( & self ) -> String {
195+ "Offer Paths" . to_string ( )
196+ }
197+ #[ cfg( not( c_bindings) ) ]
198+ fn msg_type ( & self ) -> & ' static str {
199+ "Offer Paths"
200+ }
201+ }
202+
203+ impl OnionMessageContents for ServeStaticInvoice {
204+ fn tlv_type ( & self ) -> u64 {
205+ SERVE_INVOICE_TLV_TYPE
206+ }
207+ #[ cfg( c_bindings) ]
208+ fn msg_type ( & self ) -> String {
209+ "Serve Static Invoice" . to_string ( )
210+ }
211+ #[ cfg( not( c_bindings) ) ]
212+ fn msg_type ( & self ) -> & ' static str {
213+ "Serve Static Invoice"
214+ }
215+ }
216+
71217impl OnionMessageContents for ReleaseHeldHtlc {
72218 fn tlv_type ( & self ) -> u64 {
73219 RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +228,20 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82228 }
83229}
84230
231+ impl_writeable_tlv_based ! ( OfferPathsRequest , { } ) ;
232+
233+ impl_writeable_tlv_based ! ( OfferPaths , {
234+ ( 0 , paths, required_vec) ,
235+ ( 2 , paths_absolute_expiry, option) ,
236+ } ) ;
237+
238+ impl_writeable_tlv_based ! ( ServeStaticInvoice , {
239+ ( 0 , invoice, required) ,
240+ ( 2 , forward_invoice_request_path, required) ,
241+ } ) ;
242+
243+ impl_writeable_tlv_based ! ( StaticInvoicePersisted , { } ) ;
244+
85245impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86246
87247impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +250,12 @@ impl AsyncPaymentsMessage {
90250 /// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91251 pub fn is_known_type ( tlv_type : u64 ) -> bool {
92252 match tlv_type {
93- HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true ,
253+ OFFER_PATHS_REQ_TLV_TYPE
254+ | OFFER_PATHS_TLV_TYPE
255+ | SERVE_INVOICE_TLV_TYPE
256+ | INVOICE_PERSISTED_TLV_TYPE
257+ | HELD_HTLC_AVAILABLE_TLV_TYPE
258+ | RELEASE_HELD_HTLC_TLV_TYPE => true ,
94259 _ => false ,
95260 }
96261 }
@@ -99,20 +264,32 @@ impl AsyncPaymentsMessage {
99264impl OnionMessageContents for AsyncPaymentsMessage {
100265 fn tlv_type ( & self ) -> u64 {
101266 match self {
267+ Self :: OfferPathsRequest ( _) => OFFER_PATHS_REQ_TLV_TYPE ,
268+ Self :: OfferPaths ( msg) => msg. tlv_type ( ) ,
269+ Self :: ServeStaticInvoice ( msg) => msg. tlv_type ( ) ,
270+ Self :: StaticInvoicePersisted ( _) => INVOICE_PERSISTED_TLV_TYPE ,
102271 Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103272 Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104273 }
105274 }
106275 #[ cfg( c_bindings) ]
107276 fn msg_type ( & self ) -> String {
108277 match & self {
278+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" . to_string ( ) ,
279+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
280+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
281+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" . to_string ( ) ,
109282 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110283 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111284 }
112285 }
113286 #[ cfg( not( c_bindings) ) ]
114287 fn msg_type ( & self ) -> & ' static str {
115288 match & self {
289+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" ,
290+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
291+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
292+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" ,
116293 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117294 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118295 }
@@ -122,6 +299,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122299impl Writeable for AsyncPaymentsMessage {
123300 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124301 match self {
302+ Self :: OfferPathsRequest ( message) => message. write ( w) ,
303+ Self :: OfferPaths ( message) => message. write ( w) ,
304+ Self :: ServeStaticInvoice ( message) => message. write ( w) ,
305+ Self :: StaticInvoicePersisted ( message) => message. write ( w) ,
125306 Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126307 Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127308 }
@@ -131,6 +312,10 @@ impl Writeable for AsyncPaymentsMessage {
131312impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132313 fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133314 match tlv_type {
315+ OFFER_PATHS_REQ_TLV_TYPE => Ok ( Self :: OfferPathsRequest ( Readable :: read ( r) ?) ) ,
316+ OFFER_PATHS_TLV_TYPE => Ok ( Self :: OfferPaths ( Readable :: read ( r) ?) ) ,
317+ SERVE_INVOICE_TLV_TYPE => Ok ( Self :: ServeStaticInvoice ( Readable :: read ( r) ?) ) ,
318+ INVOICE_PERSISTED_TLV_TYPE => Ok ( Self :: StaticInvoicePersisted ( Readable :: read ( r) ?) ) ,
134319 HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135320 RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136321 _ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments