5959//! # fn node_id(&self) -> PublicKey { unimplemented!() }
6060//! # fn first_hops(&self) -> Vec<ChannelDetails> { unimplemented!() }
6161//! # 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!() }
6465//! # 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!() }
6768//! # fn retry_payment(
6869//! # &self, route: &Route, payment_id: PaymentId
6970//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
@@ -242,6 +243,18 @@ impl<T: Time> Display for PaymentAttempts<T> {
242243}
243244
244245/// 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
245258pub trait Payer {
246259 /// Returns the payer's node id.
247260 fn node_id ( & self ) -> PublicKey ;
@@ -251,13 +264,14 @@ pub trait Payer {
251264
252265 /// Sends a payment over the Lightning Network using the given [`Route`].
253266 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 > ;
256270
257271 /// Sends a spontaneous payment over the Lightning Network using the given [`Route`].
258272 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 > ;
261275
262276 /// Retries a failed payment path for the [`PaymentId`] using the given [`Route`].
263277 fn retry_payment ( & self , route : & Route , payment_id : PaymentId ) -> Result < ( ) , PaymentSendFailure > ;
@@ -346,36 +360,76 @@ where
346360
347361 /// Pays the given [`Invoice`], caching it for later use in case a retry is needed.
348362 ///
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`].
352369 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 > {
353386 if invoice. amount_milli_satoshis ( ) . is_none ( ) {
354387 Err ( PaymentError :: Invoice ( "amount missing" ) )
355388 } else {
356- self . pay_invoice_using_amount ( invoice, None )
389+ self . pay_invoice_using_amount ( invoice, None , payment_id )
357390 }
358391 }
359392
360393 /// Pays the given zero-value [`Invoice`] using the given amount, caching it for later use in
361394 /// case a retry is needed.
362395 ///
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`].
366402 pub fn pay_zero_value_invoice (
367403 & self , invoice : & Invoice , amount_msats : u64
368404 ) -> 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 > {
369423 if invoice. amount_milli_satoshis ( ) . is_some ( ) {
370424 Err ( PaymentError :: Invoice ( "amount unexpected" ) )
371425 } else {
372- self . pay_invoice_using_amount ( invoice, Some ( amount_msats) )
426+ self . pay_invoice_using_amount ( invoice, Some ( amount_msats) , payment_id )
373427 }
374428 }
375429
376430 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 > {
379433 debug_assert ! ( invoice. amount_milli_satoshis( ) . is_some( ) ^ amount_msats. is_some( ) ) ;
380434
381435 let payment_hash = PaymentHash ( invoice. payment_hash ( ) . clone ( ) . into_inner ( ) ) ;
@@ -398,7 +452,7 @@ where
398452 } ;
399453
400454 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 )
402456 } ;
403457
404458 self . pay_internal ( & route_params, payment_hash, send_payment)
@@ -408,13 +462,41 @@ where
408462 /// Pays `pubkey` an amount using the hash of the given preimage, caching it for later use in
409463 /// case a retry is needed.
410464 ///
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.
413468 pub fn pay_pubkey (
414469 & self , pubkey : PublicKey , payment_preimage : PaymentPreimage , amount_msats : u64 ,
415470 final_cltv_expiry_delta : u32
416471 ) -> Result < PaymentId , PaymentError > {
417472 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 > {
418500 match self . payment_cache . lock ( ) . unwrap ( ) . entry ( payment_hash) {
419501 hash_map:: Entry :: Occupied ( _) => return Err ( PaymentError :: Invoice ( "payment pending" ) ) ,
420502 hash_map:: Entry :: Vacant ( entry) => entry. insert ( PaymentInfo :: new ( ) ) ,
@@ -427,15 +509,15 @@ where
427509 } ;
428510
429511 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 )
431513 } ;
432514 self . pay_internal ( & route_params, payment_hash, send_payment)
433515 . map_err ( |e| { self . payment_cache . lock ( ) . unwrap ( ) . remove ( & payment_hash) ; e } )
434516 }
435517
436- fn pay_internal < F : FnOnce ( & Route ) -> Result < PaymentId , PaymentSendFailure > + Copy > (
518+ fn pay_internal < F : FnOnce ( & Route ) -> Result < ( ) , PaymentSendFailure > + Copy > (
437519 & self , params : & RouteParameters , payment_hash : PaymentHash , send_payment : F ,
438- ) -> Result < PaymentId , PaymentError > {
520+ ) -> Result < ( ) , PaymentError > {
439521 #[ cfg( feature = "std" ) ] {
440522 if has_expired ( params) {
441523 log_trace ! ( self . logger, "Invoice expired prior to send for payment {}" , log_bytes!( payment_hash. 0 ) ) ;
@@ -452,11 +534,11 @@ where
452534 ) . map_err ( |e| PaymentError :: Routing ( e) ) ?;
453535
454536 match send_payment ( & route) {
455- Ok ( payment_id ) => {
537+ Ok ( ( ) ) => {
456538 for path in route. paths {
457539 self . process_path_inflight_htlcs ( payment_hash, path) ;
458540 }
459- Ok ( payment_id )
541+ Ok ( ( ) )
460542 } ,
461543 Err ( e) => match e {
462544 PaymentSendFailure :: ParameterError ( _) => Err ( e) ,
@@ -491,13 +573,13 @@ where
491573 // consider the payment sent, so return `Ok()` here, ignoring any retry
492574 // errors.
493575 let _ = self . retry_payment ( payment_id, payment_hash, & retry_data) ;
494- Ok ( payment_id )
576+ Ok ( ( ) )
495577 } else {
496578 // This may happen if we send a payment and some paths fail, but
497579 // only due to a temporary monitor failure or the like, implying
498580 // they're really in-flight, but we haven't sent the initial
499581 // HTLC-Add messages yet.
500- Ok ( payment_id )
582+ Ok ( ( ) )
501583 }
502584 } ,
503585 } ,
@@ -2056,13 +2138,13 @@ mod tests {
20562138 self
20572139 }
20582140
2059- fn check_attempts ( & self ) -> Result < PaymentId , PaymentSendFailure > {
2141+ fn check_attempts ( & self ) -> Result < ( ) , PaymentSendFailure > {
20602142 let mut attempts = self . attempts . borrow_mut ( ) ;
20612143 * attempts += 1 ;
20622144
20632145 match self . failing_on_attempt . borrow_mut ( ) . remove ( & * attempts) {
20642146 Some ( failure) => Err ( failure) ,
2065- None => Ok ( PaymentId ( [ 1 ; 32 ] ) ) ,
2147+ None => Ok ( ( ) )
20662148 }
20672149 }
20682150
@@ -2100,15 +2182,15 @@ mod tests {
21002182
21012183 fn send_payment (
21022184 & 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 > {
21052187 self . check_value_msats ( Amount :: ForInvoice ( route. get_total_amount ( ) ) ) ;
21062188 self . check_attempts ( )
21072189 }
21082190
21092191 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 > {
21122194 self . check_value_msats ( Amount :: Spontaneous ( route. get_total_amount ( ) ) ) ;
21132195 self . check_attempts ( )
21142196 }
@@ -2117,7 +2199,7 @@ mod tests {
21172199 & self , route : & Route , _payment_id : PaymentId
21182200 ) -> Result < ( ) , PaymentSendFailure > {
21192201 self . check_value_msats ( Amount :: OnRetry ( route. get_total_amount ( ) ) ) ;
2120- self . check_attempts ( ) . map ( |_| ( ) )
2202+ self . check_attempts ( )
21212203 }
21222204
21232205 fn abandon_payment ( & self , _payment_id : PaymentId ) { }
0 commit comments