7474//! # &self, route: &Route, payment_id: PaymentId
7575//! # ) -> Result<(), PaymentSendFailure> { unimplemented!() }
7676//! # fn abandon_payment(&self, payment_id: PaymentId) { unimplemented!() }
77+ //! # fn send_probe_payment(
78+ //! # &self, route: &Route
79+ //! # ) -> Result<PaymentId, PaymentSendFailure> { unimplemented!() }
80+ //! # fn payment_is_probe(
81+ //! # &self, payment_hash: PaymentHash, payment_id: PaymentId
82+ //! # ) -> bool { unimplemented!() }
7783//! # }
7884//! #
7985//! # struct FakeRouter {}
8288//! # &self, payer: &PublicKey, params: &RouteParameters, payment_hash: &PaymentHash,
8389//! # first_hops: Option<&[&ChannelDetails]>, scorer: &S
8490//! # ) -> Result<Route, LightningError> { unimplemented!() }
91+ //! #
92+ //! # fn build_route_from_hops(
93+ //! # &self, _payer: &PublicKey, _hops: &[PublicKey], route_params: &RouteParameters
94+ //! # ) -> Result<Route, LightningError> { unimplemented!() }
8595//! # }
8696//! #
8797//! # struct FakeScorer {}
@@ -247,6 +257,12 @@ pub trait Payer {
247257
248258 /// Signals that no further retries for the given payment will occur.
249259 fn abandon_payment ( & self , payment_id : PaymentId ) ;
260+
261+ /// Send a payment probe over the given [`Route`].
262+ fn send_probe_payment ( & self , route : & Route ) -> Result < PaymentId , PaymentSendFailure > ;
263+
264+ /// Returns whether payment with the given [`PaymentId`] and [`PaymentHash`] is a probe.
265+ fn payment_is_probe ( & self , payment_hash : PaymentHash , payment_id : PaymentId ) -> bool ;
250266}
251267
252268/// A trait defining behavior for routing an [`Invoice`] payment.
@@ -256,6 +272,11 @@ pub trait Router<S: Score> {
256272 & self , payer : & PublicKey , route_params : & RouteParameters , payment_hash : & PaymentHash ,
257273 first_hops : Option < & [ & ChannelDetails ] > , scorer : & S
258274 ) -> Result < Route , LightningError > ;
275+
276+ /// Builds a [`Route`] from `payer` along the given path.
277+ fn build_route_from_hops (
278+ & self , payer : & PublicKey , hops : & [ PublicKey ] , params : & RouteParameters
279+ ) -> Result < Route , LightningError > ;
259280}
260281
261282/// Strategies available to retry payment path failures for an [`Invoice`].
@@ -411,6 +432,23 @@ where
411432 . map_err ( |e| { self . payment_cache . lock ( ) . unwrap ( ) . remove ( & payment_hash) ; e } )
412433 }
413434
435+ /// Sends a probe payment along the given path. The resulting payment will not be cached and
436+ /// resulting failures will be handled differently from regular payments.
437+ pub fn send_probe_along_path (
438+ & self , pubkey : PublicKey , hops : & [ PublicKey ] , amount_msats : u64 , final_cltv_expiry_delta : u32
439+ ) -> Result < PaymentId , PaymentError > {
440+ let route_params = RouteParameters {
441+ payment_params : PaymentParameters :: for_keysend ( pubkey) ,
442+ final_value_msat : amount_msats,
443+ final_cltv_expiry_delta,
444+ } ;
445+ let payer = self . payer . node_id ( ) ;
446+ let route = self . router . build_route_from_hops ( & payer, hops, & route_params)
447+ . map_err ( |e| PaymentError :: Routing ( e) ) ?;
448+
449+ self . payer . send_probe_payment ( & route) . map_err ( |e| PaymentError :: Sending ( e) )
450+ }
451+
414452 fn pay_internal < F : FnOnce ( & Route ) -> Result < PaymentId , PaymentSendFailure > + Copy > (
415453 & self , params : & RouteParameters , payment_hash : PaymentHash , send_payment : F ,
416454 ) -> Result < PaymentId , PaymentError > {
@@ -552,6 +590,20 @@ where
552590 } => {
553591 if let Some ( short_channel_id) = short_channel_id {
554592 let path = path. iter ( ) . collect :: < Vec < _ > > ( ) ;
593+ if let Some ( payment_id) = payment_id {
594+ // When the failed payment was a probe, we make sure to not penalize the last
595+ // hop and then drop the event instead of handing it up to the user's event
596+ // handler.
597+ if self . payer . payment_is_probe ( * payment_hash, * payment_id) {
598+ if * rejected_by_dest {
599+ self . scorer . lock ( ) . payment_path_failed ( & path, u64:: max_value ( ) ) ;
600+ } else {
601+ self . scorer . lock ( ) . payment_path_failed ( & path, * short_channel_id) ;
602+ }
603+ return ;
604+ }
605+ }
606+
555607 self . scorer . lock ( ) . payment_path_failed ( & path, * short_channel_id) ;
556608 }
557609
@@ -1402,6 +1454,14 @@ mod tests {
14021454 payment_params : Some ( route_params. payment_params . clone ( ) ) , ..Self :: route_for_value ( route_params. final_value_msat )
14031455 } )
14041456 }
1457+
1458+ fn build_route_from_hops (
1459+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , route_params : & RouteParameters
1460+ ) -> Result < Route , LightningError > {
1461+ Ok ( Route {
1462+ payment_params : Some ( route_params. payment_params . clone ( ) ) , ..Self :: route_for_value ( route_params. final_value_msat )
1463+ } )
1464+ }
14051465 }
14061466
14071467 struct FailingRouter ;
@@ -1413,6 +1473,12 @@ mod tests {
14131473 ) -> Result < Route , LightningError > {
14141474 Err ( LightningError { err : String :: new ( ) , action : ErrorAction :: IgnoreError } )
14151475 }
1476+
1477+ fn build_route_from_hops (
1478+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , _route_params : & RouteParameters
1479+ ) -> Result < Route , LightningError > {
1480+ Err ( LightningError { err : String :: new ( ) , action : ErrorAction :: IgnoreError } )
1481+ }
14161482 }
14171483
14181484 struct TestScorer {
@@ -1604,6 +1670,17 @@ mod tests {
16041670 }
16051671
16061672 fn abandon_payment ( & self , _payment_id : PaymentId ) { }
1673+
1674+ fn send_probe_payment ( & self , route : & Route ) -> Result < PaymentId , PaymentSendFailure > {
1675+ // TODO: for now copied from spontaneous, figure out what to do here.
1676+ self . check_value_msats ( Amount :: Spontaneous ( route. get_total_amount ( ) ) ) ;
1677+ self . check_attempts ( )
1678+ }
1679+
1680+ fn payment_is_probe ( & self , _payment_hash : PaymentHash , _payment_id : PaymentId ) -> bool {
1681+ // TODO: figure out what to do here.
1682+ false
1683+ }
16071684 }
16081685
16091686 // *** Full Featured Functional Tests with a Real ChannelManager ***
@@ -1616,6 +1693,12 @@ mod tests {
16161693 ) -> Result < Route , LightningError > {
16171694 self . 0 . borrow_mut ( ) . pop_front ( ) . unwrap ( )
16181695 }
1696+
1697+ fn build_route_from_hops (
1698+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , _route_params : & RouteParameters
1699+ ) -> Result < Route , LightningError > {
1700+ self . 0 . borrow_mut ( ) . pop_front ( ) . unwrap ( )
1701+ }
16191702 }
16201703 impl ManualRouter {
16211704 fn expect_find_route ( & self , result : Result < Route , LightningError > ) {
0 commit comments