@@ -159,20 +159,26 @@ pub(crate) struct HTLCPreviousHopData {
159
159
}
160
160
161
161
enum 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
+ } ,
166
168
/// Contains the payer-provided preimage.
167
169
Spontaneous ( PaymentPreimage ) ,
168
170
}
169
171
172
+ /// HTLCs that are to us and can be failed/claimed by the user
170
173
struct ClaimableHTLC {
171
174
prev_hop : HTLCPreviousHopData ,
172
175
cltv_expiry : u32 ,
176
+ /// The amount (in msats) of this MPP part
173
177
value : u64 ,
174
178
onion_payload : OnionPayload ,
175
179
timer_ticks : u8 ,
180
+ /// The sum total of all MPP parts
181
+ total_msat : u64 ,
176
182
}
177
183
178
184
/// 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
3096
3102
HTLCForwardInfo :: AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info : PendingHTLCInfo {
3097
3103
routing, incoming_shared_secret, payment_hash, amt_to_forward, .. } ,
3098
3104
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
+ } ,
3102
3110
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 ) ,
3104
3112
_ => {
3105
3113
panic ! ( "short_channel_id == 0 should imply any pending_forward entries are of type Receive" ) ;
3106
3114
}
@@ -3115,6 +3123,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3115
3123
} ,
3116
3124
value : amt_to_forward,
3117
3125
timer_ticks : 0 ,
3126
+ total_msat : if let Some ( data) = & payment_data { data. total_msat } else { amt_to_forward } ,
3118
3127
cltv_expiry,
3119
3128
onion_payload,
3120
3129
} ;
@@ -3138,7 +3147,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3138
3147
}
3139
3148
3140
3149
macro_rules! check_total_value {
3141
- ( $payment_data_total_msat : expr , $payment_secret : expr, $payment_preimage: expr) => { {
3150
+ ( $payment_data : expr, $payment_preimage: expr) => { {
3142
3151
let mut payment_received_generated = false ;
3143
3152
let htlcs = channel_state. claimable_htlcs. entry( payment_hash)
3144
3153
. or_insert( Vec :: new( ) ) ;
@@ -3153,28 +3162,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3153
3162
for htlc in htlcs. iter( ) {
3154
3163
total_value += htlc. value;
3155
3164
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 {
3158
3167
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) ;
3160
3169
total_value = msgs:: MAX_VALUE_MSAT ;
3161
3170
}
3162
3171
if total_value >= msgs:: MAX_VALUE_MSAT { break ; }
3163
3172
} ,
3164
3173
_ => unreachable!( ) ,
3165
3174
}
3166
3175
}
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 {
3168
3177
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 ) ;
3170
3179
fail_htlc!( claimable_htlc) ;
3171
- } else if total_value == $payment_data_total_msat {
3180
+ } else if total_value == $payment_data . total_msat {
3172
3181
htlcs. push( claimable_htlc) ;
3173
3182
new_events. push( events:: Event :: PaymentReceived {
3174
3183
payment_hash,
3175
3184
purpose: events:: PaymentPurpose :: InvoicePayment {
3176
3185
payment_preimage: $payment_preimage,
3177
- payment_secret: $payment_secret,
3186
+ payment_secret: $payment_data . payment_secret,
3178
3187
} ,
3179
3188
amt: total_value,
3180
3189
} ) ;
@@ -3199,17 +3208,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3199
3208
match payment_secrets. entry ( payment_hash) {
3200
3209
hash_map:: Entry :: Vacant ( _) => {
3201
3210
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 ) {
3204
3214
Ok ( payment_preimage) => payment_preimage,
3205
3215
Err ( ( ) ) => {
3206
3216
fail_htlc ! ( claimable_htlc) ;
3207
3217
continue
3208
3218
}
3209
3219
} ;
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) ;
3213
3221
} ,
3214
3222
OnionPayload :: Spontaneous ( preimage) => {
3215
3223
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
3230
3238
}
3231
3239
} ,
3232
3240
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 ( ) ;
3241
3247
if inbound_payment. get ( ) . payment_secret != payment_data. payment_secret {
3242
3248
log_trace ! ( self . logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret." , log_bytes!( payment_hash. 0 ) ) ;
3243
3249
fail_htlc ! ( claimable_htlc) ;
@@ -3246,7 +3252,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3246
3252
log_bytes!( payment_hash. 0 ) , payment_data. total_msat, inbound_payment. get( ) . min_value_msat. unwrap( ) ) ;
3247
3253
fail_htlc ! ( claimable_htlc) ;
3248
3254
} 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) ;
3250
3256
if payment_received_generated {
3251
3257
inbound_payment. remove_entry ( ) ;
3252
3258
}
@@ -3465,10 +3471,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
3465
3471
debug_assert ! ( false ) ;
3466
3472
return false ;
3467
3473
}
3468
- if let OnionPayload :: Invoice ( ref final_hop_data ) = htlcs[ 0 ] . onion_payload {
3474
+ if let OnionPayload :: Invoice { .. } = htlcs[ 0 ] . onion_payload {
3469
3475
// Check if we've received all the parts we need for an MPP (the value of the parts adds to total_msat).
3470
3476
// 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 ) {
3472
3478
return true ;
3473
3479
} else if htlcs. into_iter ( ) . any ( |htlc| {
3474
3480
htlc. timer_ticks += 1 ;
@@ -6069,20 +6075,21 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
6069
6075
impl Writeable for ClaimableHTLC {
6070
6076
fn write < W : Writer > ( & self , writer : & mut W ) -> Result < ( ) , io:: Error > {
6071
6077
let payment_data = match & self . onion_payload {
6072
- OnionPayload :: Invoice ( data ) => Some ( data . clone ( ) ) ,
6078
+ OnionPayload :: Invoice { _legacy_hop_data } => Some ( _legacy_hop_data ) ,
6073
6079
_ => None ,
6074
6080
} ;
6075
6081
let keysend_preimage = match self . onion_payload {
6076
- OnionPayload :: Invoice ( _ ) => None ,
6082
+ OnionPayload :: Invoice { .. } => None ,
6077
6083
OnionPayload :: Spontaneous ( preimage) => Some ( preimage. clone ( ) ) ,
6078
6084
} ;
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
+ } ) ;
6086
6093
Ok ( ( ) )
6087
6094
}
6088
6095
}
@@ -6093,32 +6100,41 @@ impl Readable for ClaimableHTLC {
6093
6100
let mut value = 0 ;
6094
6101
let mut payment_data: Option < msgs:: FinalOnionHopData > = None ;
6095
6102
let mut cltv_expiry = 0 ;
6103
+ let mut total_msat = None ;
6096
6104
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
+ } ) ;
6104
6113
let onion_payload = match keysend_preimage {
6105
6114
Some ( p) => {
6106
6115
if payment_data. is_some ( ) {
6107
6116
return Err ( DecodeError :: InvalidValue )
6108
6117
}
6118
+ if total_msat. is_none ( ) {
6119
+ total_msat = Some ( value) ;
6120
+ }
6109
6121
OnionPayload :: Spontaneous ( p)
6110
6122
} ,
6111
6123
None => {
6112
6124
if payment_data. is_none ( ) {
6113
6125
return Err ( DecodeError :: InvalidValue )
6114
6126
}
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 ( ) }
6116
6131
} ,
6117
6132
} ;
6118
6133
Ok ( Self {
6119
6134
prev_hop : prev_hop. 0 . unwrap ( ) ,
6120
6135
timer_ticks : 0 ,
6121
6136
value,
6137
+ total_msat : total_msat. unwrap ( ) ,
6122
6138
onion_payload,
6123
6139
cltv_expiry,
6124
6140
} )
@@ -7319,15 +7335,15 @@ mod tests {
7319
7335
// payment verification fails as expected.
7320
7336
let mut bad_payment_hash = payment_hash. clone ( ) ;
7321
7337
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 ) {
7323
7339
Ok ( _) => panic ! ( "Unexpected ok" ) ,
7324
7340
Err ( ( ) ) => {
7325
7341
nodes[ 0 ] . logger . assert_log_contains ( "lightning::ln::inbound_payment" . to_string ( ) , "Failing HTLC with user-generated payment_hash" . to_string ( ) , 1 ) ;
7326
7342
}
7327
7343
}
7328
7344
7329
7345
// 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( ) ) ;
7331
7347
}
7332
7348
}
7333
7349
0 commit comments