@@ -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 ( ( ) ) }
@@ -207,25 +208,31 @@ pub(super) fn compute_payinfo(
207
208
}
208
209
209
210
let mut htlc_minimum_msat: u64 = 1 ;
210
- for ( _, tlvs) in intermediate_nodes. iter ( ) {
211
+ let mut htlc_maximum_msat: u64 = 21_000_000 * 100_000_000 * 1_000 ; // Total bitcoin supply
212
+ for ( _, tlvs, max_htlc_candidate) in intermediate_nodes. iter ( ) {
211
213
// The min htlc for an intermediate node is that node's min minus the fees charged by all of the
212
214
// following hops for forwarding that min, since that fee amount will automatically be included
213
215
// in the amount that this node receives and contribute towards reaching its min.
214
216
htlc_minimum_msat = amt_to_forward_msat (
215
217
core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
216
218
& tlvs. payment_relay
217
219
) . unwrap_or ( 1 ) ; // If underflow occurs, we definitely reached this node's min
220
+ htlc_maximum_msat = amt_to_forward_msat (
221
+ core:: cmp:: min ( * max_htlc_candidate, htlc_maximum_msat) , & tlvs. payment_relay
222
+ ) . ok_or ( ( ) ) ?; // If underflow occurs, we cannot send to this hop without exceeding their max
218
223
}
219
224
htlc_minimum_msat = core:: cmp:: max (
220
225
payee_tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat
221
226
) ;
227
+ htlc_maximum_msat = core:: cmp:: min ( payee_htlc_maximum_msat, htlc_maximum_msat) ;
222
228
229
+ if htlc_maximum_msat < htlc_minimum_msat { return Err ( ( ) ) }
223
230
Ok ( BlindedPayInfo {
224
231
fee_base_msat : u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?,
225
232
fee_proportional_millionths : u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?,
226
233
cltv_expiry_delta,
227
234
htlc_minimum_msat,
228
- htlc_maximum_msat : 21_000_000 * 100_000_000 * 1_000 , // TODO
235
+ htlc_maximum_msat,
229
236
features : BlindedHopFeatures :: empty ( ) ,
230
237
} )
231
238
}
@@ -265,7 +272,7 @@ mod tests {
265
272
htlc_minimum_msat: 100 ,
266
273
} ,
267
274
features: BlindedHopFeatures :: empty( ) ,
268
- } ) , ( dummy_pk, ForwardTlvs {
275
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
269
276
short_channel_id: 0 ,
270
277
payment_relay: PaymentRelay {
271
278
cltv_expiry_delta: 144 ,
@@ -277,19 +284,21 @@ mod tests {
277
284
htlc_minimum_msat: 1_000 ,
278
285
} ,
279
286
features: BlindedHopFeatures :: empty( ) ,
280
- } ) ] ;
287
+ } , u64 :: max_value ( ) ) ] ;
281
288
let recv_tlvs = ReceiveTlvs {
282
289
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
283
290
payment_constraints : PaymentConstraints {
284
291
max_cltv_expiry : 0 ,
285
292
htlc_minimum_msat : 1 ,
286
293
} ,
287
294
} ;
288
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
295
+ let htlc_maximum_msat = 100_000 ;
296
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
289
297
assert_eq ! ( blinded_payinfo. fee_base_msat, 201 ) ;
290
298
assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 1001 ) ;
291
299
assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 288 ) ;
292
300
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 900 ) ;
301
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, htlc_maximum_msat) ;
293
302
}
294
303
295
304
#[ test]
@@ -301,11 +310,12 @@ mod tests {
301
310
htlc_minimum_msat : 1 ,
302
311
} ,
303
312
} ;
304
- let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs) . unwrap ( ) ;
313
+ let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs, 4242 ) . unwrap ( ) ;
305
314
assert_eq ! ( blinded_payinfo. fee_base_msat, 0 ) ;
306
315
assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 0 ) ;
307
316
assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 0 ) ;
308
317
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 1 ) ;
318
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, 4242 ) ;
309
319
}
310
320
311
321
#[ test]
@@ -325,7 +335,7 @@ mod tests {
325
335
htlc_minimum_msat: 1 ,
326
336
} ,
327
337
features: BlindedHopFeatures :: empty( ) ,
328
- } ) , ( dummy_pk, ForwardTlvs {
338
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
329
339
short_channel_id: 0 ,
330
340
payment_relay: PaymentRelay {
331
341
cltv_expiry_delta: 0 ,
@@ -337,15 +347,16 @@ mod tests {
337
347
htlc_minimum_msat: 2_000 ,
338
348
} ,
339
349
features: BlindedHopFeatures :: empty( ) ,
340
- } ) ] ;
350
+ } , u64 :: max_value ( ) ) ] ;
341
351
let recv_tlvs = ReceiveTlvs {
342
352
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
343
353
payment_constraints : PaymentConstraints {
344
354
max_cltv_expiry : 0 ,
345
355
htlc_minimum_msat : 3 ,
346
356
} ,
347
357
} ;
348
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
358
+ let htlc_maximum_msat = 100_000 ;
359
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
349
360
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 2_000 ) ;
350
361
}
351
362
@@ -366,7 +377,7 @@ mod tests {
366
377
htlc_minimum_msat: 5_000 ,
367
378
} ,
368
379
features: BlindedHopFeatures :: empty( ) ,
369
- } ) , ( dummy_pk, ForwardTlvs {
380
+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
370
381
short_channel_id: 0 ,
371
382
payment_relay: PaymentRelay {
372
383
cltv_expiry_delta: 0 ,
@@ -378,7 +389,7 @@ mod tests {
378
389
htlc_minimum_msat: 2_000 ,
379
390
} ,
380
391
features: BlindedHopFeatures :: empty( ) ,
381
- } ) ] ;
392
+ } , u64 :: max_value ( ) ) ] ;
382
393
let recv_tlvs = ReceiveTlvs {
383
394
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
384
395
payment_constraints : PaymentConstraints {
@@ -387,7 +398,53 @@ mod tests {
387
398
} ,
388
399
} ;
389
400
let htlc_minimum_msat = 3798 ;
390
- let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs) . unwrap ( ) ;
401
+ assert ! ( super :: compute_payinfo( & intermediate_nodes[ ..] , & recv_tlvs, htlc_minimum_msat - 1 ) . is_err( ) ) ;
402
+
403
+ let htlc_maximum_msat = htlc_minimum_msat + 1 ;
404
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, htlc_maximum_msat) . unwrap ( ) ;
391
405
assert_eq ! ( blinded_payinfo. htlc_minimum_msat, htlc_minimum_msat) ;
406
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, htlc_maximum_msat) ;
407
+ }
408
+
409
+ #[ test]
410
+ fn aggregated_htlc_max ( ) {
411
+ // Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max
412
+ // htlc ends up as the min (htlc_max - following_fees) along the path.
413
+ let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
414
+ let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
415
+ short_channel_id: 0 ,
416
+ payment_relay: PaymentRelay {
417
+ cltv_expiry_delta: 0 ,
418
+ fee_proportional_millionths: 500 ,
419
+ fee_base_msat: 1_000 ,
420
+ } ,
421
+ payment_constraints: PaymentConstraints {
422
+ max_cltv_expiry: 0 ,
423
+ htlc_minimum_msat: 1 ,
424
+ } ,
425
+ features: BlindedHopFeatures :: empty( ) ,
426
+ } , 5_000 ) , ( dummy_pk, ForwardTlvs {
427
+ short_channel_id: 0 ,
428
+ payment_relay: PaymentRelay {
429
+ cltv_expiry_delta: 0 ,
430
+ fee_proportional_millionths: 500 ,
431
+ fee_base_msat: 1 ,
432
+ } ,
433
+ payment_constraints: PaymentConstraints {
434
+ max_cltv_expiry: 0 ,
435
+ htlc_minimum_msat: 1 ,
436
+ } ,
437
+ features: BlindedHopFeatures :: empty( ) ,
438
+ } , 10_000 ) ] ;
439
+ let recv_tlvs = ReceiveTlvs {
440
+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
441
+ payment_constraints : PaymentConstraints {
442
+ max_cltv_expiry : 0 ,
443
+ htlc_minimum_msat : 1 ,
444
+ } ,
445
+ } ;
446
+
447
+ let blinded_payinfo = super :: compute_payinfo ( & intermediate_nodes[ ..] , & recv_tlvs, 10_000 ) . unwrap ( ) ;
448
+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, 3997 ) ;
392
449
}
393
450
}
0 commit comments