@@ -259,6 +259,80 @@ pub(super) fn build_onion_payloads(
259259 Ok ( ( res, cur_value_msat, cur_cltv) )
260260}
261261
262+ /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
263+ pub ( super ) fn build_trampoline_payloads (
264+ path : & Path , total_msat : u64 , recipient_onion : RecipientOnionFields ,
265+ starting_htlc_offset : u32 , keysend_preimage : & Option < PaymentPreimage > ,
266+ ) -> Result < ( Vec < msgs:: OutboundTrampolinePayload > , u64 , u32 ) , APIError > {
267+ let mut cur_value_msat = 0u64 ;
268+ let mut cur_cltv = starting_htlc_offset;
269+ let mut last_node_id = None ;
270+ let mut res: Vec < msgs:: OutboundTrampolinePayload > = Vec :: with_capacity (
271+ path. trampoline_hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
272+ ) ;
273+
274+ for ( idx, hop) in path. trampoline_hops . iter ( ) . rev ( ) . enumerate ( ) {
275+ // First hop gets special values so that it can check, on receipt, that everything is
276+ // exactly as it should be (and the next hop isn't trying to probe to find out if we're
277+ // the intended recipient).
278+ let value_msat = if cur_value_msat == 0 { hop. fee_msat } else { cur_value_msat } ;
279+ let cltv = if cur_cltv == starting_htlc_offset {
280+ hop. cltv_expiry_delta + starting_htlc_offset
281+ } else {
282+ cur_cltv
283+ } ;
284+ if idx == 0 {
285+ let BlindedTail {
286+ blinding_point,
287+ hops,
288+ final_value_msat,
289+ excess_final_cltv_expiry_delta,
290+ ..
291+ } = path. blinded_tail . as_ref ( ) . ok_or ( APIError :: InvalidRoute {
292+ err : "Trampoline payments must terminate in blinded tails." . to_owned ( ) ,
293+ } ) ?;
294+ let mut blinding_point = Some ( * blinding_point) ;
295+ for ( i, blinded_hop) in hops. iter ( ) . enumerate ( ) {
296+ if i == hops. len ( ) - 1 {
297+ cur_value_msat += final_value_msat;
298+ res. push ( msgs:: OutboundTrampolinePayload :: BlindedReceive {
299+ sender_intended_htlc_amt_msat : * final_value_msat,
300+ total_msat,
301+ cltv_expiry_height : cur_cltv + excess_final_cltv_expiry_delta,
302+ encrypted_tlvs : blinded_hop. encrypted_payload . clone ( ) ,
303+ intro_node_blinding_point : blinding_point. take ( ) ,
304+ keysend_preimage : * keysend_preimage,
305+ custom_tlvs : recipient_onion. custom_tlvs . clone ( ) ,
306+ } ) ;
307+ } else {
308+ res. push ( msgs:: OutboundTrampolinePayload :: BlindedForward {
309+ encrypted_tlvs : blinded_hop. encrypted_payload . clone ( ) ,
310+ intro_node_blinding_point : blinding_point. take ( ) ,
311+ } ) ;
312+ }
313+ }
314+ } else {
315+ let payload = msgs:: OutboundTrampolinePayload :: Forward {
316+ amt_to_forward : value_msat,
317+ outgoing_cltv_value : cltv,
318+ outgoing_node_id : last_node_id
319+ . expect ( "outgoing node id cannot be None after last hop" ) ,
320+ } ;
321+ res. insert ( 0 , payload) ;
322+ }
323+ cur_value_msat += hop. fee_msat ;
324+ if cur_value_msat >= 21000000 * 100000000 * 1000 {
325+ return Err ( APIError :: InvalidRoute { err : "Channel fees overflowed?" . to_owned ( ) } ) ;
326+ }
327+ cur_cltv += hop. cltv_expiry_delta as u32 ;
328+ if cur_cltv >= 500000000 {
329+ return Err ( APIError :: InvalidRoute { err : "Channel CLTV overflowed?" . to_owned ( ) } ) ;
330+ }
331+ last_node_id = Some ( hop. pubkey ) ;
332+ }
333+ Ok ( ( res, cur_value_msat, cur_cltv) )
334+ }
335+
262336/// Length of the onion data packet. Before TLV-based onions this was 20 65-byte hops, though now
263337/// the hops can be of variable length.
264338pub ( crate ) const ONION_DATA_LEN : usize = 20 * 65 ;
0 commit comments