@@ -144,12 +144,12 @@ impl Readable for BlindedPaymentTlvs {
144
144
145
145
/// Construct blinded payment hops for the given `intermediate_nodes` and payee info.
146
146
pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
147
- secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ ( PublicKey , ForwardTlvs ) ] ,
147
+ secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ ( PublicKey , ForwardTlvs , u64 ) ] ,
148
148
payee_node_id : PublicKey , payee_tlvs : ReceiveTlvs , session_priv : & SecretKey
149
149
) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
150
- let pks = intermediate_nodes. iter ( ) . map ( |( pk, _) | pk)
150
+ let pks = intermediate_nodes. iter ( ) . map ( |( pk, _, _ ) | pk)
151
151
. chain ( core:: iter:: once ( & payee_node_id) ) ;
152
- let tlvs = intermediate_nodes. iter ( ) . map ( |( _, tlvs) | BlindedPaymentTlvsRef :: Forward ( tlvs) )
152
+ let tlvs = intermediate_nodes. iter ( ) . map ( |( _, tlvs, _ ) | BlindedPaymentTlvsRef :: Forward ( tlvs) )
153
153
. chain ( core:: iter:: once ( BlindedPaymentTlvsRef :: Receive ( & payee_tlvs) ) ) ;
154
154
utils:: construct_blinded_hops ( secp_ctx, pks, tlvs, session_priv)
155
155
}
@@ -176,12 +176,13 @@ fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> O
176
176
}
177
177
178
178
pub ( super ) fn compute_payinfo (
179
- intermediate_nodes : & [ ( PublicKey , ForwardTlvs ) ] , payee_tlvs : & ReceiveTlvs
179
+ intermediate_nodes : & [ ( PublicKey , ForwardTlvs , u64 ) ] , payee_tlvs : & ReceiveTlvs ,
180
+ payee_htlc_maximum_msat : u64
180
181
) -> Result < BlindedPayInfo , ( ) > {
181
182
let mut curr_base_fee: u64 = 0 ;
182
183
let mut curr_prop_mil: u64 = 0 ;
183
184
let mut cltv_expiry_delta: u16 = 0 ;
184
- for ( _, tlvs) in intermediate_nodes. iter ( ) . rev ( ) {
185
+ for ( _, tlvs, _ ) in intermediate_nodes. iter ( ) . rev ( ) {
185
186
// In the future, we'll want to take the intersection of all supported features for the
186
187
// `BlindedPayInfo`, but there are no features in that context right now.
187
188
if tlvs. features . requires_unknown_bits_from ( & BlindedHopFeatures :: empty ( ) ) { return Err ( ( ) ) }
@@ -208,25 +209,31 @@ pub(super) fn compute_payinfo(
208
209
}
209
210
210
211
let mut htlc_minimum_msat: u64 = 1 ;
211
- for ( _, tlvs) in intermediate_nodes. iter ( ) {
212
+ let mut htlc_maximum_msat: u64 = 21_000_000 * 100_000_000 * 1_000 ; // Total bitcoin supply
213
+ for ( _, tlvs, max_htlc_candidate) in intermediate_nodes. iter ( ) {
212
214
// The min htlc for an intermediate node is that node's min minus the fees charged by all of the
213
215
// following hops for forwarding that min, since that fee amount will automatically be included
214
216
// in the amount that this node receives and contribute towards reaching its min.
215
217
htlc_minimum_msat = amt_to_forward_msat (
216
218
core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
217
219
& tlvs. payment_relay
218
220
) . unwrap_or ( 1 ) ; // If underflow occurs, we definitely reached this node's min
221
+ htlc_maximum_msat = amt_to_forward_msat (
222
+ core:: cmp:: min ( * max_htlc_candidate, htlc_maximum_msat) , & tlvs. payment_relay
223
+ ) . ok_or ( ( ) ) ?; // If underflow occurs, we cannot send to this hop without exceeding their max
219
224
}
220
225
htlc_minimum_msat = core:: cmp:: max (
221
226
payee_tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat
222
227
) ;
228
+ htlc_maximum_msat = core:: cmp:: min ( payee_htlc_maximum_msat, htlc_maximum_msat) ;
223
229
230
+ if htlc_maximum_msat < htlc_minimum_msat { return Err ( ( ) ) }
224
231
Ok ( BlindedPayInfo {
225
232
fee_base_msat : u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?,
226
233
fee_proportional_millionths : u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?,
227
234
cltv_expiry_delta,
228
235
htlc_minimum_msat,
229
- htlc_maximum_msat : 21_000_000 * 100_000_000 * 1_000 , // TODO
236
+ htlc_maximum_msat,
230
237
features : BlindedHopFeatures :: empty ( ) ,
231
238
} )
232
239
}
@@ -266,7 +273,7 @@ mod tests {
266
273
htlc_minimum_msat: 100 ,
267
274
} ,
268
275
features: BlindedHopFeatures :: empty( ) ,
269
- } ) , ( dummy_pk, ForwardTlvs {
276
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
270
277
short_channel_id: 0 ,
271
278
payment_relay: PaymentRelay {
272
279
cltv_expiry_delta: 144 ,
@@ -278,19 +285,21 @@ mod tests {
278
285
htlc_minimum_msat: 1_000 ,
279
286
} ,
280
287
features: BlindedHopFeatures :: empty( ) ,
281
- } ) ] ;
288
+ } , u64 :: max_value ( ) ) ] ;
282
289
let recv_tlvs = ReceiveTlvs {
283
290
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
284
291
payment_constraints : PaymentConstraints {
285
292
max_cltv_expiry : 0 ,
286
293
htlc_minimum_msat : 1 ,
287
294
} ,
288
295
} ;
289
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
296
+ let htlc_maximum_msat = 100_000 ;
297
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
290
298
assert_eq ! ( blinded_payinfo. fee_base_msat, 201 ) ;
291
299
assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 1001 ) ;
292
300
assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 288 ) ;
293
301
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 900 ) ;
302
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, htlc_maximum_msat) ;
294
303
}
295
304
296
305
#[ test]
@@ -302,11 +311,12 @@ mod tests {
302
311
htlc_minimum_msat : 1 ,
303
312
} ,
304
313
} ;
305
- let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs) . unwrap ( ) ;
314
+ let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs, 4242 ) . unwrap ( ) ;
306
315
assert_eq ! ( blinded_payinfo. fee_base_msat, 0 ) ;
307
316
assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 0 ) ;
308
317
assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 0 ) ;
309
318
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 1 ) ;
319
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, 4242 ) ;
310
320
}
311
321
312
322
#[ test]
@@ -326,7 +336,7 @@ mod tests {
326
336
htlc_minimum_msat: 1 ,
327
337
} ,
328
338
features: BlindedHopFeatures :: empty( ) ,
329
- } ) , ( dummy_pk, ForwardTlvs {
339
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
330
340
short_channel_id: 0 ,
331
341
payment_relay: PaymentRelay {
332
342
cltv_expiry_delta: 0 ,
@@ -338,15 +348,16 @@ mod tests {
338
348
htlc_minimum_msat: 2_000 ,
339
349
} ,
340
350
features: BlindedHopFeatures :: empty( ) ,
341
- } ) ] ;
351
+ } , u64 :: max_value ( ) ) ] ;
342
352
let recv_tlvs = ReceiveTlvs {
343
353
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
344
354
payment_constraints : PaymentConstraints {
345
355
max_cltv_expiry : 0 ,
346
356
htlc_minimum_msat : 3 ,
347
357
} ,
348
358
} ;
349
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
359
+ let htlc_maximum_msat = 100_000 ;
360
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
350
361
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 2_000 ) ;
351
362
}
352
363
@@ -367,7 +378,7 @@ mod tests {
367
378
htlc_minimum_msat: 5_000 ,
368
379
} ,
369
380
features: BlindedHopFeatures :: empty( ) ,
370
- } ) , ( dummy_pk, ForwardTlvs {
381
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
371
382
short_channel_id: 0 ,
372
383
payment_relay: PaymentRelay {
373
384
cltv_expiry_delta: 0 ,
@@ -379,7 +390,7 @@ mod tests {
379
390
htlc_minimum_msat: 2_000 ,
380
391
} ,
381
392
features: BlindedHopFeatures :: empty( ) ,
382
- } ) ] ;
393
+ } , u64 :: max_value ( ) ) ] ;
383
394
let recv_tlvs = ReceiveTlvs {
384
395
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
385
396
payment_constraints : PaymentConstraints {
@@ -388,7 +399,53 @@ mod tests {
388
399
} ,
389
400
} ;
390
401
let htlc_minimum_msat = 3798 ;
391
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
402
+ assert ! ( super :: compute_payinfo( & intermediate_nodes[ ..] , & recv_tlvs, htlc_minimum_msat - 1 ) . is_err( ) ) ;
403
+
404
+ let htlc_maximum_msat = htlc_minimum_msat + 1 ;
405
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
392
406
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, htlc_minimum_msat) ;
407
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, htlc_maximum_msat) ;
408
+ }
409
+
410
+ #[ test]
411
+ fn aggregated_htlc_max ( ) {
412
+ // Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max
413
+ // htlc ends up as the min (htlc_max - following_fees) along the path.
414
+ let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
415
+ let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
416
+ short_channel_id: 0 ,
417
+ payment_relay: PaymentRelay {
418
+ cltv_expiry_delta: 0 ,
419
+ fee_proportional_millionths: 500 ,
420
+ fee_base_msat: 1_000 ,
421
+ } ,
422
+ payment_constraints: PaymentConstraints {
423
+ max_cltv_expiry: 0 ,
424
+ htlc_minimum_msat: 1 ,
425
+ } ,
426
+ features: BlindedHopFeatures :: empty( ) ,
427
+ } , 5_000 ) , ( dummy_pk, ForwardTlvs {
428
+ short_channel_id: 0 ,
429
+ payment_relay: PaymentRelay {
430
+ cltv_expiry_delta: 0 ,
431
+ fee_proportional_millionths: 500 ,
432
+ fee_base_msat: 1 ,
433
+ } ,
434
+ payment_constraints: PaymentConstraints {
435
+ max_cltv_expiry: 0 ,
436
+ htlc_minimum_msat: 1 ,
437
+ } ,
438
+ features: BlindedHopFeatures :: empty( ) ,
439
+ } , 10_000 ) ] ;
440
+ let recv_tlvs = ReceiveTlvs {
441
+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
442
+ payment_constraints : PaymentConstraints {
443
+ max_cltv_expiry : 0 ,
444
+ htlc_minimum_msat : 1 ,
445
+ } ,
446
+ } ;
447
+
448
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, 10_000 ) . unwrap ( ) ;
449
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, 3997 ) ;
393
450
}
394
451
}
0 commit comments