@@ -16,6 +16,18 @@ use crate::util::ser::{Readable, Writeable, Writer};
16
16
17
17
use core:: convert:: TryFrom ;
18
18
19
+ /// An intermediate node, its outbound channel, and relay parameters.
20
+ #[ derive( Clone , Debug ) ]
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( Clone , 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 ( ( ) ) }
@@ -215,16 +226,16 @@ pub(super) fn compute_payinfo(
215
226
216
227
let mut htlc_minimum_msat: u64 = 1 ;
217
228
let mut htlc_maximum_msat: u64 = 21_000_000 * 100_000_000 * 1_000 ; // Total bitcoin supply
218
- for ( _ , tlvs , max_htlc_candidate ) in intermediate_nodes. iter ( ) {
229
+ for node in intermediate_nodes. iter ( ) {
219
230
// The min htlc for an intermediate node is that node's min minus the fees charged by all of the
220
231
// following hops for forwarding that min, since that fee amount will automatically be included
221
232
// in the amount that this node receives and contribute towards reaching its min.
222
233
htlc_minimum_msat = amt_to_forward_msat (
223
- core:: cmp:: max ( tlvs. payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
224
- & tlvs. payment_relay
234
+ core:: cmp:: max ( node . tlvs . payment_constraints . htlc_minimum_msat , htlc_minimum_msat) ,
235
+ & node . tlvs . payment_relay
225
236
) . unwrap_or ( 1 ) ; // If underflow occurs, we definitely reached this node's min
226
237
htlc_maximum_msat = amt_to_forward_msat (
227
- core:: cmp:: min ( * max_htlc_candidate , htlc_maximum_msat) , & tlvs. payment_relay
238
+ core:: cmp:: min ( node . htlc_maximum_msat , htlc_maximum_msat) , & node . tlvs . payment_relay
228
239
) . ok_or ( ( ) ) ?; // If underflow occurs, we cannot send to this hop without exceeding their max
229
240
}
230
241
htlc_minimum_msat = core:: cmp:: max (
@@ -257,7 +268,7 @@ impl_writeable_msg!(PaymentConstraints, {
257
268
#[ cfg( test) ]
258
269
mod tests {
259
270
use bitcoin:: secp256k1:: PublicKey ;
260
- use crate :: blinded_path:: payment:: { ForwardTlvs , ReceiveTlvs , PaymentConstraints , PaymentRelay } ;
271
+ use crate :: blinded_path:: payment:: { ForwardNode , ForwardTlvs , ReceiveTlvs , PaymentConstraints , PaymentRelay } ;
261
272
use crate :: ln:: PaymentSecret ;
262
273
use crate :: ln:: features:: BlindedHopFeatures ;
263
274
@@ -266,31 +277,39 @@ mod tests {
266
277
// Taken from the spec example for aggregating blinded payment info. See
267
278
// https://github.com/lightning/bolts/blob/master/proposals/route-blinding.md#blinded-payments
268
279
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
269
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
270
- short_channel_id: 0 ,
271
- payment_relay: PaymentRelay {
272
- cltv_expiry_delta: 144 ,
273
- fee_proportional_millionths: 500 ,
274
- fee_base_msat: 100 ,
275
- } ,
276
- payment_constraints: PaymentConstraints {
277
- max_cltv_expiry: 0 ,
278
- htlc_minimum_msat: 100 ,
280
+ let intermediate_nodes = vec ! [ ForwardNode {
281
+ node_id: dummy_pk,
282
+ tlvs: 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 ,
288
+ } ,
289
+ payment_constraints: PaymentConstraints {
290
+ max_cltv_expiry: 0 ,
291
+ htlc_minimum_msat: 100 ,
292
+ } ,
293
+ features: BlindedHopFeatures :: empty( ) ,
279
294
} ,
280
- features: BlindedHopFeatures :: empty( ) ,
281
- } , u64 :: max_value( ) ) , ( dummy_pk, ForwardTlvs {
282
- short_channel_id: 0 ,
283
- payment_relay: PaymentRelay {
284
- cltv_expiry_delta: 144 ,
285
- fee_proportional_millionths: 500 ,
286
- fee_base_msat: 100 ,
295
+ htlc_maximum_msat: u64 :: max_value( ) ,
296
+ } , ForwardNode {
297
+ node_id: dummy_pk,
298
+ tlvs: ForwardTlvs {
299
+ short_channel_id: 0 ,
300
+ payment_relay: PaymentRelay {
301
+ cltv_expiry_delta: 144 ,
302
+ fee_proportional_millionths: 500 ,
303
+ fee_base_msat: 100 ,
304
+ } ,
305
+ payment_constraints: PaymentConstraints {
306
+ max_cltv_expiry: 0 ,
307
+ htlc_minimum_msat: 1_000 ,
308
+ } ,
309
+ features: BlindedHopFeatures :: empty( ) ,
287
310
} ,
288
- payment_constraints: PaymentConstraints {
289
- max_cltv_expiry: 0 ,
290
- htlc_minimum_msat: 1_000 ,
291
- } ,
292
- features: BlindedHopFeatures :: empty( ) ,
293
- } , u64 :: max_value( ) ) ] ;
311
+ htlc_maximum_msat: u64 :: max_value( ) ,
312
+ } ] ;
294
313
let recv_tlvs = ReceiveTlvs {
295
314
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
296
315
payment_constraints : PaymentConstraints {
@@ -329,31 +348,39 @@ mod tests {
329
348
// If no hops charge fees, the htlc_minimum_msat should just be the maximum htlc_minimum_msat
330
349
// along the path.
331
350
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
332
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
333
- short_channel_id: 0 ,
334
- payment_relay: PaymentRelay {
335
- cltv_expiry_delta: 0 ,
336
- fee_proportional_millionths: 0 ,
337
- fee_base_msat: 0 ,
351
+ let intermediate_nodes = vec ! [ ForwardNode {
352
+ node_id: dummy_pk,
353
+ tlvs: ForwardTlvs {
354
+ short_channel_id: 0 ,
355
+ payment_relay: PaymentRelay {
356
+ cltv_expiry_delta: 0 ,
357
+ fee_proportional_millionths: 0 ,
358
+ fee_base_msat: 0 ,
359
+ } ,
360
+ payment_constraints: PaymentConstraints {
361
+ max_cltv_expiry: 0 ,
362
+ htlc_minimum_msat: 1 ,
363
+ } ,
364
+ features: BlindedHopFeatures :: empty( ) ,
338
365
} ,
339
- payment_constraints : PaymentConstraints {
340
- max_cltv_expiry : 0 ,
341
- htlc_minimum_msat : 1 ,
342
- } ,
343
- features : BlindedHopFeatures :: empty ( ) ,
344
- } , u64 :: max_value ( ) ) , ( dummy_pk , ForwardTlvs {
345
- short_channel_id : 0 ,
346
- payment_relay : PaymentRelay {
347
- cltv_expiry_delta : 0 ,
348
- fee_proportional_millionths : 0 ,
349
- fee_base_msat : 0 ,
350
- } ,
351
- payment_constraints : PaymentConstraints {
352
- max_cltv_expiry : 0 ,
353
- htlc_minimum_msat : 2_000 ,
366
+ htlc_maximum_msat : u64 :: max_value ( )
367
+ } , ForwardNode {
368
+ node_id : dummy_pk ,
369
+ tlvs : ForwardTlvs {
370
+ short_channel_id : 0 ,
371
+ payment_relay : PaymentRelay {
372
+ cltv_expiry_delta : 0 ,
373
+ fee_proportional_millionths : 0 ,
374
+ fee_base_msat : 0 ,
375
+ } ,
376
+ payment_constraints : PaymentConstraints {
377
+ max_cltv_expiry : 0 ,
378
+ htlc_minimum_msat : 2_000 ,
379
+ } ,
380
+ features : BlindedHopFeatures :: empty ( ) ,
354
381
} ,
355
- features : BlindedHopFeatures :: empty ( ) ,
356
- } , u64 :: max_value ( ) ) ] ;
382
+ htlc_maximum_msat : u64 :: max_value ( )
383
+ } ] ;
357
384
let recv_tlvs = ReceiveTlvs {
358
385
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
359
386
payment_constraints : PaymentConstraints {
@@ -371,31 +398,39 @@ mod tests {
371
398
// Create a path with varying fees and htlc_mins, and make sure htlc_minimum_msat ends up as the
372
399
// max (htlc_min - following_fees) along the path.
373
400
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
374
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
375
- short_channel_id: 0 ,
376
- payment_relay: PaymentRelay {
377
- cltv_expiry_delta: 0 ,
378
- fee_proportional_millionths: 500 ,
379
- fee_base_msat: 1_000 ,
380
- } ,
381
- payment_constraints: PaymentConstraints {
382
- max_cltv_expiry: 0 ,
383
- htlc_minimum_msat: 5_000 ,
401
+ let intermediate_nodes = vec ! [ ForwardNode {
402
+ node_id: dummy_pk,
403
+ tlvs: ForwardTlvs {
404
+ short_channel_id: 0 ,
405
+ payment_relay: PaymentRelay {
406
+ cltv_expiry_delta: 0 ,
407
+ fee_proportional_millionths: 500 ,
408
+ fee_base_msat: 1_000 ,
409
+ } ,
410
+ payment_constraints: PaymentConstraints {
411
+ max_cltv_expiry: 0 ,
412
+ htlc_minimum_msat: 5_000 ,
413
+ } ,
414
+ features: BlindedHopFeatures :: empty( ) ,
384
415
} ,
385
- features: BlindedHopFeatures :: empty( ) ,
386
- } , u64 :: max_value( ) ) , ( dummy_pk, ForwardTlvs {
387
- short_channel_id: 0 ,
388
- payment_relay: PaymentRelay {
389
- cltv_expiry_delta: 0 ,
390
- fee_proportional_millionths: 500 ,
391
- fee_base_msat: 200 ,
416
+ htlc_maximum_msat: u64 :: max_value( )
417
+ } , ForwardNode {
418
+ node_id: dummy_pk,
419
+ tlvs: ForwardTlvs {
420
+ short_channel_id: 0 ,
421
+ payment_relay: PaymentRelay {
422
+ cltv_expiry_delta: 0 ,
423
+ fee_proportional_millionths: 500 ,
424
+ fee_base_msat: 200 ,
425
+ } ,
426
+ payment_constraints: PaymentConstraints {
427
+ max_cltv_expiry: 0 ,
428
+ htlc_minimum_msat: 2_000 ,
429
+ } ,
430
+ features: BlindedHopFeatures :: empty( ) ,
392
431
} ,
393
- payment_constraints: PaymentConstraints {
394
- max_cltv_expiry: 0 ,
395
- htlc_minimum_msat: 2_000 ,
396
- } ,
397
- features: BlindedHopFeatures :: empty( ) ,
398
- } , u64 :: max_value( ) ) ] ;
432
+ htlc_maximum_msat: u64 :: max_value( )
433
+ } ] ;
399
434
let recv_tlvs = ReceiveTlvs {
400
435
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
401
436
payment_constraints : PaymentConstraints {
@@ -417,31 +452,39 @@ mod tests {
417
452
// Create a path with varying fees and `htlc_maximum_msat`s, and make sure the aggregated max
418
453
// htlc ends up as the min (htlc_max - following_fees) along the path.
419
454
let dummy_pk = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
420
- let intermediate_nodes = vec ! [ ( dummy_pk, ForwardTlvs {
421
- short_channel_id: 0 ,
422
- payment_relay: PaymentRelay {
423
- cltv_expiry_delta: 0 ,
424
- fee_proportional_millionths: 500 ,
425
- fee_base_msat: 1_000 ,
455
+ let intermediate_nodes = vec ! [ ForwardNode {
456
+ node_id: dummy_pk,
457
+ tlvs: ForwardTlvs {
458
+ short_channel_id: 0 ,
459
+ payment_relay: PaymentRelay {
460
+ cltv_expiry_delta: 0 ,
461
+ fee_proportional_millionths: 500 ,
462
+ fee_base_msat: 1_000 ,
463
+ } ,
464
+ payment_constraints: PaymentConstraints {
465
+ max_cltv_expiry: 0 ,
466
+ htlc_minimum_msat: 1 ,
467
+ } ,
468
+ features: BlindedHopFeatures :: empty( ) ,
426
469
} ,
427
- payment_constraints : PaymentConstraints {
428
- max_cltv_expiry : 0 ,
429
- htlc_minimum_msat : 1 ,
430
- } ,
431
- features : BlindedHopFeatures :: empty ( ) ,
432
- } , 5_000 ) , ( dummy_pk , ForwardTlvs {
433
- short_channel_id : 0 ,
434
- payment_relay : PaymentRelay {
435
- cltv_expiry_delta : 0 ,
436
- fee_proportional_millionths : 500 ,
437
- fee_base_msat : 1 ,
438
- } ,
439
- payment_constraints : PaymentConstraints {
440
- max_cltv_expiry : 0 ,
441
- htlc_minimum_msat : 1 ,
470
+ htlc_maximum_msat : 5_000 ,
471
+ } , ForwardNode {
472
+ node_id : dummy_pk ,
473
+ tlvs : ForwardTlvs {
474
+ short_channel_id : 0 ,
475
+ payment_relay : PaymentRelay {
476
+ cltv_expiry_delta : 0 ,
477
+ fee_proportional_millionths : 500 ,
478
+ fee_base_msat : 1 ,
479
+ } ,
480
+ payment_constraints : PaymentConstraints {
481
+ max_cltv_expiry : 0 ,
482
+ htlc_minimum_msat : 1 ,
483
+ } ,
484
+ features : BlindedHopFeatures :: empty ( ) ,
442
485
} ,
443
- features : BlindedHopFeatures :: empty ( ) ,
444
- } , 10_000 ) ] ;
486
+ htlc_maximum_msat : 10_000
487
+ } ] ;
445
488
let recv_tlvs = ReceiveTlvs {
446
489
payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
447
490
payment_constraints : PaymentConstraints {
0 commit comments