@@ -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) ]
@@ -350,12 +350,18 @@ pub(super) fn build_onion_payloads<'a>(
350350 let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
351351 path. hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
352352 ) ;
353- let blinded_tail_with_hop_iter = path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
354- hops : bt. hops . iter ( ) ,
355- blinding_point : bt. blinding_point ,
356- final_value_msat : bt. final_value_msat ,
357- excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
358- } ) ;
353+
354+ // don't include blinded tail when Trampoline hops are present
355+ let blinded_tail_with_hop_iter = if path. trampoline_hops . is_empty ( ) {
356+ path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
357+ hops : bt. hops . iter ( ) ,
358+ blinding_point : bt. blinding_point ,
359+ final_value_msat : bt. final_value_msat ,
360+ excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
361+ } )
362+ } else {
363+ None
364+ } ;
359365
360366 let ( value_msat, cltv) = build_onion_payloads_callback (
361367 path. hops . iter ( ) ,
@@ -587,7 +593,6 @@ pub(super) fn construct_onion_packet(
587593 )
588594}
589595
590- #[ allow( unused) ]
591596pub ( super ) fn construct_trampoline_onion_packet (
592597 payloads : Vec < msgs:: OutboundTrampolinePayload > , onion_keys : Vec < OnionKeys > ,
593598 prng_seed : [ u8 ; 32 ] , associated_data : & PaymentHash , length : Option < u16 > ,
@@ -1344,17 +1349,78 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
13441349 keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
13451350 prng_seed : [ u8 ; 32 ] ,
13461351) -> Result < ( msgs:: OnionPacket , u64 , u32 ) , APIError > {
1347- let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1348- APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1349- } ) ?;
1350- let ( onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1352+ let mut trampoline_payloads = vec ! [ ] ;
1353+ let mut outer_total_msat = total_msat;
1354+ let mut outer_starting_htlc_offset = cur_block_height;
1355+ let mut outer_session_priv_override = None ;
1356+
1357+ if !path. trampoline_hops . is_empty ( ) {
1358+ ( trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) =
1359+ build_trampoline_onion_payloads (
1360+ path,
1361+ total_msat,
1362+ recipient_onion,
1363+ cur_block_height,
1364+ keysend_preimage,
1365+ ) ?;
1366+ }
1367+
1368+ let ( mut onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
13511369 & path,
13521370 total_msat,
13531371 recipient_onion,
13541372 cur_block_height,
13551373 keysend_preimage,
13561374 invoice_request,
13571375 ) ?;
1376+
1377+ if !path. trampoline_hops . is_empty ( ) {
1378+ let onion_keys =
1379+ construct_trampoline_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1380+ APIError :: InvalidRoute {
1381+ err : "Pubkey along hop was maliciously selected" . to_owned ( ) ,
1382+ }
1383+ } ) ?;
1384+ let trampoline_packet = construct_trampoline_onion_packet (
1385+ trampoline_payloads,
1386+ onion_keys,
1387+ prng_seed,
1388+ payment_hash,
1389+ None ,
1390+ )
1391+ . map_err ( |_| APIError :: InvalidRoute {
1392+ err : "Route size too large considering onion data" . to_owned ( ) ,
1393+ } ) ?;
1394+
1395+ let last_payload = onion_payloads. pop ( ) . ok_or ( APIError :: InvalidRoute {
1396+ err : "Non-Trampoline path needs at least one hop" . to_owned ( ) ,
1397+ } ) ?;
1398+
1399+ match last_payload {
1400+ OutboundOnionPayload :: Receive { .. } => {
1401+ onion_payloads. push ( OutboundOnionPayload :: TrampolineEntrypoint {
1402+ amt_to_forward : outer_total_msat,
1403+ outgoing_cltv_value : outer_starting_htlc_offset,
1404+ multipath_trampoline_data : None ,
1405+ trampoline_packet,
1406+ } ) ;
1407+ } ,
1408+ _ => {
1409+ return Err ( APIError :: InvalidRoute {
1410+ err : "Last non-Trampoline hop must be of type OutboundOnionPayload::Receive"
1411+ . to_owned ( ) ,
1412+ } ) ;
1413+ } ,
1414+ } ;
1415+
1416+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
1417+ outer_session_priv_override = Some ( SecretKey :: from_slice ( & session_priv_hash[ ..] ) . expect ( "You broke SHA-256!" ) ) ;
1418+ }
1419+
1420+ let outer_session_priv = outer_session_priv_override. as_ref ( ) . unwrap_or ( session_priv) ;
1421+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, outer_session_priv) . map_err ( |_| {
1422+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1423+ } ) ?;
13581424 let onion_packet = construct_onion_packet ( onion_payloads, onion_keys, prng_seed, payment_hash)
13591425 . map_err ( |_| APIError :: InvalidRoute {
13601426 err : "Route size too large considering onion data" . to_owned ( ) ,
0 commit comments