@@ -317,6 +317,80 @@ pub(super) fn build_onion_payloads(
317
317
Ok ( ( res, cur_value_msat, cur_cltv) )
318
318
}
319
319
320
+ /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
321
+ pub ( super ) fn build_trampoline_payloads (
322
+ path : & Path , total_msat : u64 , mut recipient_onion : RecipientOnionFields ,
323
+ starting_htlc_offset : u32 , keysend_preimage : & Option < PaymentPreimage > ,
324
+ ) -> Result < ( Vec < msgs:: OutboundTrampolinePayload > , u64 , u32 ) , APIError > {
325
+ let mut cur_value_msat = 0u64 ;
326
+ let mut cur_cltv = starting_htlc_offset;
327
+ let mut last_node_id = None ;
328
+ let mut res: Vec < msgs:: OutboundTrampolinePayload > = Vec :: with_capacity (
329
+ path. trampoline_hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
330
+ ) ;
331
+
332
+ for ( idx, hop) in path. trampoline_hops . iter ( ) . rev ( ) . enumerate ( ) {
333
+ // First hop gets special values so that it can check, on receipt, that everything is
334
+ // exactly as it should be (and the next hop isn't trying to probe to find out if we're
335
+ // the intended recipient).
336
+ let value_msat = if cur_value_msat == 0 { hop. fee_msat } else { cur_value_msat } ;
337
+ let cltv = if cur_cltv == starting_htlc_offset {
338
+ hop. cltv_expiry_delta + starting_htlc_offset
339
+ } else {
340
+ cur_cltv
341
+ } ;
342
+ if idx == 0 {
343
+ let BlindedTail {
344
+ blinding_point,
345
+ hops,
346
+ final_value_msat,
347
+ excess_final_cltv_expiry_delta,
348
+ ..
349
+ } = path. blinded_tail . as_ref ( ) . ok_or ( APIError :: InvalidRoute {
350
+ err : "Trampoline payments must terminate in blinded tails." . to_owned ( ) ,
351
+ } ) ?;
352
+ let mut blinding_point = Some ( * blinding_point) ;
353
+ for ( i, blinded_hop) in hops. iter ( ) . enumerate ( ) {
354
+ if i == hops. len ( ) - 1 {
355
+ cur_value_msat += final_value_msat;
356
+ res. push ( msgs:: OutboundTrampolinePayload :: BlindedReceive {
357
+ sender_intended_htlc_amt_msat : * final_value_msat,
358
+ total_msat,
359
+ cltv_expiry_height : cur_cltv + excess_final_cltv_expiry_delta,
360
+ encrypted_tlvs : blinded_hop. encrypted_payload . clone ( ) ,
361
+ intro_node_blinding_point : blinding_point. take ( ) ,
362
+ keysend_preimage : * keysend_preimage,
363
+ custom_tlvs : recipient_onion. custom_tlvs . clone ( ) ,
364
+ } ) ;
365
+ } else {
366
+ res. push ( msgs:: OutboundTrampolinePayload :: BlindedForward {
367
+ encrypted_tlvs : blinded_hop. encrypted_payload . clone ( ) ,
368
+ intro_node_blinding_point : blinding_point. take ( ) ,
369
+ } ) ;
370
+ }
371
+ }
372
+ } else {
373
+ let payload = msgs:: OutboundTrampolinePayload :: Forward {
374
+ amt_to_forward : value_msat,
375
+ outgoing_cltv_value : cltv,
376
+ outgoing_node_id : last_node_id
377
+ . expect ( "outgoing node id cannot be None after last hop" ) ,
378
+ } ;
379
+ res. insert ( 0 , payload) ;
380
+ }
381
+ cur_value_msat += hop. fee_msat ;
382
+ if cur_value_msat >= 21000000 * 100000000 * 1000 {
383
+ return Err ( APIError :: InvalidRoute { err : "Channel fees overflowed?" . to_owned ( ) } ) ;
384
+ }
385
+ cur_cltv += hop. cltv_expiry_delta as u32 ;
386
+ if cur_cltv >= 500000000 {
387
+ return Err ( APIError :: InvalidRoute { err : "Channel CLTV overflowed?" . to_owned ( ) } ) ;
388
+ }
389
+ last_node_id = Some ( hop. pubkey ) ;
390
+ }
391
+ Ok ( ( res, cur_value_msat, cur_cltv) )
392
+ }
393
+
320
394
/// Length of the onion data packet. Before TLV-based onions this was 20 65-byte hops, though now
321
395
/// the hops can be of variable length.
322
396
pub ( crate ) const ONION_DATA_LEN : usize = 20 * 65 ;
0 commit comments