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
20
21
// TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4.
22
+ const OFFER_PATHS_REQ_TLV_TYPE : u64 = 65538 ;
23
+ const OFFER_PATHS_TLV_TYPE : u64 = 65540 ;
24
+ const SERVE_INVOICE_TLV_TYPE : u64 = 65542 ;
25
+ const INVOICE_PERSISTED_TLV_TYPE : u64 = 65544 ;
21
26
const HELD_HTLC_AVAILABLE_TLV_TYPE : u64 = 72 ;
22
27
const RELEASE_HELD_HTLC_TLV_TYPE : u64 = 74 ;
23
28
24
29
/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
25
30
///
26
31
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
27
32
pub trait AsyncPaymentsMessageHandler {
33
+ /// Handle an [`OfferPathsRequest`] message. If we are a static invoice server and the message was
34
+ /// sent over paths that we previously provided to an async recipient via
35
+ /// [`UserConfig::paths_to_static_invoice_server`], an [`OfferPaths`] message should be returned.
36
+ ///
37
+ /// [`UserConfig::paths_to_static_invoice_server`]: crate::util::config::UserConfig::paths_to_static_invoice_server
38
+ fn handle_offer_paths_request (
39
+ & self , message : OfferPathsRequest , context : AsyncPaymentsContext ,
40
+ responder : Option < Responder > ,
41
+ ) -> Option < ( OfferPaths , ResponseInstruction ) > ;
42
+
43
+ /// Handle an [`OfferPaths`] message. If this is in response to an [`OfferPathsRequest`] that
44
+ /// we previously sent as an async recipient, we should build an [`Offer`] containing the
45
+ /// included [`OfferPaths::paths`] and a corresponding [`StaticInvoice`], and reply with
46
+ /// [`ServeStaticInvoice`].
47
+ ///
48
+ /// [`Offer`]: crate::offers::offer::Offer
49
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
50
+ fn handle_offer_paths (
51
+ & self , message : OfferPaths , context : AsyncPaymentsContext , responder : Option < Responder > ,
52
+ ) -> Option < ( ServeStaticInvoice , ResponseInstruction ) > ;
53
+
54
+ /// Handle a [`ServeStaticInvoice`] message. If this is in response to an [`OfferPaths`] message
55
+ /// we previously sent as a static invoice server, a [`StaticInvoicePersisted`] message should be
56
+ /// sent once the message is handled.
57
+ fn handle_serve_static_invoice (
58
+ & self , message : ServeStaticInvoice , context : AsyncPaymentsContext ,
59
+ responder : Option < Responder > ,
60
+ ) ;
61
+
62
+ /// Handle a [`StaticInvoicePersisted`] message. If this is in response to a
63
+ /// [`ServeStaticInvoice`] message we previously sent as an async recipient, then the offer we
64
+ /// generated on receipt of a previous [`OfferPaths`] message is now ready to be used for async
65
+ /// payments.
66
+ fn handle_static_invoice_persisted (
67
+ & self , message : StaticInvoicePersisted , context : AsyncPaymentsContext ,
68
+ ) ;
69
+
28
70
/// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
29
71
/// the held funds.
30
72
fn handle_held_htlc_available (
@@ -50,6 +92,29 @@ pub trait AsyncPaymentsMessageHandler {
50
92
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
51
93
#[ derive( Clone , Debug ) ]
52
94
pub enum AsyncPaymentsMessage {
95
+ /// A request from an async recipient for [`BlindedMessagePath`]s, sent to a static invoice
96
+ /// server.
97
+ OfferPathsRequest ( OfferPathsRequest ) ,
98
+
99
+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
100
+ /// static invoice server in response to an [`OfferPathsRequest`].
101
+ ///
102
+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
103
+ OfferPaths ( OfferPaths ) ,
104
+
105
+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
106
+ /// provided in response to [`InvoiceRequest`]s from payers.
107
+ ///
108
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
109
+ ServeStaticInvoice ( ServeStaticInvoice ) ,
110
+
111
+ /// Confirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
112
+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
113
+ /// recipient in response to a [`ServeStaticInvoice`] message.
114
+ ///
115
+ /// [`Offer`]: crate::offers::offer::Offer
116
+ StaticInvoicePersisted ( StaticInvoicePersisted ) ,
117
+
53
118
/// An HTLC is being held upstream for the often-offline recipient, to be released via
54
119
/// [`ReleaseHeldHtlc`].
55
120
HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +123,62 @@ pub enum AsyncPaymentsMessage {
58
123
ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59
124
}
60
125
126
+ /// A request from an async recipient for [`BlindedMessagePath`]s from a static invoice server.
127
+ /// These paths will be used in the async recipient's [`Offer::paths`], so payers can request
128
+ /// [`StaticInvoice`]s from the static invoice server.
129
+ ///
130
+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
131
+ #[ derive( Clone , Debug ) ]
132
+ pub struct OfferPathsRequest { }
133
+
134
+ /// [`BlindedMessagePath`]s to be included in an async recipient's [`Offer::paths`], sent by a
135
+ /// static invoice server in response to an [`OfferPathsRequest`].
136
+ ///
137
+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
138
+ #[ derive( Clone , Debug ) ]
139
+ pub struct OfferPaths {
140
+ /// The paths that should be included in the async recipient's [`Offer::paths`].
141
+ ///
142
+ /// [`Offer::paths`]: crate::offers::offer::Offer::paths
143
+ pub paths : Vec < BlindedMessagePath > ,
144
+ /// The time as seconds since the Unix epoch at which the [`Self::paths`] expire.
145
+ pub paths_absolute_expiry : Option < u64 > ,
146
+ }
147
+
148
+ /// A request from an async recipient to a static invoice server that a [`StaticInvoice`] be
149
+ /// provided in response to [`InvoiceRequest`]s from payers.
150
+ ///
151
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
152
+ #[ derive( Clone , Debug ) ]
153
+ pub struct ServeStaticInvoice {
154
+ /// The invoice that should be served by the static invoice server. Once this invoice has been
155
+ /// persisted, the [`Responder`] accompanying this message should be used to send
156
+ /// [`StaticInvoicePersisted`] to the recipient to confirm that the offer corresponding to the
157
+ /// invoice is ready to receive async payments.
158
+ pub invoice : StaticInvoice ,
159
+ /// If a static invoice server receives an [`InvoiceRequest`] for a [`StaticInvoice`], they should
160
+ /// also forward the [`InvoiceRequest`] to the async recipient so they can respond with a fresh
161
+ /// [`Bolt12Invoice`] if the recipient is online at the time. Use this path to forward the
162
+ /// [`InvoiceRequest`] to the async recipient.
163
+ ///
164
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
165
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
166
+ pub forward_invoice_request_path : BlindedMessagePath ,
167
+ /// The "slot" in the static invoice server's database that this invoice should go into. This
168
+ /// allows recipients to replace a specific invoice that is stored by the server, which is useful
169
+ /// for limiting the number of invoices stored by the server while also keeping all the invoices
170
+ /// persisted with the server fresh.
171
+ pub invoice_slot : u8 ,
172
+ }
173
+
174
+ /// Confirmation from a static invoice server that a [`StaticInvoice`] was persisted and the
175
+ /// corresponding [`Offer`] is ready to be used to receive async payments. Sent to an async
176
+ /// recipient in response to a [`ServeStaticInvoice`] message.
177
+ ///
178
+ /// [`Offer`]: crate::offers::offer::Offer
179
+ #[ derive( Clone , Debug ) ]
180
+ pub struct StaticInvoicePersisted { }
181
+
61
182
/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62
183
/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63
184
/// will cause the upstream HTLC to be released.
@@ -68,6 +189,34 @@ pub struct HeldHtlcAvailable {}
68
189
#[ derive( Clone , Debug ) ]
69
190
pub struct ReleaseHeldHtlc { }
70
191
192
+ impl OnionMessageContents for OfferPaths {
193
+ fn tlv_type ( & self ) -> u64 {
194
+ OFFER_PATHS_TLV_TYPE
195
+ }
196
+ #[ cfg( c_bindings) ]
197
+ fn msg_type ( & self ) -> String {
198
+ "Offer Paths" . to_string ( )
199
+ }
200
+ #[ cfg( not( c_bindings) ) ]
201
+ fn msg_type ( & self ) -> & ' static str {
202
+ "Offer Paths"
203
+ }
204
+ }
205
+
206
+ impl OnionMessageContents for ServeStaticInvoice {
207
+ fn tlv_type ( & self ) -> u64 {
208
+ SERVE_INVOICE_TLV_TYPE
209
+ }
210
+ #[ cfg( c_bindings) ]
211
+ fn msg_type ( & self ) -> String {
212
+ "Serve Static Invoice" . to_string ( )
213
+ }
214
+ #[ cfg( not( c_bindings) ) ]
215
+ fn msg_type ( & self ) -> & ' static str {
216
+ "Serve Static Invoice"
217
+ }
218
+ }
219
+
71
220
impl OnionMessageContents for ReleaseHeldHtlc {
72
221
fn tlv_type ( & self ) -> u64 {
73
222
RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +231,21 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82
231
}
83
232
}
84
233
234
+ impl_writeable_tlv_based ! ( OfferPathsRequest , { } ) ;
235
+
236
+ impl_writeable_tlv_based ! ( OfferPaths , {
237
+ ( 0 , paths, required_vec) ,
238
+ ( 2 , paths_absolute_expiry, option) ,
239
+ } ) ;
240
+
241
+ impl_writeable_tlv_based ! ( ServeStaticInvoice , {
242
+ ( 0 , invoice, required) ,
243
+ ( 2 , forward_invoice_request_path, required) ,
244
+ ( 4 , invoice_slot, required) ,
245
+ } ) ;
246
+
247
+ impl_writeable_tlv_based ! ( StaticInvoicePersisted , { } ) ;
248
+
85
249
impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86
250
87
251
impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +254,12 @@ impl AsyncPaymentsMessage {
90
254
/// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91
255
pub fn is_known_type ( tlv_type : u64 ) -> bool {
92
256
match tlv_type {
93
- HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true ,
257
+ OFFER_PATHS_REQ_TLV_TYPE
258
+ | OFFER_PATHS_TLV_TYPE
259
+ | SERVE_INVOICE_TLV_TYPE
260
+ | INVOICE_PERSISTED_TLV_TYPE
261
+ | HELD_HTLC_AVAILABLE_TLV_TYPE
262
+ | RELEASE_HELD_HTLC_TLV_TYPE => true ,
94
263
_ => false ,
95
264
}
96
265
}
@@ -99,20 +268,32 @@ impl AsyncPaymentsMessage {
99
268
impl OnionMessageContents for AsyncPaymentsMessage {
100
269
fn tlv_type ( & self ) -> u64 {
101
270
match self {
271
+ Self :: OfferPathsRequest ( _) => OFFER_PATHS_REQ_TLV_TYPE ,
272
+ Self :: OfferPaths ( msg) => msg. tlv_type ( ) ,
273
+ Self :: ServeStaticInvoice ( msg) => msg. tlv_type ( ) ,
274
+ Self :: StaticInvoicePersisted ( _) => INVOICE_PERSISTED_TLV_TYPE ,
102
275
Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103
276
Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104
277
}
105
278
}
106
279
#[ cfg( c_bindings) ]
107
280
fn msg_type ( & self ) -> String {
108
281
match & self {
282
+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" . to_string ( ) ,
283
+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
284
+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
285
+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" . to_string ( ) ,
109
286
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110
287
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111
288
}
112
289
}
113
290
#[ cfg( not( c_bindings) ) ]
114
291
fn msg_type ( & self ) -> & ' static str {
115
292
match & self {
293
+ Self :: OfferPathsRequest ( _) => "Offer Paths Request" ,
294
+ Self :: OfferPaths ( msg) => msg. msg_type ( ) ,
295
+ Self :: ServeStaticInvoice ( msg) => msg. msg_type ( ) ,
296
+ Self :: StaticInvoicePersisted ( _) => "Static Invoice Persisted" ,
116
297
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117
298
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118
299
}
@@ -122,6 +303,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122
303
impl Writeable for AsyncPaymentsMessage {
123
304
fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124
305
match self {
306
+ Self :: OfferPathsRequest ( message) => message. write ( w) ,
307
+ Self :: OfferPaths ( message) => message. write ( w) ,
308
+ Self :: ServeStaticInvoice ( message) => message. write ( w) ,
309
+ Self :: StaticInvoicePersisted ( message) => message. write ( w) ,
125
310
Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126
311
Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127
312
}
@@ -131,6 +316,10 @@ impl Writeable for AsyncPaymentsMessage {
131
316
impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132
317
fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133
318
match tlv_type {
319
+ OFFER_PATHS_REQ_TLV_TYPE => Ok ( Self :: OfferPathsRequest ( Readable :: read ( r) ?) ) ,
320
+ OFFER_PATHS_TLV_TYPE => Ok ( Self :: OfferPaths ( Readable :: read ( r) ?) ) ,
321
+ SERVE_INVOICE_TLV_TYPE => Ok ( Self :: ServeStaticInvoice ( Readable :: read ( r) ?) ) ,
322
+ INVOICE_PERSISTED_TLV_TYPE => Ok ( Self :: StaticInvoicePersisted ( Readable :: read ( r) ?) ) ,
134
323
HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135
324
RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136
325
_ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments