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
+ // 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 ;
21
29
const HELD_HTLC_AVAILABLE_TLV_TYPE : u64 = 72 ;
22
30
const RELEASE_HELD_HTLC_TLV_TYPE : u64 = 74 ;
23
31
24
32
/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
25
33
///
26
34
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
27
35
pub 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
+
28
73
/// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
29
74
/// the held funds.
30
75
fn handle_held_htlc_available (
@@ -50,6 +95,29 @@ pub trait AsyncPaymentsMessageHandler {
50
95
/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
51
96
#[ derive( Clone , Debug ) ]
52
97
pub 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
+
53
121
/// An HTLC is being held upstream for the often-offline recipient, to be released via
54
122
/// [`ReleaseHeldHtlc`].
55
123
HeldHtlcAvailable ( HeldHtlcAvailable ) ,
@@ -58,6 +126,51 @@ pub enum AsyncPaymentsMessage {
58
126
ReleaseHeldHtlc ( ReleaseHeldHtlc ) ,
59
127
}
60
128
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
+
61
174
/// An HTLC destined for the recipient of this message is being held upstream. The reply path
62
175
/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
63
176
/// will cause the upstream HTLC to be released.
@@ -68,6 +181,34 @@ pub struct HeldHtlcAvailable {}
68
181
#[ derive( Clone , Debug ) ]
69
182
pub struct ReleaseHeldHtlc { }
70
183
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
+
71
212
impl OnionMessageContents for ReleaseHeldHtlc {
72
213
fn tlv_type ( & self ) -> u64 {
73
214
RELEASE_HELD_HTLC_TLV_TYPE
@@ -82,6 +223,19 @@ impl OnionMessageContents for ReleaseHeldHtlc {
82
223
}
83
224
}
84
225
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
+
85
239
impl_writeable_tlv_based ! ( HeldHtlcAvailable , { } ) ;
86
240
87
241
impl_writeable_tlv_based ! ( ReleaseHeldHtlc , { } ) ;
@@ -90,7 +244,12 @@ impl AsyncPaymentsMessage {
90
244
/// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
91
245
pub fn is_known_type ( tlv_type : u64 ) -> bool {
92
246
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 ,
94
253
_ => false ,
95
254
}
96
255
}
@@ -99,20 +258,32 @@ impl AsyncPaymentsMessage {
99
258
impl OnionMessageContents for AsyncPaymentsMessage {
100
259
fn tlv_type ( & self ) -> u64 {
101
260
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 ,
102
265
Self :: HeldHtlcAvailable ( _) => HELD_HTLC_AVAILABLE_TLV_TYPE ,
103
266
Self :: ReleaseHeldHtlc ( msg) => msg. tlv_type ( ) ,
104
267
}
105
268
}
106
269
#[ cfg( c_bindings) ]
107
270
fn msg_type ( & self ) -> String {
108
271
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 ( ) ,
109
276
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" . to_string ( ) ,
110
277
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
111
278
}
112
279
}
113
280
#[ cfg( not( c_bindings) ) ]
114
281
fn msg_type ( & self ) -> & ' static str {
115
282
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" ,
116
287
Self :: HeldHtlcAvailable ( _) => "Held HTLC Available" ,
117
288
Self :: ReleaseHeldHtlc ( msg) => msg. msg_type ( ) ,
118
289
}
@@ -122,6 +293,10 @@ impl OnionMessageContents for AsyncPaymentsMessage {
122
293
impl Writeable for AsyncPaymentsMessage {
123
294
fn write < W : Writer > ( & self , w : & mut W ) -> Result < ( ) , io:: Error > {
124
295
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) ,
125
300
Self :: HeldHtlcAvailable ( message) => message. write ( w) ,
126
301
Self :: ReleaseHeldHtlc ( message) => message. write ( w) ,
127
302
}
@@ -131,6 +306,10 @@ impl Writeable for AsyncPaymentsMessage {
131
306
impl ReadableArgs < u64 > for AsyncPaymentsMessage {
132
307
fn read < R : io:: Read > ( r : & mut R , tlv_type : u64 ) -> Result < Self , DecodeError > {
133
308
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) ?) ) ,
134
313
HELD_HTLC_AVAILABLE_TLV_TYPE => Ok ( Self :: HeldHtlcAvailable ( Readable :: read ( r) ?) ) ,
135
314
RELEASE_HELD_HTLC_TLV_TYPE => Ok ( Self :: ReleaseHeldHtlc ( Readable :: read ( r) ?) ) ,
136
315
_ => Err ( DecodeError :: InvalidValue ) ,
0 commit comments