@@ -159,20 +159,26 @@ pub(crate) struct HTLCPreviousHopData {
159159}
160160
161161enum OnionPayload {
162- /// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
163- /// payment_secret which prevents path-probing attacks and can associate different HTLCs which
164- /// are part of the same payment.
165- Invoice ( msgs:: FinalOnionHopData ) ,
162+ /// Indicates this incoming onion payload is for the purpose of paying an invoice.
163+ Invoice {
164+ /// This is only here for backwards-compatibility in serialization, in the future it can be
165+ /// removed, breaking clients running 0.0.106 and earlier.
166+ _legacy_hop_data : msgs:: FinalOnionHopData ,
167+ } ,
166168 /// Contains the payer-provided preimage.
167169 Spontaneous ( PaymentPreimage ) ,
168170}
169171
172+ /// HTLCs that are to us and can be failed/claimed by the user
170173struct ClaimableHTLC {
171174 prev_hop : HTLCPreviousHopData ,
172175 cltv_expiry : u32 ,
176+ /// The amount (in msats) of this MPP part
173177 value : u64 ,
174178 onion_payload : OnionPayload ,
175179 timer_ticks : u8 ,
180+ /// The sum total of all MPP parts
181+ total_msat : u64 ,
176182}
177183
178184/// A payment identifier used to uniquely identify a payment to LDK.
@@ -3096,11 +3102,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
30963102 HTLCForwardInfo :: AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info : PendingHTLCInfo {
30973103 routing, incoming_shared_secret, payment_hash, amt_to_forward, .. } ,
30983104 prev_funding_outpoint } => {
3099- let ( cltv_expiry, onion_payload, phantom_shared_secret) = match routing {
3100- PendingHTLCRouting :: Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } =>
3101- ( incoming_cltv_expiry, OnionPayload :: Invoice ( payment_data) , phantom_shared_secret) ,
3105+ let ( cltv_expiry, onion_payload, payment_data, phantom_shared_secret) = match routing {
3106+ PendingHTLCRouting :: Receive { payment_data, incoming_cltv_expiry, phantom_shared_secret } => {
3107+ let _legacy_hop_data = payment_data. clone ( ) ;
3108+ ( incoming_cltv_expiry, OnionPayload :: Invoice { _legacy_hop_data } , Some ( payment_data) , phantom_shared_secret)
3109+ } ,
31023110 PendingHTLCRouting :: ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
3103- ( incoming_cltv_expiry, OnionPayload :: Spontaneous ( payment_preimage) , None ) ,
3111+ ( incoming_cltv_expiry, OnionPayload :: Spontaneous ( payment_preimage) , None , None ) ,
31043112 _ => {
31053113 panic ! ( "short_channel_id == 0 should imply any pending_forward entries are of type Receive" ) ;
31063114 }
@@ -3115,6 +3123,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31153123 } ,
31163124 value : amt_to_forward,
31173125 timer_ticks : 0 ,
3126+ total_msat : if let Some ( data) = & payment_data { data. total_msat } else { amt_to_forward } ,
31183127 cltv_expiry,
31193128 onion_payload,
31203129 } ;
@@ -3138,7 +3147,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31383147 }
31393148
31403149 macro_rules! check_total_value {
3141- ( $payment_data_total_msat : expr , $payment_secret : expr, $payment_preimage: expr) => { {
3150+ ( $payment_data : expr, $payment_preimage: expr) => { {
31423151 let mut payment_received_generated = false ;
31433152 let htlcs = channel_state. claimable_htlcs. entry( payment_hash)
31443153 . or_insert( Vec :: new( ) ) ;
@@ -3153,28 +3162,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31533162 for htlc in htlcs. iter( ) {
31543163 total_value += htlc. value;
31553164 match & htlc. onion_payload {
3156- OnionPayload :: Invoice ( htlc_payment_data ) => {
3157- if htlc_payment_data . total_msat != $payment_data_total_msat {
3165+ OnionPayload :: Invoice { .. } => {
3166+ if htlc . total_msat != $payment_data . total_msat {
31583167 log_trace!( self . logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})" ,
3159- log_bytes!( payment_hash. 0 ) , $payment_data_total_msat , htlc_payment_data . total_msat) ;
3168+ log_bytes!( payment_hash. 0 ) , $payment_data . total_msat , htlc . total_msat) ;
31603169 total_value = msgs:: MAX_VALUE_MSAT ;
31613170 }
31623171 if total_value >= msgs:: MAX_VALUE_MSAT { break ; }
31633172 } ,
31643173 _ => unreachable!( ) ,
31653174 }
31663175 }
3167- if total_value >= msgs:: MAX_VALUE_MSAT || total_value > $payment_data_total_msat {
3176+ if total_value >= msgs:: MAX_VALUE_MSAT || total_value > $payment_data . total_msat {
31683177 log_trace!( self . logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)" ,
3169- log_bytes!( payment_hash. 0 ) , total_value, $payment_data_total_msat ) ;
3178+ log_bytes!( payment_hash. 0 ) , total_value, $payment_data . total_msat ) ;
31703179 fail_htlc!( claimable_htlc) ;
3171- } else if total_value == $payment_data_total_msat {
3180+ } else if total_value == $payment_data . total_msat {
31723181 htlcs. push( claimable_htlc) ;
31733182 new_events. push( events:: Event :: PaymentReceived {
31743183 payment_hash,
31753184 purpose: events:: PaymentPurpose :: InvoicePayment {
31763185 payment_preimage: $payment_preimage,
3177- payment_secret: $payment_secret,
3186+ payment_secret: $payment_data . payment_secret,
31783187 } ,
31793188 amt: total_value,
31803189 } ) ;
@@ -3199,17 +3208,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
31993208 match payment_secrets. entry ( payment_hash) {
32003209 hash_map:: Entry :: Vacant ( _) => {
32013210 match claimable_htlc. onion_payload {
3202- OnionPayload :: Invoice ( ref payment_data) => {
3203- let payment_preimage = match inbound_payment:: verify ( payment_hash, payment_data. clone ( ) , self . highest_seen_timestamp . load ( Ordering :: Acquire ) as u64 , & self . inbound_payment_key , & self . logger ) {
3211+ OnionPayload :: Invoice { .. } => {
3212+ let payment_data = payment_data. unwrap ( ) ;
3213+ let payment_preimage = match inbound_payment:: verify ( payment_hash, & payment_data, self . highest_seen_timestamp . load ( Ordering :: Acquire ) as u64 , & self . inbound_payment_key , & self . logger ) {
32043214 Ok ( payment_preimage) => payment_preimage,
32053215 Err ( ( ) ) => {
32063216 fail_htlc ! ( claimable_htlc) ;
32073217 continue
32083218 }
32093219 } ;
3210- let payment_data_total_msat = payment_data. total_msat ;
3211- let payment_secret = payment_data. payment_secret . clone ( ) ;
3212- check_total_value ! ( payment_data_total_msat, payment_secret, payment_preimage) ;
3220+ check_total_value ! ( payment_data, payment_preimage) ;
32133221 } ,
32143222 OnionPayload :: Spontaneous ( preimage) => {
32153223 match channel_state. claimable_htlcs . entry ( payment_hash) {
@@ -3230,14 +3238,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32303238 }
32313239 } ,
32323240 hash_map:: Entry :: Occupied ( inbound_payment) => {
3233- let payment_data =
3234- if let OnionPayload :: Invoice ( ref data) = claimable_htlc. onion_payload {
3235- data. clone ( )
3236- } else {
3237- log_trace ! ( self . logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash" , log_bytes!( payment_hash. 0 ) ) ;
3238- fail_htlc ! ( claimable_htlc) ;
3239- continue
3240- } ;
3241+ if payment_data. is_none ( ) {
3242+ log_trace ! ( self . logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash" , log_bytes!( payment_hash. 0 ) ) ;
3243+ fail_htlc ! ( claimable_htlc) ;
3244+ continue
3245+ } ;
3246+ let payment_data = payment_data. unwrap ( ) ;
32413247 if inbound_payment. get ( ) . payment_secret != payment_data. payment_secret {
32423248 log_trace ! ( self . logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret." , log_bytes!( payment_hash. 0 ) ) ;
32433249 fail_htlc ! ( claimable_htlc) ;
@@ -3246,7 +3252,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
32463252 log_bytes!( payment_hash. 0 ) , payment_data. total_msat, inbound_payment. get( ) . min_value_msat. unwrap( ) ) ;
32473253 fail_htlc ! ( claimable_htlc) ;
32483254 } else {
3249- let payment_received_generated = check_total_value ! ( payment_data. total_msat , payment_data . payment_secret , inbound_payment. get( ) . payment_preimage) ;
3255+ let payment_received_generated = check_total_value ! ( payment_data, inbound_payment. get( ) . payment_preimage) ;
32503256 if payment_received_generated {
32513257 inbound_payment. remove_entry ( ) ;
32523258 }
@@ -3465,10 +3471,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
34653471 debug_assert ! ( false ) ;
34663472 return false ;
34673473 }
3468- if let OnionPayload :: Invoice ( ref final_hop_data ) = htlcs[ 0 ] . onion_payload {
3474+ if let OnionPayload :: Invoice { .. } = htlcs[ 0 ] . onion_payload {
34693475 // Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
34703476 // In this case we're not going to handle any timeouts of the parts here.
3471- if final_hop_data . total_msat == htlcs. iter ( ) . fold ( 0 , |total, htlc| total + htlc. value ) {
3477+ if htlcs [ 0 ] . total_msat == htlcs. iter ( ) . fold ( 0 , |total, htlc| total + htlc. value ) {
34723478 return true ;
34733479 } else if htlcs. into_iter ( ) . any ( |htlc| {
34743480 htlc. timer_ticks += 1 ;
@@ -6069,20 +6075,21 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
60696075impl Writeable for ClaimableHTLC {
60706076 fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
60716077 let payment_data = match & self . onion_payload {
6072- OnionPayload :: Invoice ( data ) => Some ( data . clone ( ) ) ,
6078+ OnionPayload :: Invoice { _legacy_hop_data } => Some ( _legacy_hop_data ) ,
60736079 _ => None ,
60746080 } ;
60756081 let keysend_preimage = match self . onion_payload {
6076- OnionPayload :: Invoice ( _ ) => None ,
6082+ OnionPayload :: Invoice { .. } => None ,
60776083 OnionPayload :: Spontaneous ( preimage) => Some ( preimage. clone ( ) ) ,
60786084 } ;
6079- write_tlv_fields !
6080- ( writer,
6081- {
6082- ( 0 , self . prev_hop, required) , ( 2 , self . value, required) ,
6083- ( 4 , payment_data, option) , ( 6 , self . cltv_expiry, required) ,
6084- ( 8 , keysend_preimage, option) ,
6085- } ) ;
6085+ write_tlv_fields ! ( writer, {
6086+ ( 0 , self . prev_hop, required) ,
6087+ ( 1 , self . total_msat, required) ,
6088+ ( 2 , self . value, required) ,
6089+ ( 4 , payment_data, option) ,
6090+ ( 6 , self . cltv_expiry, required) ,
6091+ ( 8 , keysend_preimage, option) ,
6092+ } ) ;
60866093 Ok ( ( ) )
60876094 }
60886095}
@@ -6093,32 +6100,41 @@ impl Readable for ClaimableHTLC {
60936100 let mut value = 0 ;
60946101 let mut payment_data: Option < msgs:: FinalOnionHopData > = None ;
60956102 let mut cltv_expiry = 0 ;
6103+ let mut total_msat = None ;
60966104 let mut keysend_preimage: Option < PaymentPreimage > = None ;
6097- read_tlv_fields !
6098- ( reader,
6099- {
6100- ( 0 , prev_hop, required) , ( 2 , value, required) ,
6101- ( 4 , payment_data, option) , ( 6 , cltv_expiry, required) ,
6102- ( 8 , keysend_preimage, option)
6103- } ) ;
6105+ read_tlv_fields ! ( reader, {
6106+ ( 0 , prev_hop, required) ,
6107+ ( 1 , total_msat, option) ,
6108+ ( 2 , value, required) ,
6109+ ( 4 , payment_data, option) ,
6110+ ( 6 , cltv_expiry, required) ,
6111+ ( 8 , keysend_preimage, option)
6112+ } ) ;
61046113 let onion_payload = match keysend_preimage {
61056114 Some ( p) => {
61066115 if payment_data. is_some ( ) {
61076116 return Err ( DecodeError :: InvalidValue )
61086117 }
6118+ if total_msat. is_none ( ) {
6119+ total_msat = Some ( value) ;
6120+ }
61096121 OnionPayload :: Spontaneous ( p)
61106122 } ,
61116123 None => {
61126124 if payment_data. is_none ( ) {
61136125 return Err ( DecodeError :: InvalidValue )
61146126 }
6115- OnionPayload :: Invoice ( payment_data. unwrap ( ) )
6127+ if total_msat. is_none ( ) {
6128+ total_msat = Some ( payment_data. as_ref ( ) . unwrap ( ) . total_msat ) ;
6129+ }
6130+ OnionPayload :: Invoice { _legacy_hop_data : payment_data. unwrap ( ) }
61166131 } ,
61176132 } ;
61186133 Ok ( Self {
61196134 prev_hop : prev_hop. 0 . unwrap ( ) ,
61206135 timer_ticks : 0 ,
61216136 value,
6137+ total_msat : total_msat. unwrap ( ) ,
61226138 onion_payload,
61236139 cltv_expiry,
61246140 } )
@@ -7319,15 +7335,15 @@ mod tests {
73197335 // payment verification fails as expected.
73207336 let mut bad_payment_hash = payment_hash. clone ( ) ;
73217337 bad_payment_hash. 0 [ 0 ] += 1 ;
7322- match inbound_payment:: verify ( bad_payment_hash, payment_data. clone ( ) , nodes[ 0 ] . node . highest_seen_timestamp . load ( Ordering :: Acquire ) as u64 , & nodes[ 0 ] . node . inbound_payment_key , & nodes[ 0 ] . logger ) {
7338+ match inbound_payment:: verify ( bad_payment_hash, & payment_data, nodes[ 0 ] . node . highest_seen_timestamp . load ( Ordering :: Acquire ) as u64 , & nodes[ 0 ] . node . inbound_payment_key , & nodes[ 0 ] . logger ) {
73237339 Ok ( _) => panic ! ( "Unexpected ok" ) ,
73247340 Err ( ( ) ) => {
73257341 nodes[ 0 ] . logger . assert_log_contains ( "lightning::ln::inbound_payment" . to_string ( ) , "Failing HTLC with user-generated payment_hash" . to_string ( ) , 1 ) ;
73267342 }
73277343 }
73287344
73297345 // Check that using the original payment hash succeeds.
7330- assert ! ( inbound_payment:: verify( payment_hash, payment_data, nodes[ 0 ] . node. highest_seen_timestamp. load( Ordering :: Acquire ) as u64 , & nodes[ 0 ] . node. inbound_payment_key, & nodes[ 0 ] . logger) . is_ok( ) ) ;
7346+ assert ! ( inbound_payment:: verify( payment_hash, & payment_data, nodes[ 0 ] . node. highest_seen_timestamp. load( Ordering :: Acquire ) as u64 , & nodes[ 0 ] . node. inbound_payment_key, & nodes[ 0 ] . logger) . is_ok( ) ) ;
73317347 }
73327348}
73337349
0 commit comments