@@ -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,46 @@ 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. This can be checked by calling
2744+ /// [`payment_is_probe`].
2745+ ///
2746+ /// [`payment_is_probe`]: Self::payment_is_probe
2747+ pub fn send_probe ( & self , hops : Vec < RouteHop > ) -> Result < ( PaymentHash , PaymentId ) , PaymentSendFailure > {
2748+ let payment_id = PaymentId ( self . keys_manager . get_secure_random_bytes ( ) ) ;
2749+
2750+ let payment_hash = self . probing_cookie_from_id ( & payment_id) ;
2751+
2752+ if hops. len ( ) < 2 {
2753+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
2754+ err : "No need probing a path with less than two hops" . to_string ( )
2755+ } ) )
2756+ }
2757+
2758+ let route = Route { paths : vec ! [ hops] , payment_params : None } ;
2759+
2760+ match self . send_payment_internal ( & route, payment_hash, & None , None , Some ( payment_id) , None ) {
2761+ Ok ( payment_id) => Ok ( ( payment_hash, payment_id) ) ,
2762+ Err ( e) => Err ( e)
2763+ }
2764+ }
2765+
2766+ /// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
2767+ /// payment probe.
2768+ pub ( crate ) fn payment_is_probe ( & self , payment_hash : & PaymentHash , payment_id : & PaymentId ) -> bool {
2769+ let target_payment_hash = self . probing_cookie_from_id ( payment_id) ;
2770+ target_payment_hash == * payment_hash
2771+ }
2772+
2773+ /// Returns the 'probing cookie' for the given [`PaymentId`].
2774+ fn probing_cookie_from_id ( & self , payment_id : & PaymentId ) -> PaymentHash {
2775+ let mut preimage = [ 0u8 ; 64 ] ;
2776+ preimage[ ..32 ] . copy_from_slice ( & self . probing_cookie_secret ) ;
2777+ preimage[ 32 ..] . copy_from_slice ( & payment_id. 0 ) ;
2778+ PaymentHash ( Sha256 :: hash ( & preimage) . into_inner ( ) )
2779+ }
2780+
27342781 /// Handles the generation of a funding transaction, optionally (for tests) with a function
27352782 /// which checks the correctness of the funding transaction given the associated channel.
27362783 fn funding_transaction_generated_intern < FundingOutput : Fn ( & Channel < Signer > , & Transaction ) -> Result < OutPoint , APIError > > (
@@ -3839,22 +3886,40 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
38393886 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 ( ) ) ;
38403887#[ cfg( not( test) ) ]
38413888 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
3889+
3890+ if self . payment_is_probe ( payment_hash, & payment_id) {
3891+ if !payment_retryable {
3892+ events:: Event :: ProbeSuccessful {
3893+ payment_id,
3894+ payment_hash : payment_hash. clone ( ) ,
3895+ path : path. clone ( ) ,
3896+ }
3897+ } else {
3898+ events:: Event :: ProbeFailed {
3899+ payment_id : payment_id,
3900+ payment_hash : payment_hash. clone ( ) ,
3901+ path : path. clone ( ) ,
3902+ short_channel_id,
3903+ }
3904+ }
3905+ } else {
3906+ // TODO: If we decided to blame ourselves (or one of our channels) in
3907+ // process_onion_failure we should close that channel as it implies our
3908+ // next-hop is needlessly blaming us!
3909+ events:: Event :: PaymentPathFailed {
3910+ payment_id : Some ( payment_id) ,
3911+ payment_hash : payment_hash. clone ( ) ,
3912+ rejected_by_dest : !payment_retryable,
3913+ network_update,
3914+ all_paths_failed,
3915+ path : path. clone ( ) ,
3916+ short_channel_id,
3917+ retry,
3918+ #[ cfg( test) ]
3919+ error_code : onion_error_code,
3920+ #[ cfg( test) ]
3921+ error_data : onion_error_data
3922+ }
38583923 }
38593924 } ,
38603925 & HTLCFailReason :: Reason {
@@ -6631,6 +6696,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
66316696 ( 5 , self . our_network_pubkey, required) ,
66326697 ( 7 , self . fake_scid_rand_bytes, required) ,
66336698 ( 9 , htlc_purposes, vec_type) ,
6699+ ( 11 , self . probing_cookie_secret, required) ,
66346700 } ) ;
66356701
66366702 Ok ( ( ) )
@@ -6927,18 +6993,24 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
69276993 let mut pending_outbound_payments = None ;
69286994 let mut received_network_pubkey: Option < PublicKey > = None ;
69296995 let mut fake_scid_rand_bytes: Option < [ u8 ; 32 ] > = None ;
6996+ let mut probing_cookie_secret: Option < [ u8 ; 32 ] > = None ;
69306997 let mut claimable_htlc_purposes = None ;
69316998 read_tlv_fields ! ( reader, {
69326999 ( 1 , pending_outbound_payments_no_retry, option) ,
69337000 ( 3 , pending_outbound_payments, option) ,
69347001 ( 5 , received_network_pubkey, option) ,
69357002 ( 7 , fake_scid_rand_bytes, option) ,
69367003 ( 9 , claimable_htlc_purposes, vec_type) ,
7004+ ( 11 , probing_cookie_secret, option) ,
69377005 } ) ;
69387006 if fake_scid_rand_bytes. is_none ( ) {
69397007 fake_scid_rand_bytes = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
69407008 }
69417009
7010+ if probing_cookie_secret. is_none ( ) {
7011+ probing_cookie_secret = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
7012+ }
7013+
69427014 if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
69437015 pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
69447016 } else if pending_outbound_payments. is_none ( ) {
@@ -7144,6 +7216,8 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
71447216 outbound_scid_aliases : Mutex :: new ( outbound_scid_aliases) ,
71457217 fake_scid_rand_bytes : fake_scid_rand_bytes. unwrap ( ) ,
71467218
7219+ probing_cookie_secret : probing_cookie_secret. unwrap ( ) ,
7220+
71477221 our_network_key,
71487222 our_network_pubkey,
71497223 secp_ctx,
0 commit comments