9
9
10
10
//! Message handling for async payments.
11
11
12
- use crate :: blinded_path:: message:: AsyncPaymentsContext ;
12
+ use crate :: blinded_path:: message:: { AsyncPaymentsContext , BlindedMessagePath } ;
13
13
use crate :: io;
14
14
use crate :: ln:: msgs:: DecodeError ;
15
+ use crate :: offers:: static_invoice:: StaticInvoice ;
15
16
use crate :: onion_message:: messenger:: { MessageSendInstructions , Responder , ResponseInstruction } ;
16
17
use crate :: onion_message:: packet:: OnionMessageContents ;
17
18
use crate :: prelude:: * ;
18
19
use crate :: util:: ser:: { Readable , ReadableArgs , Writeable , Writer } ;
19
20
21
+ use core:: time:: Duration ;
22
+
20
23
// 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 ;
21
28
const HELD_HTLC_AVAILABLE_TLV_TYPE : u64 = 72 ;
22
29
const RELEASE_HELD_HTLC_TLV_TYPE : u64 = 74 ;
23
30
24
31
/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
25
32
///
26
33
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
27
34
pub 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
+
28
72
/// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
29
73
/// the held funds.
30
74
fn handle_held_htlc_available (
@@ -50,6 +94,29 @@ pub trait AsyncPaymentsMessageHandler {
50
94
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
51
95
#[ derive( Clone , Debug ) ]
52
96
pub 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
+
53
120
/// An HTLC is being held upstream for the often-offline recipient, to be released via
54
121
/// [`ReleaseHeldHtlc`].
55
122
HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +125,57 @@ pub enum AsyncPaymentsMessage {
58
125
ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59
126
}
60
127
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
+
61
179
/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62
180
/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63
181
/// will cause the upstream HTLC to be released.
@@ -68,6 +186,34 @@ pub struct HeldHtlcAvailable {}
68
186
#[ derive( Clone , Debug ) ]
69
187
pub struct ReleaseHeldHtlc { }
70
188
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
+
71
217
impl OnionMessageContents for ReleaseHeldHtlc {
72
218
fn tlv_type ( & self ) -> u64 {
73
219
RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +228,20 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82
228
}
83
229
}
84
230
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
+
85
245
impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86
246
87
247
impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +250,12 @@ impl AsyncPaymentsMessage {
90
250
/// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91
251
pub fn is_known_type ( tlv_type : u64 ) -> bool {
92
252
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 ,
94
259
_ => false ,
95
260
}
96
261
}
@@ -99,20 +264,32 @@ impl AsyncPaymentsMessage {
99
264
impl OnionMessageContents for AsyncPaymentsMessage {
100
265
fn tlv_type ( & self ) -> u64 {
101
266
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 ,
102
271
Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103
272
Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104
273
}
105
274
}
106
275
#[ cfg( c_bindings) ]
107
276
fn msg_type ( & self ) -> String {
108
277
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 ( ) ,
109
282
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110
283
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111
284
}
112
285
}
113
286
#[ cfg( not( c_bindings) ) ]
114
287
fn msg_type ( & self ) -> & ' static str {
115
288
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" ,
116
293
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117
294
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118
295
}
@@ -122,6 +299,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122
299
impl Writeable for AsyncPaymentsMessage {
123
300
fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124
301
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) ,
125
306
Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126
307
Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127
308
}
@@ -131,6 +312,10 @@ impl Writeable for AsyncPaymentsMessage {
131
312
impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132
313
fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133
314
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) ?) ) ,
134
319
HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135
320
RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136
321
_ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments