@@ -2528,7 +2528,8 @@ where
25282528
25292529 fn construct_recv_pending_htlc_info (
25302530 & 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 > ,
25322533 ) -> Result < PendingHTLCInfo , ReceiveError > {
25332534 // final_incorrect_cltv_expiry
25342535 if hop_data. outgoing_cltv_value > cltv_expiry {
@@ -2555,7 +2556,10 @@ where
25552556 msg : "The final CLTV expiry is too soon to handle" ,
25562557 } ) ;
25572558 }
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+ {
25592563 return Err ( ReceiveError {
25602564 err_code : 19 ,
25612565 err_data : amt_msat. to_be_bytes ( ) . to_vec ( ) ,
@@ -2622,7 +2626,7 @@ where
26222626 incoming_amt_msat : Some ( amt_msat) ,
26232627 outgoing_amt_msat : hop_data. amt_to_forward ,
26242628 outgoing_cltv_value : hop_data. outgoing_cltv_value ,
2625- skimmed_fee_msat : None ,
2629+ skimmed_fee_msat : counterparty_skimmed_fee_msat ,
26262630 } )
26272631 }
26282632
@@ -2861,7 +2865,7 @@ where
28612865 onion_utils:: Hop :: Receive ( next_hop_data) => {
28622866 // OUR PAYMENT!
28632867 match self . construct_recv_pending_htlc_info ( next_hop_data, shared_secret, msg. payment_hash ,
2864- msg. amount_msat , msg. cltv_expiry , None , allow_underpay)
2868+ msg. amount_msat , msg. cltv_expiry , None , allow_underpay, msg . skimmed_fee_msat )
28652869 {
28662870 Ok ( info) => {
28672871 // Note that we could obviously respond immediately with an update_fulfill_htlc
@@ -3680,7 +3684,7 @@ where
36803684 onion_utils:: Hop :: Receive ( hop_data) => {
36813685 match self . construct_recv_pending_htlc_info( hop_data,
36823686 incoming_shared_secret, payment_hash, outgoing_amt_msat,
3683- outgoing_cltv_value, Some ( phantom_shared_secret) , false )
3687+ outgoing_cltv_value, Some ( phantom_shared_secret) , false , None )
36843688 {
36853689 Ok ( info) => phantom_receives. push( ( prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![ ( info, prev_htlc_id) ] ) ) ,
36863690 Err ( ReceiveError { err_code, err_data, msg } ) => failed_payment!( msg, err_code, err_data, Some ( phantom_shared_secret) )
@@ -3931,6 +3935,8 @@ where
39313935 htlcs. iter_mut( ) . for_each( |htlc| htlc. total_value_received = Some ( amount_msat) ) ;
39323936 let counterparty_skimmed_fee_msat = htlcs. iter( )
39333937 . map( |htlc| htlc. counterparty_skimmed_fee_msat. unwrap_or( 0 ) ) . sum( ) ;
3938+ debug_assert!( total_value. saturating_sub( amount_msat) <=
3939+ counterparty_skimmed_fee_msat) ;
39343940 new_events. push_back( ( events:: Event :: PaymentClaimable {
39353941 receiver_node_id: Some ( receiver_node_id) ,
39363942 payment_hash,
@@ -9743,6 +9749,50 @@ mod tests {
97439749 get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendAcceptChannel , last_random_pk) ;
97449750 }
97459751
9752+ #[ test]
9753+ fn reject_excessively_underpaying_htlcs ( ) {
9754+ let chanmon_cfg = create_chanmon_cfgs ( 1 ) ;
9755+ let node_cfg = create_node_cfgs ( 1 , & chanmon_cfg) ;
9756+ let node_chanmgr = create_node_chanmgrs ( 1 , & node_cfg, & [ None ] ) ;
9757+ let node = create_network ( 1 , & node_cfg, & node_chanmgr) ;
9758+ let sender_intended_amt_msat = 100 ;
9759+ let extra_fee_msat = 10 ;
9760+ let hop_data = msgs:: OnionHopData {
9761+ amt_to_forward : 100 ,
9762+ outgoing_cltv_value : 42 ,
9763+ format : msgs:: OnionHopDataFormat :: FinalNode {
9764+ keysend_preimage : None ,
9765+ payment_metadata : None ,
9766+ payment_data : Some ( msgs:: FinalOnionHopData {
9767+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9768+ } ) ,
9769+ }
9770+ } ;
9771+ // Check that if the amount we received + the penultimate hop extra fee is less than the sender
9772+ // intended amount, we fail the payment.
9773+ if let Err ( crate :: ln:: channelmanager:: ReceiveError { err_code, .. } ) =
9774+ node[ 0 ] . node . construct_recv_pending_htlc_info ( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9775+ sender_intended_amt_msat - extra_fee_msat - 1 , 42 , None , true , Some ( extra_fee_msat) )
9776+ {
9777+ assert_eq ! ( err_code, 19 ) ;
9778+ } else { panic ! ( ) ; }
9779+
9780+ // If amt_received + extra_fee is equal to the sender intended amount, we're fine.
9781+ let hop_data = msgs:: OnionHopData { // This is the same hop_data as above, OnionHopData doesn't implement Clone
9782+ amt_to_forward : 100 ,
9783+ outgoing_cltv_value : 42 ,
9784+ format : msgs:: OnionHopDataFormat :: FinalNode {
9785+ keysend_preimage : None ,
9786+ payment_metadata : None ,
9787+ payment_data : Some ( msgs:: FinalOnionHopData {
9788+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9789+ } ) ,
9790+ }
9791+ } ;
9792+ assert ! ( node[ 0 ] . node. construct_recv_pending_htlc_info( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9793+ sender_intended_amt_msat - extra_fee_msat, 42 , None , true , Some ( extra_fee_msat) ) . is_ok( ) ) ;
9794+ }
9795+
97469796 #[ cfg( anchors) ]
97479797 #[ test]
97489798 fn test_anchors_zero_fee_htlc_tx_fallback ( ) {
0 commit comments