@@ -2410,7 +2410,8 @@ where
24102410
24112411 fn construct_recv_pending_htlc_info (
24122412 & self , hop_data : msgs:: OnionHopData , shared_secret : [ u8 ; 32 ] , payment_hash : PaymentHash ,
2413- amt_msat : u64 , cltv_expiry : u32 , phantom_shared_secret : Option < [ u8 ; 32 ] > , allow_underpay : bool
2413+ amt_msat : u64 , cltv_expiry : u32 , phantom_shared_secret : Option < [ u8 ; 32 ] > , allow_underpay : bool ,
2414+ counterparty_skimmed_fee_msat : Option < u64 > ,
24142415 ) -> Result < PendingHTLCInfo , ReceiveError > {
24152416 // final_incorrect_cltv_expiry
24162417 if hop_data. outgoing_cltv_value > cltv_expiry {
@@ -2437,7 +2438,10 @@ where
24372438 msg : "The final CLTV expiry is too soon to handle" ,
24382439 } ) ;
24392440 }
2440- if !allow_underpay && hop_data. amt_to_forward > amt_msat {
2441+ if ( !allow_underpay && hop_data. amt_to_forward > amt_msat) ||
2442+ ( allow_underpay && hop_data. amt_to_forward >
2443+ amt_msat. saturating_add ( counterparty_skimmed_fee_msat. unwrap_or ( 0 ) ) )
2444+ {
24412445 return Err ( ReceiveError {
24422446 err_code : 19 ,
24432447 err_data : amt_msat. to_be_bytes ( ) . to_vec ( ) ,
@@ -2735,7 +2739,7 @@ where
27352739 onion_utils:: Hop :: Receive ( next_hop_data) => {
27362740 // OUR PAYMENT!
27372741 match self . construct_recv_pending_htlc_info ( next_hop_data, shared_secret, msg. payment_hash ,
2738- msg. amount_msat , msg. cltv_expiry , None , allow_underpay)
2742+ msg. amount_msat , msg. cltv_expiry , None , allow_underpay, msg . skimmed_fee_msat )
27392743 {
27402744 Ok ( info) => {
27412745 // Note that we could obviously respond immediately with an update_fulfill_htlc
@@ -3553,7 +3557,7 @@ where
35533557 onion_utils:: Hop :: Receive ( hop_data) => {
35543558 match self . construct_recv_pending_htlc_info( hop_data,
35553559 incoming_shared_secret, payment_hash, outgoing_amt_msat,
3556- outgoing_cltv_value, Some ( phantom_shared_secret) , false )
3560+ outgoing_cltv_value, Some ( phantom_shared_secret) , false , None )
35573561 {
35583562 Ok ( info) => phantom_receives. push( ( prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, vec![ ( info, prev_htlc_id) ] ) ) ,
35593563 Err ( ReceiveError { err_code, err_data, msg } ) => failed_payment!( msg, err_code, err_data, Some ( phantom_shared_secret) )
@@ -9555,6 +9559,50 @@ mod tests {
95559559 get_event_msg ! ( nodes[ 1 ] , MessageSendEvent :: SendAcceptChannel , last_random_pk) ;
95569560 }
95579561
9562+ #[ test]
9563+ fn reject_excessively_underpaying_htlcs ( ) {
9564+ let chanmon_cfg = create_chanmon_cfgs ( 1 ) ;
9565+ let node_cfg = create_node_cfgs ( 1 , & chanmon_cfg) ;
9566+ let node_chanmgr = create_node_chanmgrs ( 1 , & node_cfg, & [ None ] ) ;
9567+ let node = create_network ( 1 , & node_cfg, & node_chanmgr) ;
9568+ let sender_intended_amt_msat = 100 ;
9569+ let extra_fee_msat = 10 ;
9570+ let hop_data = msgs:: OnionHopData {
9571+ amt_to_forward : 100 ,
9572+ outgoing_cltv_value : 42 ,
9573+ format : msgs:: OnionHopDataFormat :: FinalNode {
9574+ keysend_preimage : None ,
9575+ payment_metadata : None ,
9576+ payment_data : Some ( msgs:: FinalOnionHopData {
9577+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9578+ } ) ,
9579+ }
9580+ } ;
9581+ // Check that if the amount we received + the penultimate hop extra fee is less than the sender
9582+ // intended amount, we fail the payment.
9583+ if let Err ( crate :: ln:: channelmanager:: ReceiveError { err_code, .. } ) =
9584+ node[ 0 ] . node . construct_recv_pending_htlc_info ( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9585+ sender_intended_amt_msat - extra_fee_msat - 1 , 42 , None , true , Some ( extra_fee_msat) )
9586+ {
9587+ assert_eq ! ( err_code, 19 ) ;
9588+ } else { panic ! ( ) ; }
9589+
9590+ // If amt_received + extra_fee is equal to the sender intended amount, we're fine.
9591+ let hop_data = msgs:: OnionHopData { // This is the same hop_data as above, OnionHopData doesn't implement Clone
9592+ amt_to_forward : 100 ,
9593+ outgoing_cltv_value : 42 ,
9594+ format : msgs:: OnionHopDataFormat :: FinalNode {
9595+ keysend_preimage : None ,
9596+ payment_metadata : None ,
9597+ payment_data : Some ( msgs:: FinalOnionHopData {
9598+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) , total_msat : sender_intended_amt_msat,
9599+ } ) ,
9600+ }
9601+ } ;
9602+ assert ! ( node[ 0 ] . node. construct_recv_pending_htlc_info( hop_data, [ 0 ; 32 ] , PaymentHash ( [ 0 ; 32 ] ) ,
9603+ sender_intended_amt_msat - extra_fee_msat, 42 , None , true , Some ( extra_fee_msat) ) . is_ok( ) ) ;
9604+ }
9605+
95589606 #[ cfg( anchors) ]
95599607 #[ test]
95609608 fn test_anchors_zero_fee_htlc_tx_fallback ( ) {
0 commit comments