@@ -16,6 +16,18 @@ use crate::util::ser::{Readable, Writeable, Writer};
16
16
17
17
use core:: convert:: TryFrom ;
18
18
19
+ #[ derive( Debug ) ]
20
+ /// An intermediate node, its outbound channel, and relay parameters.
21
+ pub struct ForwardNode {
22
+ /// The TLVs for this node's [`BlindedHop`], where the fee parameters contained within are also
23
+ /// used for [`BlindedPayInfo`] construction.
24
+ pub tlvs : ForwardTlvs ,
25
+ /// This node's pubkey.
26
+ pub node_id : PublicKey ,
27
+ /// The maximum value, in msat, that may be accepted by this node.
28
+ pub htlc_maximum_msat : u64 ,
29
+ }
30
+
19
31
/// Data to construct a [`BlindedHop`] for forwarding a payment.
20
32
#[ derive( Debug ) ]
21
33
pub struct ForwardTlvs {
@@ -150,12 +162,12 @@ impl Readable for BlindedPaymentTlvs {
150
162
151
163
/// Construct blinded payment hops for the given `intermediate_nodes` and payee info.
152
164
pub ( super ) fn blinded_hops < T : secp256k1:: Signing + secp256k1:: Verification > (
153
- secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ ( PublicKey , ForwardTlvs , u64 ) ] ,
165
+ secp_ctx : & Secp256k1 < T > , intermediate_nodes : & [ ForwardNode ] ,
154
166
payee_node_id : PublicKey , payee_tlvs : ReceiveTlvs , session_priv : & SecretKey
155
167
) -> Result < Vec < BlindedHop > , secp256k1:: Error > {
156
- let pks = intermediate_nodes. iter ( ) . map ( |( pk , _ , _ ) | pk )
168
+ let pks = intermediate_nodes. iter ( ) . map ( |node| & node . node_id )
157
169
. chain ( core:: iter:: once ( & payee_node_id) ) ;
158
- let tlvs = intermediate_nodes. iter ( ) . map ( |( _ , tlvs , _ ) | BlindedPaymentTlvsRef :: Forward ( tlvs) )
170
+ let tlvs = intermediate_nodes. iter ( ) . map ( |node | BlindedPaymentTlvsRef :: Forward ( & node . tlvs ) )
159
171
. chain ( core:: iter:: once ( BlindedPaymentTlvsRef :: Receive ( & payee_tlvs) ) ) ;
160
172
utils:: construct_blinded_hops ( secp_ctx, pks, tlvs, session_priv)
161
173
}
@@ -182,13 +194,12 @@ fn amt_to_forward_msat(inbound_amt_msat: u64, payment_relay: &PaymentRelay) -> O
182
194
}
183
195
184
196
pub ( super ) fn compute_payinfo (
185
- intermediate_nodes : & [ ( PublicKey , ForwardTlvs , u64 ) ] , payee_tlvs : & ReceiveTlvs ,
186
- payee_htlc_maximum_msat : u64
197
+ intermediate_nodes : & [ ForwardNode ] , payee_tlvs : & ReceiveTlvs , payee_htlc_maximum_msat : u64
187
198
) -> Result < BlindedPayInfo , ( ) > {
188
199
let mut curr_base_fee: u64 = 0 ;
189
200
let mut curr_prop_mil: u64 = 0 ;
190
201
let mut cltv_expiry_delta: u16 = 0 ;
191
- for ( _ , tlvs, _ ) in intermediate_nodes. iter ( ) . rev ( ) {
202
+ for tlvs in intermediate_nodes. iter ( ) . rev ( ) . map ( |n| & n . tlvs ) {
192
203
// In the future, we'll want to take the intersection of all supported features for the
193
204
// `BlindedPayInfo`, but there are no features in that context right now.
194
205
if tlvs. features . requires_unknown_bits_from ( & BlindedHopFeatures :: empty ( ) ) { return Err ( ( ) ) }
@@ -216,16 +227,16 @@ pub(super) fn compute_payinfo(
216
227
217
228
let mut htlc_minimum_msat: u64 = 1 ;
218
229
let mut htlc_maximum_msat: u64 = 21_000_000 * 100_000_000 * 1_000 ; // Total bitcoin supply
219
- for ( _ , tlvs , max_htlc_candidate ) in intermediate_nodes. iter ( ) {
230
+ for node in intermediate_nodes. iter ( ) {
220
231
// The min htlc for an intermediate node is that node's min minus the fees charged by all of the
221
232
// following hops for forwarding that min, since that fee amount will automatically be included
222
233
// in the amount that this node receives and contribute towards reaching its min.
223
234
htlc_minimum_msat = amt_to_forward_msat (
224
- core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
225
- & tlvs. payment_relay
235
+ core:: cmp:: max ( node . tlvs . payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
236
+ & node . tlvs . payment_relay
226
237
) . unwrap_or ( 1 ) ; // If underflow occurs, we definitely reached this node's min
227
238
htlc_maximum_msat = amt_to_forward_msat (
228
- core:: cmp:: min ( * max_htlc_candidate , htlc_maximum_msat) , & tlvs. payment_relay
239
+ core:: cmp:: min ( node . htlc_maximum_msat , htlc_maximum_msat) , & node . tlvs . payment_relay
229
240
) . ok_or ( ( ) ) ?; // If underflow occurs, we cannot send to this hop without exceeding their max
230
241
}
231
242
htlc_minimum_msat = core:: cmp:: max (
@@ -258,7 +269,7 @@ impl_writeable_msg!(PaymentConstraints, {
258
269
#[ cfg( test) ]
259
270
mod tests {
260
271
use bitcoin:: secp256k1:: PublicKey ;
261
- use crate :: blinded_path:: payment:: { ForwardTlvs , ReceiveTlvs , PaymentConstraints , PaymentRelay } ;
272
+ use crate :: blinded_path:: payment:: { ForwardNode , ForwardTlvs , ReceiveTlvs , PaymentConstraints , PaymentRelay } ;
262
273
use crate :: ln:: PaymentSecret ;
263
274
use crate :: ln:: features:: BlindedHopFeatures ;
264
275
@@ -267,31 +278,39 @@ mod tests {
267
278
// Taken from the spec example for aggregating blinded payment info. See
268
279
// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md#blinded-payments
269
280
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
270
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
271
- short_channel_id: 0 ,
272
- payment_relay: PaymentRelay {
273
- cltv_expiry_delta: 144 ,
274
- fee_proportional_millionths: 500 ,
275
- fee_base_msat: 100 ,
276
- } ,
277
- payment_constraints: PaymentConstraints {
278
- max_cltv_expiry: 0 ,
279
- htlc_minimum_msat: 100 ,
281
+ let intermediate_nodes = vec ! [ ForwardNode {
282
+ node_id: dummy_pk,
283
+ tlvs: ForwardTlvs {
284
+ short_channel_id: 0 ,
285
+ payment_relay: PaymentRelay {
286
+ cltv_expiry_delta: 144 ,
287
+ fee_proportional_millionths: 500 ,
288
+ fee_base_msat: 100 ,
289
+ } ,
290
+ payment_constraints: PaymentConstraints {
291
+ max_cltv_expiry: 0 ,
292
+ htlc_minimum_msat: 100 ,
293
+ } ,
294
+ features: BlindedHopFeatures :: empty( ) ,
280
295
} ,
281
- features: BlindedHopFeatures :: empty( ) ,
282
- } , u64 :: max_value( ) ) , ( dummy_pk, ForwardTlvs {
283
- short_channel_id: 0 ,
284
- payment_relay: PaymentRelay {
285
- cltv_expiry_delta: 144 ,
286
- fee_proportional_millionths: 500 ,
287
- fee_base_msat: 100 ,
296
+ htlc_maximum_msat: u64 :: max_value( ) ,
297
+ } , ForwardNode {
298
+ node_id: dummy_pk,
299
+ tlvs: ForwardTlvs {
300
+ short_channel_id: 0 ,
301
+ payment_relay: PaymentRelay {
302
+ cltv_expiry_delta: 144 ,
303
+ fee_proportional_millionths: 500 ,
304
+ fee_base_msat: 100 ,
305
+ } ,
306
+ payment_constraints: PaymentConstraints {
307
+ max_cltv_expiry: 0 ,
308
+ htlc_minimum_msat: 1_000 ,
309
+ } ,
310
+ features: BlindedHopFeatures :: empty( ) ,
288
311
} ,
289
- payment_constraints: PaymentConstraints {
290
- max_cltv_expiry: 0 ,
291
- htlc_minimum_msat: 1_000 ,
292
- } ,
293
- features: BlindedHopFeatures :: empty( ) ,
294
- } , u64 :: max_value( ) ) ] ;
312
+ htlc_maximum_msat: u64 :: max_value( ) ,
313
+ } ] ;
295
314
let recv_tlvs = ReceiveTlvs {
296
315
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
297
316
payment_constraints : PaymentConstraints {
@@ -330,31 +349,39 @@ mod tests {
330
349
// If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat
331
350
// along the path.
332
351
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
333
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
334
- short_channel_id: 0 ,
335
- payment_relay: PaymentRelay {
336
- cltv_expiry_delta: 0 ,
337
- fee_proportional_millionths: 0 ,
338
- fee_base_msat: 0 ,
352
+ let intermediate_nodes = vec ! [ ForwardNode {
353
+ node_id: dummy_pk,
354
+ tlvs: ForwardTlvs {
355
+ short_channel_id: 0 ,
356
+ payment_relay: PaymentRelay {
357
+ cltv_expiry_delta: 0 ,
358
+ fee_proportional_millionths: 0 ,
359
+ fee_base_msat: 0 ,
360
+ } ,
361
+ payment_constraints: PaymentConstraints {
362
+ max_cltv_expiry: 0 ,
363
+ htlc_minimum_msat: 1 ,
364
+ } ,
365
+ features: BlindedHopFeatures :: empty( ) ,
339
366
} ,
340
- payment_constraints : PaymentConstraints {
341
- max_cltv_expiry : 0 ,
342
- htlc_minimum_msat : 1 ,
343
- } ,
344
- features : BlindedHopFeatures :: empty ( ) ,
345
- } , u64 :: max_value ( ) ) , ( dummy_pk , ForwardTlvs {
346
- short_channel_id : 0 ,
347
- payment_relay : PaymentRelay {
348
- cltv_expiry_delta : 0 ,
349
- fee_proportional_millionths : 0 ,
350
- fee_base_msat : 0 ,
351
- } ,
352
- payment_constraints : PaymentConstraints {
353
- max_cltv_expiry : 0 ,
354
- htlc_minimum_msat : 2_000 ,
367
+ htlc_maximum_msat : u64 :: max_value ( )
368
+ } , ForwardNode {
369
+ node_id : dummy_pk ,
370
+ tlvs : ForwardTlvs {
371
+ short_channel_id : 0 ,
372
+ payment_relay : PaymentRelay {
373
+ cltv_expiry_delta : 0 ,
374
+ fee_proportional_millionths : 0 ,
375
+ fee_base_msat : 0 ,
376
+ } ,
377
+ payment_constraints : PaymentConstraints {
378
+ max_cltv_expiry : 0 ,
379
+ htlc_minimum_msat : 2_000 ,
380
+ } ,
381
+ features : BlindedHopFeatures :: empty ( ) ,
355
382
} ,
356
- features : BlindedHopFeatures :: empty ( ) ,
357
- } , u64 :: max_value ( ) ) ] ;
383
+ htlc_maximum_msat : u64 :: max_value ( )
384
+ } ] ;
358
385
let recv_tlvs = ReceiveTlvs {
359
386
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
360
387
payment_constraints : PaymentConstraints {
@@ -372,31 +399,39 @@ mod tests {
372
399
// Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the
373
400
// max (htlc_min - following_fees) along the path.
374
401
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
375
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
376
- short_channel_id: 0 ,
377
- payment_relay: PaymentRelay {
378
- cltv_expiry_delta: 0 ,
379
- fee_proportional_millionths: 500 ,
380
- fee_base_msat: 1_000 ,
381
- } ,
382
- payment_constraints: PaymentConstraints {
383
- max_cltv_expiry: 0 ,
384
- htlc_minimum_msat: 5_000 ,
402
+ let intermediate_nodes = vec ! [ ForwardNode {
403
+ node_id: dummy_pk,
404
+ tlvs: ForwardTlvs {
405
+ short_channel_id: 0 ,
406
+ payment_relay: PaymentRelay {
407
+ cltv_expiry_delta: 0 ,
408
+ fee_proportional_millionths: 500 ,
409
+ fee_base_msat: 1_000 ,
410
+ } ,
411
+ payment_constraints: PaymentConstraints {
412
+ max_cltv_expiry: 0 ,
413
+ htlc_minimum_msat: 5_000 ,
414
+ } ,
415
+ features: BlindedHopFeatures :: empty( ) ,
385
416
} ,
386
- features: BlindedHopFeatures :: empty( ) ,
387
- } , u64 :: max_value( ) ) , ( dummy_pk, ForwardTlvs {
388
- short_channel_id: 0 ,
389
- payment_relay: PaymentRelay {
390
- cltv_expiry_delta: 0 ,
391
- fee_proportional_millionths: 500 ,
392
- fee_base_msat: 200 ,
417
+ htlc_maximum_msat: u64 :: max_value( )
418
+ } , ForwardNode {
419
+ node_id: dummy_pk,
420
+ tlvs: ForwardTlvs {
421
+ short_channel_id: 0 ,
422
+ payment_relay: PaymentRelay {
423
+ cltv_expiry_delta: 0 ,
424
+ fee_proportional_millionths: 500 ,
425
+ fee_base_msat: 200 ,
426
+ } ,
427
+ payment_constraints: PaymentConstraints {
428
+ max_cltv_expiry: 0 ,
429
+ htlc_minimum_msat: 2_000 ,
430
+ } ,
431
+ features: BlindedHopFeatures :: empty( ) ,
393
432
} ,
394
- payment_constraints: PaymentConstraints {
395
- max_cltv_expiry: 0 ,
396
- htlc_minimum_msat: 2_000 ,
397
- } ,
398
- features: BlindedHopFeatures :: empty( ) ,
399
- } , u64 :: max_value( ) ) ] ;
433
+ htlc_maximum_msat: u64 :: max_value( )
434
+ } ] ;
400
435
let recv_tlvs = ReceiveTlvs {
401
436
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
402
437
payment_constraints : PaymentConstraints {
@@ -418,31 +453,39 @@ mod tests {
418
453
// Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max
419
454
// htlc ends up as the min (htlc_max - following_fees) along the path.
420
455
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
421
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
422
- short_channel_id: 0 ,
423
- payment_relay: PaymentRelay {
424
- cltv_expiry_delta: 0 ,
425
- fee_proportional_millionths: 500 ,
426
- fee_base_msat: 1_000 ,
456
+ let intermediate_nodes = vec ! [ ForwardNode {
457
+ node_id: dummy_pk,
458
+ tlvs: ForwardTlvs {
459
+ short_channel_id: 0 ,
460
+ payment_relay: PaymentRelay {
461
+ cltv_expiry_delta: 0 ,
462
+ fee_proportional_millionths: 500 ,
463
+ fee_base_msat: 1_000 ,
464
+ } ,
465
+ payment_constraints: PaymentConstraints {
466
+ max_cltv_expiry: 0 ,
467
+ htlc_minimum_msat: 1 ,
468
+ } ,
469
+ features: BlindedHopFeatures :: empty( ) ,
427
470
} ,
428
- payment_constraints : PaymentConstraints {
429
- max_cltv_expiry : 0 ,
430
- htlc_minimum_msat : 1 ,
431
- } ,
432
- features : BlindedHopFeatures :: empty ( ) ,
433
- } , 5_000 ) , ( dummy_pk , ForwardTlvs {
434
- short_channel_id : 0 ,
435
- payment_relay : PaymentRelay {
436
- cltv_expiry_delta : 0 ,
437
- fee_proportional_millionths : 500 ,
438
- fee_base_msat : 1 ,
439
- } ,
440
- payment_constraints : PaymentConstraints {
441
- max_cltv_expiry : 0 ,
442
- htlc_minimum_msat : 1 ,
471
+ htlc_maximum_msat : 5_000 ,
472
+ } , ForwardNode {
473
+ node_id : dummy_pk ,
474
+ tlvs : ForwardTlvs {
475
+ short_channel_id : 0 ,
476
+ payment_relay : PaymentRelay {
477
+ cltv_expiry_delta : 0 ,
478
+ fee_proportional_millionths : 500 ,
479
+ fee_base_msat : 1 ,
480
+ } ,
481
+ payment_constraints : PaymentConstraints {
482
+ max_cltv_expiry : 0 ,
483
+ htlc_minimum_msat : 1 ,
484
+ } ,
485
+ features : BlindedHopFeatures :: empty ( ) ,
443
486
} ,
444
- features : BlindedHopFeatures :: empty ( ) ,
445
- } , 10_000 ) ] ;
487
+ htlc_maximum_msat : 10_000
488
+ } ] ;
446
489
let recv_tlvs = ReceiveTlvs {
447
490
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
448
491
payment_constraints : PaymentConstraints {
0 commit comments