@@ -2528,7 +2528,8 @@ where
2528
2528
2529
2529
fn construct_recv_pending_htlc_info (
2530
2530
& self , hop_data : msgs:: OnionHopData , shared_secret : [ u8 ; 32 ] , payment_hash : PaymentHash ,
2531
- amt_msat : u64 , cltv_expiry : u32 , phantom_shared_secret : Option < [ u8 ; 32 ] > , allow_underpay : bool
2531
+ amt_msat : u64 , cltv_expiry : u32 , phantom_shared_secret : Option < [ u8 ; 32 ] > , allow_underpay : bool ,
2532
+ counterparty_skimmed_fee_msat : Option < u64 > ,
2532
2533
) -> Result < PendingHTLCInfo , ReceiveError > {
2533
2534
// final_incorrect_cltv_expiry
2534
2535
if hop_data. outgoing_cltv_value > cltv_expiry {
@@ -2555,7 +2556,10 @@ where
2555
2556
msg : "The final CLTV expiry is too soon to handle" ,
2556
2557
} ) ;
2557
2558
}
2558
- if !allow_underpay && hop_data. amt_to_forward > amt_msat {
2559
+ if ( !allow_underpay && hop_data. amt_to_forward > amt_msat) ||
2560
+ ( allow_underpay && hop_data. amt_to_forward >
2561
+ amt_msat. saturating_add ( counterparty_skimmed_fee_msat. unwrap_or ( 0 ) ) )
2562
+ {
2559
2563
return Err ( ReceiveError {
2560
2564
err_code : 19 ,
2561
2565
err_data : amt_msat. to_be_bytes ( ) . to_vec ( ) ,
@@ -2622,7 +2626,7 @@ where
2622
2626
incoming_amt_msat : Some ( amt_msat) ,
2623
2627
outgoing_amt_msat : hop_data. amt_to_forward ,
2624
2628
outgoing_cltv_value : hop_data. outgoing_cltv_value ,
2625
- skimmed_fee_msat : None ,
2629
+ skimmed_fee_msat : counterparty_skimmed_fee_msat ,
2626
2630
} )
2627
2631
}
2628
2632
@@ -2857,7 +2861,7 @@ where
2857
2861
onion_utils:: Hop :: Receive ( next_hop_data) => {
2858
2862
// OUR PAYMENT!
2859
2863
match self . construct_recv_pending_htlc_info ( next_hop_data, shared_secret, msg. payment_hash ,
2860
- msg. amount_msat , msg. cltv_expiry , None , allow_underpay)
2864
+ msg. amount_msat , msg. cltv_expiry , None , allow_underpay, msg . skimmed_fee_msat )
2861
2865
{
2862
2866
Ok ( info) => {
2863
2867
// Note that we could obviously respond immediately with an update_fulfill_htlc
@@ -3676,7 +3680,7 @@ where
3676
3680
onion_utils:: Hop :: Receive ( hop_data) => {
3677
3681
match self . construct_recv_pending_htlc_info( hop_data,
3678
3682
incoming_shared_secret, payment_hash, outgoing_amt_msat,
3679
- outgoing_cltv_value, Some ( phantom_shared_secret) , false )
3683
+ outgoing_cltv_value, Some ( phantom_shared_secret) , false , None )
3680
3684
{
3681
3685
Ok ( info) => phantom_receives. push( ( prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![ ( info, prev_htlc_id) ] ) ) ,
3682
3686
Err ( ReceiveError { err_code, err_data, msg } ) => failed_payment!( msg, err_code, err_data, Some ( phantom_shared_secret) )
@@ -9741,6 +9745,50 @@ mod tests {
9741
9745
get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendAcceptChannel , last_random_pk) ;
9742
9746
}
9743
9747
9748
+ #[ test]
9749
+ fn reject_excessively_underpaying_htlcs ( ) {
9750
+ let chanmon_cfg = create_chanmon_cfgs ( 1 ) ;
9751
+ let node_cfg = create_node_cfgs ( 1 , & chanmon_cfg) ;
9752
+ let node_chanmgr = create_node_chanmgrs ( 1 , & node_cfg, & [ None ] ) ;
9753
+ let node = create_network ( 1 , & node_cfg, & node_chanmgr) ;
9754
+ let sender_intended_amt_msat = 100 ;
9755
+ let extra_fee_msat = 10 ;
9756
+ let hop_data = msgs:: OnionHopData {
9757
+ amt_to_forward : 100 ,
9758
+ outgoing_cltv_value : 42 ,
9759
+ format : msgs:: OnionHopDataFormat :: FinalNode {
9760
+ keysend_preimage : None ,
9761
+ payment_metadata : None ,
9762
+ payment_data : Some ( msgs:: FinalOnionHopData {
9763
+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9764
+ } ) ,
9765
+ }
9766
+ } ;
9767
+ // Check that if the amount we received + the penultimate hop extra fee is less than the sender
9768
+ // intended amount, we fail the payment.
9769
+ if let Err ( crate :: ln:: channelmanager:: ReceiveError { err_code, .. } ) =
9770
+ node[ 0 ] . node . construct_recv_pending_htlc_info ( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9771
+ sender_intended_amt_msat - extra_fee_msat - 1 , 42 , None , true , Some ( extra_fee_msat) )
9772
+ {
9773
+ assert_eq ! ( err_code, 19 ) ;
9774
+ } else { panic ! ( ) ; }
9775
+
9776
+ // If amt_received + extra_fee is equal to the sender intended amount, we're fine.
9777
+ let hop_data = msgs:: OnionHopData { // This is the same hop_data as above, OnionHopData doesn't implement Clone
9778
+ amt_to_forward : 100 ,
9779
+ outgoing_cltv_value : 42 ,
9780
+ format : msgs:: OnionHopDataFormat :: FinalNode {
9781
+ keysend_preimage : None ,
9782
+ payment_metadata : None ,
9783
+ payment_data : Some ( msgs:: FinalOnionHopData {
9784
+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9785
+ } ) ,
9786
+ }
9787
+ } ;
9788
+ assert ! ( node[ 0 ] . node. construct_recv_pending_htlc_info( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9789
+ sender_intended_amt_msat - extra_fee_msat, 42 , None , true , Some ( extra_fee_msat) ) . is_ok( ) ) ;
9790
+ }
9791
+
9744
9792
#[ cfg( anchors) ]
9745
9793
#[ test]
9746
9794
fn test_anchors_zero_fee_htlc_tx_fallback ( ) {
0 commit comments