@@ -740,6 +740,11 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
740740 /// [fake scids]: crate::util::scid_utils::fake_scid
741741 fake_scid_rand_bytes : [ u8 ; 32 ] ,
742742
743+ /// When we send payment probes, we generate the [`PaymentHash`] based on this cookie secret
744+ /// and a random [`PaymentId`]. This allows us to discern probes from real payments, without
745+ /// keeping additional state.
746+ probing_cookie_secret : [ u8 ; 32 ] ,
747+
743748 /// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
744749 /// value increases strictly since we don't assume access to a time source.
745750 last_node_announcement_serial : AtomicUsize ,
@@ -1589,6 +1594,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
15891594 inbound_payment_key : expanded_inbound_key,
15901595 fake_scid_rand_bytes : keys_manager. get_secure_random_bytes ( ) ,
15911596
1597+ probing_cookie_secret : keys_manager. get_secure_random_bytes ( ) ,
1598+
15921599 last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
15931600 highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
15941601
@@ -2731,6 +2738,43 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
27312738 }
27322739 }
27332740
2741+ /// Send a payment that is probing the given route for liquidity. We calculate the
2742+ /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
2743+ /// us to easily discern them from real payments.
2744+ pub fn send_probe ( & self , hops : Vec < RouteHop > ) -> Result < ( PaymentHash , PaymentId ) , PaymentSendFailure > {
2745+ let payment_id = PaymentId ( self . keys_manager . get_secure_random_bytes ( ) ) ;
2746+
2747+ let payment_hash = self . probing_cookie_from_id ( & payment_id) ;
2748+
2749+ if hops. len ( ) < 2 {
2750+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
2751+ err : "No need probing a path with less than two hops" . to_string ( )
2752+ } ) )
2753+ }
2754+
2755+ let route = Route { paths : vec ! [ hops] , payment_params : None } ;
2756+
2757+ match self . send_payment_internal ( & route, payment_hash, & None , None , Some ( payment_id) , None ) {
2758+ Ok ( payment_id) => Ok ( ( payment_hash, payment_id) ) ,
2759+ Err ( e) => Err ( e)
2760+ }
2761+ }
2762+
2763+ /// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
2764+ /// payment probe.
2765+ pub ( crate ) fn payment_is_probe ( & self , payment_hash : & PaymentHash , payment_id : & PaymentId ) -> bool {
2766+ let target_payment_hash = self . probing_cookie_from_id ( payment_id) ;
2767+ target_payment_hash == * payment_hash
2768+ }
2769+
2770+ /// Returns the 'probing cookie' for the given [`PaymentId`].
2771+ fn probing_cookie_from_id ( & self , payment_id : & PaymentId ) -> PaymentHash {
2772+ let mut preimage = [ 0u8 ; 64 ] ;
2773+ preimage[ ..32 ] . copy_from_slice ( & self . probing_cookie_secret ) ;
2774+ preimage[ 32 ..] . copy_from_slice ( & payment_id. 0 ) ;
2775+ PaymentHash ( Sha256 :: hash ( & preimage) . into_inner ( ) )
2776+ }
2777+
27342778 /// Handles the generation of a funding transaction, optionally (for tests) with a function
27352779 /// which checks the correctness of the funding transaction given the associated channel.
27362780 fn funding_transaction_generated_intern < FundingOutput : Fn ( & Channel < Signer > , & Transaction ) -> Result < OutPoint , APIError > > (
@@ -3839,22 +3883,40 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
38393883 let ( network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils:: process_onion_failure ( & self . secp_ctx , & self . logger , & source, err. data . clone ( ) ) ;
38403884#[ cfg( not( test) ) ]
38413885 let ( network_update, short_channel_id, payment_retryable, _, _) = onion_utils:: process_onion_failure ( & self . secp_ctx , & self . logger , & source, err. data . clone ( ) ) ;
3842- // TODO: If we decided to blame ourselves (or one of our channels) in
3843- // process_onion_failure we should close that channel as it implies our
3844- // next-hop is needlessly blaming us!
3845- events:: Event :: PaymentPathFailed {
3846- payment_id : Some ( payment_id) ,
3847- payment_hash : payment_hash. clone ( ) ,
3848- rejected_by_dest : !payment_retryable,
3849- network_update,
3850- all_paths_failed,
3851- path : path. clone ( ) ,
3852- short_channel_id,
3853- retry,
3854- #[ cfg( test) ]
3855- error_code : onion_error_code,
3856- #[ cfg( test) ]
3857- error_data : onion_error_data
3886+
3887+ if self . payment_is_probe ( payment_hash, & payment_id) {
3888+ if !payment_retryable {
3889+ events:: Event :: ProbeSuccessful {
3890+ payment_id,
3891+ payment_hash : payment_hash. clone ( ) ,
3892+ path : path. clone ( ) ,
3893+ }
3894+ } else {
3895+ events:: Event :: ProbeFailed {
3896+ payment_id : payment_id,
3897+ payment_hash : payment_hash. clone ( ) ,
3898+ path : path. clone ( ) ,
3899+ short_channel_id,
3900+ }
3901+ }
3902+ } else {
3903+ // TODO: If we decided to blame ourselves (or one of our channels) in
3904+ // process_onion_failure we should close that channel as it implies our
3905+ // next-hop is needlessly blaming us!
3906+ events:: Event :: PaymentPathFailed {
3907+ payment_id : Some ( payment_id) ,
3908+ payment_hash : payment_hash. clone ( ) ,
3909+ rejected_by_dest : !payment_retryable,
3910+ network_update,
3911+ all_paths_failed,
3912+ path : path. clone ( ) ,
3913+ short_channel_id,
3914+ retry,
3915+ #[ cfg( test) ]
3916+ error_code : onion_error_code,
3917+ #[ cfg( test) ]
3918+ error_data : onion_error_data
3919+ }
38583920 }
38593921 } ,
38603922 & HTLCFailReason :: Reason {
@@ -6631,6 +6693,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
66316693 ( 5 , self . our_network_pubkey, required) ,
66326694 ( 7 , self . fake_scid_rand_bytes, required) ,
66336695 ( 9 , htlc_purposes, vec_type) ,
6696+ ( 11 , self . probing_cookie_secret, required) ,
66346697 } ) ;
66356698
66366699 Ok ( ( ) )
@@ -6927,18 +6990,24 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
69276990 let mut pending_outbound_payments = None ;
69286991 let mut received_network_pubkey: Option < PublicKey > = None ;
69296992 let mut fake_scid_rand_bytes: Option < [ u8 ; 32 ] > = None ;
6993+ let mut probing_cookie_secret: Option < [ u8 ; 32 ] > = None ;
69306994 let mut claimable_htlc_purposes = None ;
69316995 read_tlv_fields ! ( reader, {
69326996 ( 1 , pending_outbound_payments_no_retry, option) ,
69336997 ( 3 , pending_outbound_payments, option) ,
69346998 ( 5 , received_network_pubkey, option) ,
69356999 ( 7 , fake_scid_rand_bytes, option) ,
69367000 ( 9 , claimable_htlc_purposes, vec_type) ,
7001+ ( 11 , probing_cookie_secret, option) ,
69377002 } ) ;
69387003 if fake_scid_rand_bytes. is_none ( ) {
69397004 fake_scid_rand_bytes = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
69407005 }
69417006
7007+ if probing_cookie_secret. is_none ( ) {
7008+ probing_cookie_secret = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
7009+ }
7010+
69427011 if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
69437012 pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
69447013 } else if pending_outbound_payments. is_none ( ) {
@@ -7144,6 +7213,8 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
71447213 outbound_scid_aliases : Mutex :: new ( outbound_scid_aliases) ,
71457214 fake_scid_rand_bytes : fake_scid_rand_bytes. unwrap ( ) ,
71467215
7216+ probing_cookie_secret : probing_cookie_secret. unwrap ( ) ,
7217+
71477218 our_network_key,
71487219 our_network_pubkey,
71497220 secp_ctx,
0 commit comments