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+ // TODO: document static invoice server onion message payload types in a bLIP.
25+ const OFFER_PATHS_REQ_TLV_TYPE : u64 = 65538 ;
26+ const OFFER_PATHS_TLV_TYPE : u64 = 65540 ;
27+ const SERVE_INVOICE_TLV_TYPE : u64 = 65542 ;
28+ const INVOICE_PERSISTED_TLV_TYPE : u64 = 65544 ;
2129const HELD_HTLC_AVAILABLE_TLV_TYPE : u64 = 72 ;
2230const RELEASE_HELD_HTLC_TLV_TYPE : u64 = 74 ;
2331
2432/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
2533///
2634/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
2735pub trait AsyncPaymentsMessageHandler {
36+ /// Handle an [`OfferPathsRequest`] message. If the message was sent over paths that we previously
37+ /// provided to an async recipient via [`UserConfig::paths_to_static_invoice_server`], an
38+ /// [`OfferPaths`] message should be returned.
39+ ///
40+ /// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
41+ fn handle_offer_paths_request (
42+ & self , message : OfferPathsRequest , context : AsyncPaymentsContext ,
43+ responder : Option < Responder > ,
44+ ) -> Option < ( OfferPaths , ResponseInstruction ) > ;
45+
46+ /// Handle an [`OfferPaths`] message. If this is in response to an [`OfferPathsRequest`] that
47+ /// we previously sent as an async recipient, we should build an [`Offer`] containing the
48+ /// included [`OfferPaths::paths`] and a corresponding [`StaticInvoice`], and reply with
49+ /// [`ServeStaticInvoice`].
50+ ///
51+ /// [`Offer`]: crate::offers::offer::Offer
52+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
53+ fn handle_offer_paths (
54+ & self , message : OfferPaths , context : AsyncPaymentsContext , responder : Option < Responder > ,
55+ ) -> Option < ( ServeStaticInvoice , ResponseInstruction ) > ;
56+
57+ /// Handle a [`ServeStaticInvoice`] message. If this is in response to an [`OfferPaths`] message
58+ /// we previously sent, a [`StaticInvoicePersisted`] message should be sent once the message is
59+ /// handled.
60+ fn handle_serve_static_invoice (
61+ & self , message : ServeStaticInvoice , context : AsyncPaymentsContext ,
62+ responder : Option < Responder > ,
63+ ) ;
64+
65+ /// Handle a [`StaticInvoicePersisted`] message. If this is in response to a
66+ /// [`ServeStaticInvoice`] message we previously sent as an async recipient, then the offer we
67+ /// generated on receipt of a previous [`OfferPaths`] message is now ready to be used for async
68+ /// payments.
69+ fn handle_static_invoice_persisted (
70+ & self , message : StaticInvoicePersisted , context : AsyncPaymentsContext ,
71+ ) ;
72+
2873 /// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
2974 /// the held funds.
3075 fn handle_held_htlc_available (
@@ -50,6 +95,25 @@ pub trait AsyncPaymentsMessageHandler {
5095/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
5196#[ derive( Clone , Debug ) ]
5297pub enum AsyncPaymentsMessage {
98+ /// A request for [`BlindedMessagePath`]s from a static invoice server.
99+ OfferPathsRequest ( OfferPathsRequest ) ,
100+
101+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent in
102+ /// response to an [`OfferPathsRequest`].
103+ ///
104+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
105+ OfferPaths ( OfferPaths ) ,
106+
107+ /// A request to serve a [`StaticInvoice`] on behalf of an async recipient.
108+ ServeStaticInvoice ( ServeStaticInvoice ) ,
109+
110+ /// Confirms that a [`StaticInvoice`] was persisted by a static invoice server and the
111+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent in response to a
112+ /// [`ServeStaticInvoice`] message.
113+ ///
114+ /// [`Offer`]: crate::offers::offer::Offer
115+ StaticInvoicePersisted ( StaticInvoicePersisted ) ,
116+
53117 /// An HTLC is being held upstream for the often-offline recipient, to be released via
54118 /// [`ReleaseHeldHtlc`].
55119 HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +122,51 @@ pub enum AsyncPaymentsMessage {
58122 ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59123}
60124
125+ /// A request for [`BlindedMessagePath`]s from a static invoice server. These paths will be used
126+ /// in the async recipient's [`Offer::paths`], so payers can request [`StaticInvoice`]s from the
127+ /// static invoice server.
128+ ///
129+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
130+ #[ derive( Clone , Debug ) ]
131+ pub struct OfferPathsRequest { }
132+
133+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent in
134+ /// response to an [`OfferPathsRequest`].
135+ ///
136+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
137+ #[ derive( Clone , Debug ) ]
138+ pub struct OfferPaths {
139+ /// The paths that should be included in the async recipient's [`Offer::paths`].
140+ ///
141+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
142+ pub paths : Vec < BlindedMessagePath > ,
143+ /// The time as duration since the Unix epoch at which the [`Self::paths`] expire.
144+ pub paths_absolute_expiry : Option < Duration > ,
145+ }
146+
147+ /// Indicates that a [`StaticInvoice`] should be provided by a static invoice server in response to
148+ /// [`InvoiceRequest`]s from payers behalf of an async recipient.
149+ ///
150+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
151+ #[ derive( Clone , Debug ) ]
152+ pub struct ServeStaticInvoice {
153+ /// The invoice that should be served by the static invoice server. Once this invoice has been
154+ /// persisted, the [`Responder`] accompanying this message should be used to send
155+ /// [`StaticInvoicePersisted`] to the recipient to confirm that the offer corresponding to the
156+ /// invoice is ready to receive async payments.
157+ pub invoice : StaticInvoice ,
158+ // TODO: include blinded paths to forward the invreq to the async recipient
159+ // pub invoice_request_paths: Vec<BlindedMessagePath>,
160+ }
161+
162+ /// Confirms that a [`StaticInvoice`] was persisted by a static invoice server and the
163+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent in response to a
164+ /// [`ServeStaticInvoice`] message.
165+ ///
166+ /// [`Offer`]: crate::offers::offer::Offer
167+ #[ derive( Clone , Debug ) ]
168+ pub struct StaticInvoicePersisted { }
169+
61170/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62171/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63172/// will cause the upstream HTLC to be released.
@@ -68,6 +177,34 @@ pub struct HeldHtlcAvailable {}
68177#[ derive( Clone , Debug ) ]
69178pub struct ReleaseHeldHtlc { }
70179
180+ impl OnionMessageContents for OfferPaths {
181+ fn tlv_type ( & self ) -> u64 {
182+ OFFER_PATHS_TLV_TYPE
183+ }
184+ #[ cfg( c_bindings) ]
185+ fn msg_type ( & self ) -> String {
186+ "Offer Paths" . to_string ( )
187+ }
188+ #[ cfg( not( c_bindings) ) ]
189+ fn msg_type ( & self ) -> & ' static str {
190+ "Offer Paths"
191+ }
192+ }
193+
194+ impl OnionMessageContents for ServeStaticInvoice {
195+ fn tlv_type ( & self ) -> u64 {
196+ SERVE_INVOICE_TLV_TYPE
197+ }
198+ #[ cfg( c_bindings) ]
199+ fn msg_type ( & self ) -> String {
200+ "Serve Static Invoice" . to_string ( )
201+ }
202+ #[ cfg( not( c_bindings) ) ]
203+ fn msg_type ( & self ) -> & ' static str {
204+ "Serve Static Invoice"
205+ }
206+ }
207+
71208impl OnionMessageContents for ReleaseHeldHtlc {
72209 fn tlv_type ( & self ) -> u64 {
73210 RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +219,19 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82219 }
83220}
84221
222+ impl_writeable_tlv_based ! ( OfferPathsRequest , { } ) ;
223+
224+ impl_writeable_tlv_based ! ( OfferPaths , {
225+ ( 0 , paths, required_vec) ,
226+ ( 2 , paths_absolute_expiry, option) ,
227+ } ) ;
228+
229+ impl_writeable_tlv_based ! ( ServeStaticInvoice , {
230+ ( 0 , invoice, required) ,
231+ } ) ;
232+
233+ impl_writeable_tlv_based ! ( StaticInvoicePersisted , { } ) ;
234+
85235impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86236
87237impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +240,12 @@ impl AsyncPaymentsMessage {
90240 /// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91241 pub fn is_known_type ( tlv_type : u64 ) -> bool {
92242 match tlv_type {
93- HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true ,
243+ OFFER_PATHS_REQ_TLV_TYPE
244+ | OFFER_PATHS_TLV_TYPE
245+ | SERVE_INVOICE_TLV_TYPE
246+ | INVOICE_PERSISTED_TLV_TYPE
247+ | HELD_HTLC_AVAILABLE_TLV_TYPE
248+ | RELEASE_HELD_HTLC_TLV_TYPE => true ,
94249 _ => false ,
95250 }
96251 }
@@ -99,20 +254,32 @@ impl AsyncPaymentsMessage {
99254impl OnionMessageContents for AsyncPaymentsMessage {
100255 fn tlv_type ( & self ) -> u64 {
101256 match self {
257+ Self :: OfferPathsRequest ( _) => OFFER_PATHS_REQ_TLV_TYPE ,
258+ Self :: OfferPaths ( msg) => msg. tlv_type ( ) ,
259+ Self :: ServeStaticInvoice ( msg) => msg. tlv_type ( ) ,
260+ Self :: StaticInvoicePersisted ( _) => INVOICE_PERSISTED_TLV_TYPE ,
102261 Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103262 Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104263 }
105264 }
106265 #[ cfg( c_bindings) ]
107266 fn msg_type ( & self ) -> String {
108267 match & self {
268+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" . to_string ( ) ,
269+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
270+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
271+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" . to_string ( ) ,
109272 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110273 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111274 }
112275 }
113276 #[ cfg( not( c_bindings) ) ]
114277 fn msg_type ( & self ) -> & ' static str {
115278 match & self {
279+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" ,
280+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
281+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
282+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" ,
116283 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117284 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118285 }
@@ -122,6 +289,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122289impl Writeable for AsyncPaymentsMessage {
123290 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124291 match self {
292+ Self :: OfferPathsRequest ( message) => message. write ( w) ,
293+ Self :: OfferPaths ( message) => message. write ( w) ,
294+ Self :: ServeStaticInvoice ( message) => message. write ( w) ,
295+ Self :: StaticInvoicePersisted ( message) => message. write ( w) ,
125296 Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126297 Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127298 }
@@ -131,6 +302,10 @@ impl Writeable for AsyncPaymentsMessage {
131302impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132303 fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133304 match tlv_type {
305+ OFFER_PATHS_REQ_TLV_TYPE => Ok ( Self :: OfferPathsRequest ( Readable :: read ( r) ?) ) ,
306+ OFFER_PATHS_TLV_TYPE => Ok ( Self :: OfferPaths ( Readable :: read ( r) ?) ) ,
307+ SERVE_INVOICE_TLV_TYPE => Ok ( Self :: ServeStaticInvoice ( Readable :: read ( r) ?) ) ,
308+ INVOICE_PERSISTED_TLV_TYPE => Ok ( Self :: StaticInvoicePersisted ( Readable :: read ( r) ?) ) ,
134309 HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135310 RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136311 _ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments