@@ -740,6 +740,11 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
740
740
/// [fake scids]: crate::util::scid_utils::fake_scid
741
741
fake_scid_rand_bytes : [ u8 ; 32 ] ,
742
742
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
+
743
748
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
744
749
/// value increases strictly since we don't assume access to a time source.
745
750
last_node_announcement_serial : AtomicUsize ,
@@ -1589,6 +1594,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1589
1594
inbound_payment_key : expanded_inbound_key,
1590
1595
fake_scid_rand_bytes : keys_manager. get_secure_random_bytes ( ) ,
1591
1596
1597
+ probing_cookie_secret : keys_manager. get_secure_random_bytes ( ) ,
1598
+
1592
1599
last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
1593
1600
highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
1594
1601
@@ -2731,6 +2738,43 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
2731
2738
}
2732
2739
}
2733
2740
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
+
2734
2778
/// Handles the generation of a funding transaction, optionally (for tests) with a function
2735
2779
/// which checks the correctness of the funding transaction given the associated channel.
2736
2780
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
3839
3883
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 ( ) ) ;
3840
3884
#[ cfg( not( test) ) ]
3841
3885
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
+ }
3858
3920
}
3859
3921
} ,
3860
3922
& HTLCFailReason :: Reason {
@@ -6631,6 +6693,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
6631
6693
( 5 , self . our_network_pubkey, required) ,
6632
6694
( 7 , self . fake_scid_rand_bytes, required) ,
6633
6695
( 9 , htlc_purposes, vec_type) ,
6696
+ ( 11 , self . probing_cookie_secret, required) ,
6634
6697
} ) ;
6635
6698
6636
6699
Ok ( ( ) )
@@ -6927,18 +6990,24 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6927
6990
let mut pending_outbound_payments = None ;
6928
6991
let mut received_network_pubkey: Option < PublicKey > = None ;
6929
6992
let mut fake_scid_rand_bytes: Option < [ u8 ; 32 ] > = None ;
6993
+ let mut probing_cookie_secret: Option < [ u8 ; 32 ] > = None ;
6930
6994
let mut claimable_htlc_purposes = None ;
6931
6995
read_tlv_fields ! ( reader, {
6932
6996
( 1 , pending_outbound_payments_no_retry, option) ,
6933
6997
( 3 , pending_outbound_payments, option) ,
6934
6998
( 5 , received_network_pubkey, option) ,
6935
6999
( 7 , fake_scid_rand_bytes, option) ,
6936
7000
( 9 , claimable_htlc_purposes, vec_type) ,
7001
+ ( 11 , probing_cookie_secret, option) ,
6937
7002
} ) ;
6938
7003
if fake_scid_rand_bytes. is_none ( ) {
6939
7004
fake_scid_rand_bytes = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
6940
7005
}
6941
7006
7007
+ if probing_cookie_secret. is_none ( ) {
7008
+ probing_cookie_secret = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
7009
+ }
7010
+
6942
7011
if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
6943
7012
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
6944
7013
} 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>
7144
7213
outbound_scid_aliases : Mutex :: new ( outbound_scid_aliases) ,
7145
7214
fake_scid_rand_bytes : fake_scid_rand_bytes. unwrap ( ) ,
7146
7215
7216
+ probing_cookie_secret : probing_cookie_secret. unwrap ( ) ,
7217
+
7147
7218
our_network_key,
7148
7219
our_network_pubkey,
7149
7220
secp_ctx,
0 commit comments