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 we are a static invoice server and the message was
37+ /// sent over paths that we previously provided to an async recipient via
38+ /// [`UserConfig::paths_to_static_invoice_server`], an [`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 as a static invoice server, a [`StaticInvoicePersisted`] message should be
59+ /// sent once the message is 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,29 @@ pub trait AsyncPaymentsMessageHandler {
5095/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
5196#[ derive( Clone , Debug ) ]
5297pub enum AsyncPaymentsMessage {
98+ /// A request from an async recipient for [`BlindedMessagePath`]s, sent to a static invoice
99+ /// server.
100+ OfferPathsRequest ( OfferPathsRequest ) ,
101+
102+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
103+ /// static invoice server in response to an [`OfferPathsRequest`].
104+ ///
105+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
106+ OfferPaths ( OfferPaths ) ,
107+
108+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
109+ /// provided in response to [`InvoiceRequest`]s from payers.
110+ ///
111+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
112+ ServeStaticInvoice ( ServeStaticInvoice ) ,
113+
114+ /// Confirmirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
115+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
116+ /// recipient in response to a [`ServeStaticInvoice`] message.
117+ ///
118+ /// [`Offer`]: crate::offers::offer::Offer
119+ StaticInvoicePersisted ( StaticInvoicePersisted ) ,
120+
53121 /// An HTLC is being held upstream for the often-offline recipient, to be released via
54122 /// [`ReleaseHeldHtlc`].
55123 HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +126,51 @@ pub enum AsyncPaymentsMessage {
58126 ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59127}
60128
129+ /// A request from an async recipient for [`BlindedMessagePath`]s from a static invoice server.
130+ /// These paths will be used in the async recipient's [`Offer::paths`], so payers can request
131+ /// [`StaticInvoice`]s from the static invoice server.
132+ ///
133+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
134+ #[ derive( Clone , Debug ) ]
135+ pub struct OfferPathsRequest { }
136+
137+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
138+ /// static invoice server in response to an [`OfferPathsRequest`].
139+ ///
140+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
141+ #[ derive( Clone , Debug ) ]
142+ pub struct OfferPaths {
143+ /// The paths that should be included in the async recipient's [`Offer::paths`].
144+ ///
145+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
146+ pub paths : Vec < BlindedMessagePath > ,
147+ /// The time as duration since the Unix epoch at which the [`Self::paths`] expire.
148+ pub paths_absolute_expiry : Option < Duration > ,
149+ }
150+
151+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
152+ /// provided in response to [`InvoiceRequest`]s from payers.
153+ ///
154+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
155+ #[ derive( Clone , Debug ) ]
156+ pub struct ServeStaticInvoice {
157+ /// The invoice that should be served by the static invoice server. Once this invoice has been
158+ /// persisted, the [`Responder`] accompanying this message should be used to send
159+ /// [`StaticInvoicePersisted`] to the recipient to confirm that the offer corresponding to the
160+ /// invoice is ready to receive async payments.
161+ pub invoice : StaticInvoice ,
162+ // TODO: include blinded paths to forward the invreq to the async recipient
163+ // pub invoice_request_paths: Vec<BlindedMessagePath>,
164+ }
165+
166+ /// Confirmirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
167+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
168+ /// recipient in response to a [`ServeStaticInvoice`] message.
169+ ///
170+ /// [`Offer`]: crate::offers::offer::Offer
171+ #[ derive( Clone , Debug ) ]
172+ pub struct StaticInvoicePersisted { }
173+
61174/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62175/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63176/// will cause the upstream HTLC to be released.
@@ -68,6 +181,34 @@ pub struct HeldHtlcAvailable {}
68181#[ derive( Clone , Debug ) ]
69182pub struct ReleaseHeldHtlc { }
70183
184+ impl OnionMessageContents for OfferPaths {
185+ fn tlv_type ( & self ) -> u64 {
186+ OFFER_PATHS_TLV_TYPE
187+ }
188+ #[ cfg( c_bindings) ]
189+ fn msg_type ( & self ) -> String {
190+ "Offer Paths" . to_string ( )
191+ }
192+ #[ cfg( not( c_bindings) ) ]
193+ fn msg_type ( & self ) -> & ' static str {
194+ "Offer Paths"
195+ }
196+ }
197+
198+ impl OnionMessageContents for ServeStaticInvoice {
199+ fn tlv_type ( & self ) -> u64 {
200+ SERVE_INVOICE_TLV_TYPE
201+ }
202+ #[ cfg( c_bindings) ]
203+ fn msg_type ( & self ) -> String {
204+ "Serve Static Invoice" . to_string ( )
205+ }
206+ #[ cfg( not( c_bindings) ) ]
207+ fn msg_type ( & self ) -> & ' static str {
208+ "Serve Static Invoice"
209+ }
210+ }
211+
71212impl OnionMessageContents for ReleaseHeldHtlc {
72213 fn tlv_type ( & self ) -> u64 {
73214 RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +223,19 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82223 }
83224}
84225
226+ impl_writeable_tlv_based ! ( OfferPathsRequest , { } ) ;
227+
228+ impl_writeable_tlv_based ! ( OfferPaths , {
229+ ( 0 , paths, required_vec) ,
230+ ( 2 , paths_absolute_expiry, option) ,
231+ } ) ;
232+
233+ impl_writeable_tlv_based ! ( ServeStaticInvoice , {
234+ ( 0 , invoice, required) ,
235+ } ) ;
236+
237+ impl_writeable_tlv_based ! ( StaticInvoicePersisted , { } ) ;
238+
85239impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86240
87241impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +244,12 @@ impl AsyncPaymentsMessage {
90244 /// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91245 pub fn is_known_type ( tlv_type : u64 ) -> bool {
92246 match tlv_type {
93- HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true ,
247+ OFFER_PATHS_REQ_TLV_TYPE
248+ | OFFER_PATHS_TLV_TYPE
249+ | SERVE_INVOICE_TLV_TYPE
250+ | INVOICE_PERSISTED_TLV_TYPE
251+ | HELD_HTLC_AVAILABLE_TLV_TYPE
252+ | RELEASE_HELD_HTLC_TLV_TYPE => true ,
94253 _ => false ,
95254 }
96255 }
@@ -99,20 +258,32 @@ impl AsyncPaymentsMessage {
99258impl OnionMessageContents for AsyncPaymentsMessage {
100259 fn tlv_type ( & self ) -> u64 {
101260 match self {
261+ Self :: OfferPathsRequest ( _) => OFFER_PATHS_REQ_TLV_TYPE ,
262+ Self :: OfferPaths ( msg) => msg. tlv_type ( ) ,
263+ Self :: ServeStaticInvoice ( msg) => msg. tlv_type ( ) ,
264+ Self :: StaticInvoicePersisted ( _) => INVOICE_PERSISTED_TLV_TYPE ,
102265 Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103266 Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104267 }
105268 }
106269 #[ cfg( c_bindings) ]
107270 fn msg_type ( & self ) -> String {
108271 match & self {
272+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" . to_string ( ) ,
273+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
274+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
275+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" . to_string ( ) ,
109276 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110277 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111278 }
112279 }
113280 #[ cfg( not( c_bindings) ) ]
114281 fn msg_type ( & self ) -> & ' static str {
115282 match & self {
283+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" ,
284+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
285+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
286+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" ,
116287 Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117288 Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118289 }
@@ -122,6 +293,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122293impl Writeable for AsyncPaymentsMessage {
123294 fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124295 match self {
296+ Self :: OfferPathsRequest ( message) => message. write ( w) ,
297+ Self :: OfferPaths ( message) => message. write ( w) ,
298+ Self :: ServeStaticInvoice ( message) => message. write ( w) ,
299+ Self :: StaticInvoicePersisted ( message) => message. write ( w) ,
125300 Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126301 Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127302 }
@@ -131,6 +306,10 @@ impl Writeable for AsyncPaymentsMessage {
131306impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132307 fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133308 match tlv_type {
309+ OFFER_PATHS_REQ_TLV_TYPE => Ok ( Self :: OfferPathsRequest ( Readable :: read ( r) ?) ) ,
310+ OFFER_PATHS_TLV_TYPE => Ok ( Self :: OfferPaths ( Readable :: read ( r) ?) ) ,
311+ SERVE_INVOICE_TLV_TYPE => Ok ( Self :: ServeStaticInvoice ( Readable :: read ( r) ?) ) ,
312+ INVOICE_PERSISTED_TLV_TYPE => Ok ( Self :: StaticInvoicePersisted ( Readable :: read ( r) ?) ) ,
134313 HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135314 RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136315 _ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments