@@ -749,6 +749,11 @@ pub struct ChannelManager<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref,
749
749
/// [fake scids]: crate::util::scid_utils::fake_scid
750
750
fake_scid_rand_bytes : [ u8 ; 32 ] ,
751
751
752
+ /// When we send payment probes, we generate the [`PaymentHash`] based on this cookie secret
753
+ /// and a random [`PaymentId`]. This allows us to discern probes from real payments, without
754
+ /// keeping additional state.
755
+ probing_cookie_secret : [ u8 ; 32 ] ,
756
+
752
757
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
753
758
/// value increases strictly since we don't assume access to a time source.
754
759
last_node_announcement_serial : AtomicUsize ,
@@ -1605,6 +1610,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
1605
1610
inbound_payment_key : expanded_inbound_key,
1606
1611
fake_scid_rand_bytes : keys_manager. get_secure_random_bytes ( ) ,
1607
1612
1613
+ probing_cookie_secret : keys_manager. get_secure_random_bytes ( ) ,
1614
+
1608
1615
last_node_announcement_serial : AtomicUsize :: new ( 0 ) ,
1609
1616
highest_seen_timestamp : AtomicUsize :: new ( 0 ) ,
1610
1617
@@ -2722,6 +2729,49 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
2722
2729
}
2723
2730
}
2724
2731
2732
+ /// Send a payment that is probing the given route for liquidity. We calculate the
2733
+ /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
2734
+ /// us to easily discern them from real payments. This can be checked by calling
2735
+ /// [`payment_is_probe`].
2736
+ ///
2737
+ /// [`payment_is_probe`]: Self::payment_is_probe
2738
+ pub fn send_probe_payment ( & self , route : & Route ) -> Result < ( PaymentHash , PaymentId ) , PaymentSendFailure > {
2739
+ let payment_id = PaymentId ( self . keys_manager . get_secure_random_bytes ( ) ) ;
2740
+
2741
+ let mut preimage = [ 0u8 ; 32 ] ;
2742
+ preimage[ ..16 ] . copy_from_slice ( & self . probing_cookie_secret [ ..16 ] ) ;
2743
+ preimage[ 16 ..] . copy_from_slice ( & payment_id. 0 [ ..16 ] ) ;
2744
+
2745
+ let payment_hash = PaymentHash ( Sha256 :: hash ( & preimage) . into_inner ( ) ) ;
2746
+
2747
+ if let Some ( params) = & route. payment_params {
2748
+ if params. max_path_count != 1 {
2749
+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
2750
+ err : "Probe payments need to be sent over a single path" . to_string ( )
2751
+ } ) )
2752
+ }
2753
+ } else {
2754
+ return Err ( PaymentSendFailure :: ParameterError ( APIError :: APIMisuseError {
2755
+ err : "No parameters provided" . to_string ( )
2756
+ } ) )
2757
+ }
2758
+
2759
+ match self . send_payment_internal ( route, payment_hash, & None , None , Some ( payment_id) , None ) {
2760
+ Ok ( payment_id) => Ok ( ( payment_hash, payment_id) ) ,
2761
+ Err ( e) => Err ( e)
2762
+ }
2763
+ }
2764
+
2765
+ /// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
2766
+ /// payment probe.
2767
+ pub fn payment_is_probe ( & self , payment_hash : PaymentHash , payment_id : PaymentId ) -> bool {
2768
+ let mut preimage = [ 0u8 ; 32 ] ;
2769
+ preimage[ ..16 ] . copy_from_slice ( & self . probing_cookie_secret [ ..16 ] ) ;
2770
+ preimage[ 16 ..] . copy_from_slice ( & payment_id. 0 [ ..16 ] ) ;
2771
+ let target_payment_hash = PaymentHash ( Sha256 :: hash ( & preimage) . into_inner ( ) ) ;
2772
+ target_payment_hash == payment_hash
2773
+ }
2774
+
2725
2775
/// Handles the generation of a funding transaction, optionally (for tests) with a function
2726
2776
/// which checks the correctness of the funding transaction given the associated channel.
2727
2777
fn funding_transaction_generated_intern < FundingOutput : Fn ( & Channel < Signer > , & Transaction ) -> Result < OutPoint , APIError > > (
@@ -6623,6 +6673,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable f
6623
6673
( 5 , self . our_network_pubkey, required) ,
6624
6674
( 7 , self . fake_scid_rand_bytes, required) ,
6625
6675
( 9 , htlc_purposes, vec_type) ,
6676
+ ( 11 , self . probing_cookie_secret, required) ,
6626
6677
} ) ;
6627
6678
6628
6679
Ok ( ( ) )
@@ -6919,18 +6970,24 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
6919
6970
let mut pending_outbound_payments = None ;
6920
6971
let mut received_network_pubkey: Option < PublicKey > = None ;
6921
6972
let mut fake_scid_rand_bytes: Option < [ u8 ; 32 ] > = None ;
6973
+ let mut probing_cookie_secret: Option < [ u8 ; 32 ] > = None ;
6922
6974
let mut claimable_htlc_purposes = None ;
6923
6975
read_tlv_fields ! ( reader, {
6924
6976
( 1 , pending_outbound_payments_no_retry, option) ,
6925
6977
( 3 , pending_outbound_payments, option) ,
6926
6978
( 5 , received_network_pubkey, option) ,
6927
6979
( 7 , fake_scid_rand_bytes, option) ,
6928
6980
( 9 , claimable_htlc_purposes, vec_type) ,
6981
+ ( 11 , probing_cookie_secret, option) ,
6929
6982
} ) ;
6930
6983
if fake_scid_rand_bytes. is_none ( ) {
6931
6984
fake_scid_rand_bytes = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
6932
6985
}
6933
6986
6987
+ if probing_cookie_secret. is_none ( ) {
6988
+ probing_cookie_secret = Some ( args. keys_manager . get_secure_random_bytes ( ) ) ;
6989
+ }
6990
+
6934
6991
if pending_outbound_payments. is_none ( ) && pending_outbound_payments_no_retry. is_none ( ) {
6935
6992
pending_outbound_payments = Some ( pending_outbound_payments_compat) ;
6936
6993
} else if pending_outbound_payments. is_none ( ) {
@@ -7136,6 +7193,8 @@ impl<'a, Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
7136
7193
outbound_scid_aliases : Mutex :: new ( outbound_scid_aliases) ,
7137
7194
fake_scid_rand_bytes : fake_scid_rand_bytes. unwrap ( ) ,
7138
7195
7196
+ probing_cookie_secret : probing_cookie_secret. unwrap ( ) ,
7197
+
7139
7198
our_network_key,
7140
7199
our_network_pubkey,
7141
7200
secp_ctx,
0 commit comments