@@ -144,12 +144,12 @@ impl Readable for BlindedPaymentTlvs {
144144
145145/// Construct blinded payment hops for the given `intermediate_nodes` and payee info.
146146pub ( 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 ) ] ,
148148 payee_node_id : PublicKey , payee_tlvs : ReceiveTlvs , session_priv : & SecretKey
149149) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
150- let pks = intermediate_nodes. iter ( ) . map ( |( pk, _) | pk)
150+ let pks = intermediate_nodes. iter ( ) . map ( |( pk, _, _ ) | pk)
151151 . 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) )
153153 . chain ( core:: iter:: once ( BlindedPaymentTlvsRef :: Receive ( & payee_tlvs) ) ) ;
154154 utils:: construct_blinded_hops ( secp_ctx, pks, tlvs, session_priv)
155155}
@@ -176,12 +176,13 @@ fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> O
176176}
177177
178178pub ( 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
180181) -> Result < BlindedPayInfo , ( ) > {
181182 let mut curr_base_fee: u64 = 0 ;
182183 let mut curr_prop_mil: u64 = 0 ;
183184 let mut cltv_expiry_delta: u16 = 0 ;
184- for ( _, tlvs) in intermediate_nodes. iter ( ) . rev ( ) {
185+ for ( _, tlvs, _ ) in intermediate_nodes. iter ( ) . rev ( ) {
185186 // In the future, we'll want to take the intersection of all supported features for the
186187 // `BlindedPayInfo`, but there are no features in that context right now.
187188 if tlvs. features . requires_unknown_bits_from ( & BlindedHopFeatures :: empty ( ) ) { return Err ( ( ) ) }
@@ -208,25 +209,31 @@ pub(super) fn compute_payinfo(
208209 }
209210
210211 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 ( ) {
212214 // The min htlc for an intermediate node is that node's min minus the fees charged by all of the
213215 // following hops for forwarding that min, since that fee amount will automatically be included
214216 // in the amount that this node receives and contribute towards reaching its min.
215217 htlc_minimum_msat = amt_to_forward_msat (
216218 core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
217219 & tlvs. payment_relay
218220 ) . 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
219224 }
220225 htlc_minimum_msat = core:: cmp:: max (
221226 payee_tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat
222227 ) ;
228+ htlc_maximum_msat = core:: cmp:: min ( payee_htlc_maximum_msat, htlc_maximum_msat) ;
223229
230+ if htlc_maximum_msat < htlc_minimum_msat { return Err ( ( ) ) }
224231 Ok ( BlindedPayInfo {
225232 fee_base_msat : u32:: try_from ( curr_base_fee) . map_err ( |_| ( ) ) ?,
226233 fee_proportional_millionths : u32:: try_from ( curr_prop_mil) . map_err ( |_| ( ) ) ?,
227234 cltv_expiry_delta,
228235 htlc_minimum_msat,
229- htlc_maximum_msat : 21_000_000 * 100_000_000 * 1_000 , // TODO
236+ htlc_maximum_msat,
230237 features : BlindedHopFeatures :: empty ( ) ,
231238 } )
232239}
@@ -266,7 +273,7 @@ mod tests {
266273 htlc_minimum_msat: 100 ,
267274 } ,
268275 features: BlindedHopFeatures :: empty( ) ,
269- } ) , ( dummy_pk, ForwardTlvs {
276+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
270277 short_channel_id: 0 ,
271278 payment_relay: PaymentRelay {
272279 cltv_expiry_delta: 144 ,
@@ -278,19 +285,21 @@ mod tests {
278285 htlc_minimum_msat: 1_000 ,
279286 } ,
280287 features: BlindedHopFeatures :: empty( ) ,
281- } ) ] ;
288+ } , u64 :: max_value ( ) ) ] ;
282289 let recv_tlvs = ReceiveTlvs {
283290 payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
284291 payment_constraints : PaymentConstraints {
285292 max_cltv_expiry : 0 ,
286293 htlc_minimum_msat : 1 ,
287294 } ,
288295 } ;
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 ( ) ;
290298 assert_eq ! ( blinded_payinfo. fee_base_msat, 201 ) ;
291299 assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 1001 ) ;
292300 assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 288 ) ;
293301 assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 900 ) ;
302+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, htlc_maximum_msat) ;
294303 }
295304
296305 #[ test]
@@ -302,11 +311,12 @@ mod tests {
302311 htlc_minimum_msat : 1 ,
303312 } ,
304313 } ;
305- let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs) . unwrap ( ) ;
314+ let blinded_payinfo = super :: compute_payinfo ( & [ ] , & recv_tlvs, 4242 ) . unwrap ( ) ;
306315 assert_eq ! ( blinded_payinfo. fee_base_msat, 0 ) ;
307316 assert_eq ! ( blinded_payinfo. fee_proportional_millionths, 0 ) ;
308317 assert_eq ! ( blinded_payinfo. cltv_expiry_delta, 0 ) ;
309318 assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 1 ) ;
319+ assert_eq ! ( blinded_payinfo. htlc_maximum_msat, 4242 ) ;
310320 }
311321
312322 #[ test]
@@ -326,7 +336,7 @@ mod tests {
326336 htlc_minimum_msat: 1 ,
327337 } ,
328338 features: BlindedHopFeatures :: empty( ) ,
329- } ) , ( dummy_pk, ForwardTlvs {
339+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
330340 short_channel_id: 0 ,
331341 payment_relay: PaymentRelay {
332342 cltv_expiry_delta: 0 ,
@@ -338,15 +348,16 @@ mod tests {
338348 htlc_minimum_msat: 2_000 ,
339349 } ,
340350 features: BlindedHopFeatures :: empty( ) ,
341- } ) ] ;
351+ } , u64 :: max_value ( ) ) ] ;
342352 let recv_tlvs = ReceiveTlvs {
343353 payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
344354 payment_constraints : PaymentConstraints {
345355 max_cltv_expiry : 0 ,
346356 htlc_minimum_msat : 3 ,
347357 } ,
348358 } ;
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 ( ) ;
350361 assert_eq ! ( blinded_payinfo. htlc_minimum_msat, 2_000 ) ;
351362 }
352363
@@ -367,7 +378,7 @@ mod tests {
367378 htlc_minimum_msat: 5_000 ,
368379 } ,
369380 features: BlindedHopFeatures :: empty( ) ,
370- } ) , ( dummy_pk, ForwardTlvs {
381+ } , u64 :: max_value ( ) ) , ( dummy_pk, ForwardTlvs {
371382 short_channel_id: 0 ,
372383 payment_relay: PaymentRelay {
373384 cltv_expiry_delta: 0 ,
@@ -379,7 +390,7 @@ mod tests {
379390 htlc_minimum_msat: 2_000 ,
380391 } ,
381392 features: BlindedHopFeatures :: empty( ) ,
382- } ) ] ;
393+ } , u64 :: max_value ( ) ) ] ;
383394 let recv_tlvs = ReceiveTlvs {
384395 payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
385396 payment_constraints : PaymentConstraints {
@@ -388,7 +399,53 @@ mod tests {
388399 } ,
389400 } ;
390401 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 ( ) ;
392406 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 ) ;
393450 }
394451}
0 commit comments