@@ -33,10 +33,10 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
3333use bitcoin:: secp256k1:: { PublicKey , Scalar , Secp256k1 , SecretKey } ;
3434
3535use crate :: io:: { Cursor , Read } ;
36- use core:: ops:: Deref ;
37-
36+ use crate :: ln:: msgs:: OutboundOnionPayload ;
3837#[ allow( unused_imports) ]
3938use crate :: prelude:: * ;
39+ use core:: ops:: Deref ;
4040
4141pub ( crate ) struct OnionKeys {
4242 #[ cfg( test) ]
@@ -367,12 +367,18 @@ pub(super) fn build_onion_payloads<'a>(
367367 let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
368368 path. hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
369369 ) ;
370- let blinded_tail_with_hop_iter = path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
371- hops : bt. hops . iter ( ) ,
372- blinding_point : bt. blinding_point ,
373- final_value_msat : bt. final_value_msat ,
374- excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
375- } ) ;
370+
371+ // don't include blinded tail when Trampoline hops are present
372+ let blinded_tail_with_hop_iter = if path. trampoline_hops . is_empty ( ) {
373+ path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
374+ hops : bt. hops . iter ( ) ,
375+ blinding_point : bt. blinding_point ,
376+ final_value_msat : bt. final_value_msat ,
377+ excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
378+ } )
379+ } else {
380+ None
381+ } ;
376382
377383 let ( value_msat, cltv) = build_onion_payloads_callback (
378384 path. hops . iter ( ) ,
@@ -604,7 +610,6 @@ pub(super) fn construct_onion_packet(
604610 )
605611}
606612
607- #[ allow( unused) ]
608613pub ( super ) fn construct_trampoline_onion_packet (
609614 payloads : Vec < msgs:: OutboundTrampolinePayload > , onion_keys : Vec < OnionKeys > ,
610615 prng_seed : [ u8 ; 32 ] , associated_data : & PaymentHash , length : Option < u16 > ,
@@ -1361,17 +1366,73 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
13611366 keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
13621367 prng_seed : [ u8 ; 32 ] ,
13631368) -> Result < ( msgs:: OnionPacket , u64 , u32 ) , APIError > {
1364- let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1365- APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1366- } ) ?;
1367- let ( onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1369+ let mut trampoline_payloads = vec ! [ ] ;
1370+ let mut outer_total_msat = total_msat;
1371+ let mut outer_starting_htlc_offset = cur_block_height;
1372+ let outer_session_priv = session_priv;
1373+
1374+ if !path. trampoline_hops . is_empty ( ) {
1375+ ( trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) =
1376+ build_trampoline_onion_payloads (
1377+ path,
1378+ total_msat,
1379+ recipient_onion,
1380+ cur_block_height,
1381+ keysend_preimage,
1382+ ) ?;
1383+ }
1384+
1385+ let ( mut onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
13681386 & path,
13691387 total_msat,
13701388 recipient_onion,
13711389 cur_block_height,
13721390 keysend_preimage,
13731391 invoice_request,
13741392 ) ?;
1393+
1394+ if !path. trampoline_hops . is_empty ( ) {
1395+ let onion_keys = construct_trampoline_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1396+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1397+ } ) ?;
1398+ let trampoline_packet = construct_trampoline_onion_packet (
1399+ trampoline_payloads,
1400+ onion_keys,
1401+ prng_seed,
1402+ payment_hash,
1403+ None ,
1404+ )
1405+ . map_err ( |_| APIError :: InvalidRoute {
1406+ err : "Route size too large considering onion data" . to_owned ( ) ,
1407+ } ) ?;
1408+
1409+ let last_payload = onion_payloads. pop ( ) . ok_or ( APIError :: InvalidRoute {
1410+ err : "Non-Trampoline path needs at least one hop" . to_owned ( ) ,
1411+ } ) ?;
1412+
1413+ match last_payload {
1414+ OutboundOnionPayload :: Receive { .. } => {
1415+ onion_payloads. push ( OutboundOnionPayload :: TrampolineEntrypoint {
1416+ amt_to_forward : outer_total_msat,
1417+ outgoing_cltv_value : outer_starting_htlc_offset,
1418+ multipath_trampoline_data : None ,
1419+ trampoline_packet,
1420+ } ) ;
1421+ } ,
1422+ _ => {
1423+ return Err ( APIError :: InvalidRoute {
1424+ err : "Last non-Trampoline hop must be of type OutboundOnionPayload::Receive"
1425+ . to_owned ( ) ,
1426+ } ) ;
1427+ } ,
1428+ } ;
1429+
1430+ // TODO: replace outer_session_priv with hash-based derivation
1431+ }
1432+
1433+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, & outer_session_priv) . map_err ( |_| {
1434+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1435+ } ) ?;
13751436 let onion_packet = construct_onion_packet ( onion_payloads, onion_keys, prng_seed, payment_hash)
13761437 . map_err ( |_| APIError :: InvalidRoute {
13771438 err : "Route size too large considering onion data" . to_owned ( ) ,
0 commit comments