59
59
//! # fn node_id(&self) -> PublicKey { unimplemented!() }
60
60
//! # fn first_hops(&self) -> Vec<ChannelDetails> { unimplemented!() }
61
61
//! # fn send_payment(
62
- //! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>
63
- //! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
62
+ //! # &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>,
63
+ //! # payment_id: PaymentId
64
+ //! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
64
65
//! # fn send_spontaneous_payment(
65
- //! # &self, route: &Route, payment_preimage: PaymentPreimage
66
- //! # ) -> Result<PaymentId , PaymentSendFailure> { unimplemented!() }
66
+ //! # &self, route: &Route, payment_preimage: PaymentPreimage, payment_id: PaymentId,
67
+ //! # ) -> Result<() , PaymentSendFailure> { unimplemented!() }
67
68
//! # fn retry_payment(
68
69
//! # &self, route: &Route, payment_id: PaymentId
69
70
//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
@@ -242,6 +243,18 @@ impl<T: Time> Display for PaymentAttempts<T> {
242
243
}
243
244
244
245
/// A trait defining behavior of an [`Invoice`] payer.
246
+ ///
247
+ /// While the behavior of [`InvoicePayer`] provides idempotency of duplicate `send_*payment` calls
248
+ /// with the same [`PaymentHash`], it is up to the `Payer` to provide idempotency across restarts.
249
+ ///
250
+ /// [`ChannelManager`] provides idempotency for duplicate payments with the same [`PaymentId`].
251
+ ///
252
+ /// In order to trivially ensure idempotency for payments, the default `Payer` implementation
253
+ /// reuses the [`PaymentHash`] bytes as the [`PaymentId`]. Custom implementations wishing to
254
+ /// provide payment idempotency with a different idempotency key (i.e. [`PaymentId`]) should map
255
+ /// the [`Invoice`] or spontaneous payment target pubkey to their own idempotency key.
256
+ ///
257
+ /// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
245
258
pub trait Payer {
246
259
/// Returns the payer's node id.
247
260
fn node_id ( & self ) -> PublicKey ;
@@ -251,13 +264,14 @@ pub trait Payer {
251
264
252
265
/// Sends a payment over the Lightning Network using the given [`Route`].
253
266
fn send_payment (
254
- & self , route : & Route , payment_hash : PaymentHash , payment_secret : & Option < PaymentSecret >
255
- ) -> Result < PaymentId , PaymentSendFailure > ;
267
+ & self , route : & Route , payment_hash : PaymentHash , payment_secret : & Option < PaymentSecret > ,
268
+ payment_id : PaymentId
269
+ ) -> Result < ( ) , PaymentSendFailure > ;
256
270
257
271
/// Sends a spontaneous payment over the Lightning Network using the given [`Route`].
258
272
fn send_spontaneous_payment (
259
- & self , route : & Route , payment_preimage : PaymentPreimage
260
- ) -> Result < PaymentId , PaymentSendFailure > ;
273
+ & self , route : & Route , payment_preimage : PaymentPreimage , payment_id : PaymentId
274
+ ) -> Result < ( ) , PaymentSendFailure > ;
261
275
262
276
/// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
263
277
fn retry_payment ( & self , route : & Route , payment_id : PaymentId ) -> Result < ( ) , PaymentSendFailure > ;
@@ -346,36 +360,76 @@ where
346
360
347
361
/// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
348
362
///
349
- /// You should ensure that the `invoice.payment_hash()` is unique and the same payment_hash has
350
- /// never been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so
351
- /// for you.
363
+ /// [`Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long
364
+ /// as the payment is still pending. Once the payment completes or fails, you must ensure that
365
+ /// a second payment with the same [`PaymentHash`] is never sent.
366
+ ///
367
+ /// If you wish to use a different payment idempotency token, see
368
+ /// [`Self::pay_invoice_with_id`].
352
369
pub fn pay_invoice ( & self , invoice : & Invoice ) -> Result < PaymentId , PaymentError > {
370
+ let payment_id = PaymentId ( invoice. payment_hash ( ) . into_inner ( ) ) ;
371
+ self . pay_invoice_with_id ( invoice, payment_id) . map ( |( ) | payment_id)
372
+ }
373
+
374
+ /// Pays the given [`Invoice`] with a custom idempotency key, caching the invoice for later use
375
+ /// in case a retry is needed.
376
+ ///
377
+ /// Note that idempotency is only guaranteed as long as the payment is still pending. Once the
378
+ /// payment completes or fails, no idempotency guarantees are made.
379
+ ///
380
+ /// You should ensure that the [`Invoice::payment_hash`] is unique and the same [`PaymentHash`]
381
+ /// has never been paid before.
382
+ ///
383
+ /// See [`Self::pay_invoice`] for a variant which uses the [`PaymentHash`] for the idempotency
384
+ /// token.
385
+ pub fn pay_invoice_with_id ( & self , invoice : & Invoice , payment_id : PaymentId ) -> Result < ( ) , PaymentError > {
353
386
if invoice. amount_milli_satoshis ( ) . is_none ( ) {
354
387
Err ( PaymentError :: Invoice ( "amount missing" ) )
355
388
} else {
356
- self . pay_invoice_using_amount ( invoice, None )
389
+ self . pay_invoice_using_amount ( invoice, None , payment_id )
357
390
}
358
391
}
359
392
360
393
/// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
361
394
/// case a retry is needed.
362
395
///
363
- /// You should ensure that the `invoice.payment_hash()` is unique and the same payment_hash has
364
- /// never been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so
365
- /// for you.
396
+ /// [`Invoice::payment_hash`] is used as the [`PaymentId`], which ensures idempotency as long
397
+ /// as the payment is still pending. Once the payment completes or fails, you must ensure that
398
+ /// a second payment with the same [`PaymentHash`] is never sent.
399
+ ///
400
+ /// If you wish to use a different payment idempotency token, see
401
+ /// [`Self::pay_zero_value_invoice_with_id`].
366
402
pub fn pay_zero_value_invoice (
367
403
& self , invoice : & Invoice , amount_msats : u64
368
404
) -> Result < PaymentId , PaymentError > {
405
+ let payment_id = PaymentId ( invoice. payment_hash ( ) . into_inner ( ) ) ;
406
+ self . pay_zero_value_invoice_with_id ( invoice, amount_msats, payment_id) . map ( |( ) | payment_id)
407
+ }
408
+
409
+ /// Pays the given zero-value [`Invoice`] using the given amount and custom idempotency key,
410
+ /// caching the invoice for later use in case a retry is needed.
411
+ ///
412
+ /// Note that idempotency is only guaranteed as long as the payment is still pending. Once the
413
+ /// payment completes or fails, no idempotency guarantees are made.
414
+ ///
415
+ /// You should ensure that the [`Invoice::payment_hash`] is unique and the same [`PaymentHash`]
416
+ /// has never been paid before.
417
+ ///
418
+ /// See [`Self::pay_zero_value_invoice`] for a variant which uses the [`PaymentHash`] for the
419
+ /// idempotency token.
420
+ pub fn pay_zero_value_invoice_with_id (
421
+ & self , invoice : & Invoice , amount_msats : u64 , payment_id : PaymentId
422
+ ) -> Result < ( ) , PaymentError > {
369
423
if invoice. amount_milli_satoshis ( ) . is_some ( ) {
370
424
Err ( PaymentError :: Invoice ( "amount unexpected" ) )
371
425
} else {
372
- self . pay_invoice_using_amount ( invoice, Some ( amount_msats) )
426
+ self . pay_invoice_using_amount ( invoice, Some ( amount_msats) , payment_id )
373
427
}
374
428
}
375
429
376
430
fn pay_invoice_using_amount (
377
- & self , invoice : & Invoice , amount_msats : Option < u64 >
378
- ) -> Result < PaymentId , PaymentError > {
431
+ & self , invoice : & Invoice , amount_msats : Option < u64 > , payment_id : PaymentId
432
+ ) -> Result < ( ) , PaymentError > {
379
433
debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
380
434
381
435
let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
@@ -398,7 +452,7 @@ where
398
452
} ;
399
453
400
454
let send_payment = |route : & Route | {
401
- self . payer . send_payment ( route, payment_hash, & payment_secret)
455
+ self . payer . send_payment ( route, payment_hash, & payment_secret, payment_id )
402
456
} ;
403
457
404
458
self . pay_internal ( & route_params, payment_hash, send_payment)
@@ -408,13 +462,41 @@ where
408
462
/// Pays `pubkey` an amount using the hash of the given preimage, caching it for later use in
409
463
/// case a retry is needed.
410
464
///
411
- /// You should ensure that `payment_preimage` is unique and that its `payment_hash` has never
412
- /// been paid before. Because [`InvoicePayer`] is stateless no effort is made to do so for you.
465
+ /// The hash of the [`PaymentPreimage`] is used as the [`PaymentId`], which ensures idempotency
466
+ /// as long as the payment is still pending. Once the payment completes or fails, you must
467
+ /// ensure that a second payment with the same [`PaymentPreimage`] is never sent.
413
468
pub fn pay_pubkey (
414
469
& self , pubkey : PublicKey , payment_preimage : PaymentPreimage , amount_msats : u64 ,
415
470
final_cltv_expiry_delta : u32
416
471
) -> Result < PaymentId , PaymentError > {
417
472
let payment_hash = PaymentHash ( Sha256 :: hash ( & payment_preimage. 0 ) . into_inner ( ) ) ;
473
+ let payment_id = PaymentId ( payment_hash. 0 ) ;
474
+ self . do_pay_pubkey ( pubkey, payment_preimage, payment_hash, payment_id, amount_msats,
475
+ final_cltv_expiry_delta)
476
+ . map ( |( ) | payment_id)
477
+ }
478
+
479
+ /// Pays `pubkey` an amount using the hash of the given preimage and a custom idempotency key,
480
+ /// caching the invoice for later use in case a retry is needed.
481
+ ///
482
+ /// Note that idempotency is only guaranteed as long as the payment is still pending. Once the
483
+ /// payment completes or fails, no idempotency guarantees are made.
484
+ ///
485
+ /// You should ensure that the [`PaymentPreimage`] is unique and the corresponding
486
+ /// [`PaymentHash`] has never been paid before.
487
+ pub fn pay_pubkey_with_id (
488
+ & self , pubkey : PublicKey , payment_preimage : PaymentPreimage , payment_id : PaymentId ,
489
+ amount_msats : u64 , final_cltv_expiry_delta : u32
490
+ ) -> Result < ( ) , PaymentError > {
491
+ let payment_hash = PaymentHash ( Sha256 :: hash ( & payment_preimage. 0 ) . into_inner ( ) ) ;
492
+ self . do_pay_pubkey ( pubkey, payment_preimage, payment_hash, payment_id, amount_msats,
493
+ final_cltv_expiry_delta)
494
+ }
495
+
496
+ fn do_pay_pubkey (
497
+ & self , pubkey : PublicKey , payment_preimage : PaymentPreimage , payment_hash : PaymentHash ,
498
+ payment_id : PaymentId , amount_msats : u64 , final_cltv_expiry_delta : u32
499
+ ) -> Result < ( ) , PaymentError > {
418
500
match self . payment_cache . lock ( ) . unwrap ( ) . entry ( payment_hash) {
419
501
hash_map:: Entry :: Occupied ( _) => return Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
420
502
hash_map:: Entry :: Vacant ( entry) => entry. insert ( PaymentInfo :: new ( ) ) ,
@@ -427,15 +509,15 @@ where
427
509
} ;
428
510
429
511
let send_payment = |route : & Route | {
430
- self . payer . send_spontaneous_payment ( route, payment_preimage)
512
+ self . payer . send_spontaneous_payment ( route, payment_preimage, payment_id )
431
513
} ;
432
514
self . pay_internal ( & route_params, payment_hash, send_payment)
433
515
. map_err ( |e| { self . payment_cache . lock ( ) . unwrap ( ) . remove ( & payment_hash) ; e } )
434
516
}
435
517
436
- fn pay_internal < F : FnOnce ( & Route ) -> Result < PaymentId , PaymentSendFailure > + Copy > (
518
+ fn pay_internal < F : FnOnce ( & Route ) -> Result < ( ) , PaymentSendFailure > + Copy > (
437
519
& self , params : & RouteParameters , payment_hash : PaymentHash , send_payment : F ,
438
- ) -> Result < PaymentId , PaymentError > {
520
+ ) -> Result < ( ) , PaymentError > {
439
521
#[ cfg( feature = "std" ) ] {
440
522
if has_expired ( params) {
441
523
log_trace ! ( self . logger, "Invoice expired prior to send for payment {}" , log_bytes!( payment_hash. 0 ) ) ;
@@ -452,11 +534,11 @@ where
452
534
) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
453
535
454
536
match send_payment ( & route) {
455
- Ok ( payment_id ) => {
537
+ Ok ( ( ) ) => {
456
538
for path in route. paths {
457
539
self . process_path_inflight_htlcs ( payment_hash, path) ;
458
540
}
459
- Ok ( payment_id )
541
+ Ok ( ( ) )
460
542
} ,
461
543
Err ( e) => match e {
462
544
PaymentSendFailure :: ParameterError ( _) => Err ( e) ,
@@ -491,13 +573,13 @@ where
491
573
// consider the payment sent, so return `Ok()` here, ignoring any retry
492
574
// errors.
493
575
let _ = self . retry_payment ( payment_id, payment_hash, & retry_data) ;
494
- Ok ( payment_id )
576
+ Ok ( ( ) )
495
577
} else {
496
578
// This may happen if we send a payment and some paths fail, but
497
579
// only due to a temporary monitor failure or the like, implying
498
580
// they're really in-flight, but we haven't sent the initial
499
581
// HTLC-Add messages yet.
500
- Ok ( payment_id )
582
+ Ok ( ( ) )
501
583
}
502
584
} ,
503
585
} ,
@@ -2056,13 +2138,13 @@ mod tests {
2056
2138
self
2057
2139
}
2058
2140
2059
- fn check_attempts ( & self ) -> Result < PaymentId , PaymentSendFailure > {
2141
+ fn check_attempts ( & self ) -> Result < ( ) , PaymentSendFailure > {
2060
2142
let mut attempts = self . attempts . borrow_mut ( ) ;
2061
2143
* attempts += 1 ;
2062
2144
2063
2145
match self . failing_on_attempt . borrow_mut ( ) . remove ( & * attempts) {
2064
2146
Some ( failure) => Err ( failure) ,
2065
- None => Ok ( PaymentId ( [ 1 ; 32 ] ) ) ,
2147
+ None => Ok ( ( ) )
2066
2148
}
2067
2149
}
2068
2150
@@ -2100,15 +2182,15 @@ mod tests {
2100
2182
2101
2183
fn send_payment (
2102
2184
& self , route : & Route , _payment_hash : PaymentHash ,
2103
- _payment_secret : & Option < PaymentSecret >
2104
- ) -> Result < PaymentId , PaymentSendFailure > {
2185
+ _payment_secret : & Option < PaymentSecret > , _payment_id : PaymentId ,
2186
+ ) -> Result < ( ) , PaymentSendFailure > {
2105
2187
self . check_value_msats ( Amount :: ForInvoice ( route. get_total_amount ( ) ) ) ;
2106
2188
self . check_attempts ( )
2107
2189
}
2108
2190
2109
2191
fn send_spontaneous_payment (
2110
- & self , route : & Route , _payment_preimage : PaymentPreimage ,
2111
- ) -> Result < PaymentId , PaymentSendFailure > {
2192
+ & self , route : & Route , _payment_preimage : PaymentPreimage , _payment_id : PaymentId ,
2193
+ ) -> Result < ( ) , PaymentSendFailure > {
2112
2194
self . check_value_msats ( Amount :: Spontaneous ( route. get_total_amount ( ) ) ) ;
2113
2195
self . check_attempts ( )
2114
2196
}
@@ -2117,7 +2199,7 @@ mod tests {
2117
2199
& self , route : & Route , _payment_id : PaymentId
2118
2200
) -> Result < ( ) , PaymentSendFailure > {
2119
2201
self . check_value_msats ( Amount :: OnRetry ( route. get_total_amount ( ) ) ) ;
2120
- self . check_attempts ( ) . map ( |_| ( ) )
2202
+ self . check_attempts ( )
2121
2203
}
2122
2204
2123
2205
fn abandon_payment ( & self , _payment_id : PaymentId ) { }
0 commit comments