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 > {
@@ -550,6 +588,17 @@ where
550588 Event :: PaymentPathFailed {
551589 payment_id, payment_hash, rejected_by_dest, path, short_channel_id, retry, ..
552590 } => {
591+ if let Some ( payment_id) = payment_id {
592+ // When the failed payment was a probe, we make sure to not penalize the last
593+ // hop and then drop the event instead of handing it up to the user's event
594+ // handler.
595+ if self . payer . payment_is_probe ( * payment_hash, * payment_id) {
596+ let path = path. iter ( ) . collect :: < Vec < _ > > ( ) ;
597+ self . scorer . lock ( ) . payment_path_failed ( & path, u64:: max_value ( ) ) ;
598+ return ;
599+ }
600+ }
601+
553602 if let Some ( short_channel_id) = short_channel_id {
554603 let path = path. iter ( ) . collect :: < Vec < _ > > ( ) ;
555604 self . scorer . lock ( ) . payment_path_failed ( & path, * short_channel_id) ;
@@ -1402,6 +1451,14 @@ mod tests {
14021451 payment_params : Some ( route_params. payment_params . clone ( ) ) , ..Self :: route_for_value ( route_params. final_value_msat )
14031452 } )
14041453 }
1454+
1455+ fn build_route_from_hops (
1456+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , route_params : & RouteParameters
1457+ ) -> Result < Route , LightningError > {
1458+ Ok ( Route {
1459+ payment_params : Some ( route_params. payment_params . clone ( ) ) , ..Self :: route_for_value ( route_params. final_value_msat )
1460+ } )
1461+ }
14051462 }
14061463
14071464 struct FailingRouter ;
@@ -1413,6 +1470,12 @@ mod tests {
14131470 ) -> Result < Route , LightningError > {
14141471 Err ( LightningError { err : String :: new ( ) , action : ErrorAction :: IgnoreError } )
14151472 }
1473+
1474+ fn build_route_from_hops (
1475+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , _route_params : & RouteParameters
1476+ ) -> Result < Route , LightningError > {
1477+ Err ( LightningError { err : String :: new ( ) , action : ErrorAction :: IgnoreError } )
1478+ }
14161479 }
14171480
14181481 struct TestScorer {
@@ -1604,6 +1667,17 @@ mod tests {
16041667 }
16051668
16061669 fn abandon_payment ( & self , _payment_id : PaymentId ) { }
1670+
1671+ fn send_probe_payment ( & self , route : & Route ) -> Result < PaymentId , PaymentSendFailure > {
1672+ // TODO: for now copied from spontaneous, figure out what to do here.
1673+ self . check_value_msats ( Amount :: Spontaneous ( route. get_total_amount ( ) ) ) ;
1674+ self . check_attempts ( )
1675+ }
1676+
1677+ fn payment_is_probe ( & self , _payment_hash : PaymentHash , _payment_id : PaymentId ) -> bool {
1678+ // TODO: figure out what to do here.
1679+ false
1680+ }
16071681 }
16081682
16091683 // *** Full Featured Functional Tests with a Real ChannelManager ***
@@ -1616,6 +1690,12 @@ mod tests {
16161690 ) -> Result < Route , LightningError > {
16171691 self . 0 . borrow_mut ( ) . pop_front ( ) . unwrap ( )
16181692 }
1693+
1694+ fn build_route_from_hops (
1695+ & self , _payer : & PublicKey , _hops : & [ PublicKey ] , _route_params : & RouteParameters
1696+ ) -> Result < Route , LightningError > {
1697+ self . 0 . borrow_mut ( ) . pop_front ( ) . unwrap ( )
1698+ }
16191699 }
16201700 impl ManualRouter {
16211701 fn expect_find_route ( & self , result : Result < Route , LightningError > ) {
0 commit comments