@@ -33,10 +33,10 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
33
33
use bitcoin:: secp256k1:: { PublicKey , Scalar , Secp256k1 , SecretKey } ;
34
34
35
35
use crate :: io:: { Cursor , Read } ;
36
- use core:: ops:: Deref ;
37
-
36
+ use crate :: ln:: msgs:: OutboundOnionPayload ;
38
37
#[ allow( unused_imports) ]
39
38
use crate :: prelude:: * ;
39
+ use core:: ops:: Deref ;
40
40
41
41
pub ( crate ) struct OnionKeys {
42
42
#[ cfg( test) ]
@@ -347,12 +347,18 @@ pub(super) fn build_onion_payloads<'a>(
347
347
let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
348
348
path. hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
349
349
) ;
350
- let blinded_tail_with_hop_iter = path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
351
- hops : bt. hops . iter ( ) ,
352
- blinding_point : bt. blinding_point ,
353
- final_value_msat : bt. final_value_msat ,
354
- excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
355
- } ) ;
350
+
351
+ // don't include blinded tail when Trampoline hops are present
352
+ let blinded_tail_with_hop_iter = if path. trampoline_hops . is_empty ( ) {
353
+ 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
+ } )
359
+ } else {
360
+ None
361
+ } ;
356
362
357
363
let ( value_msat, cltv) = build_onion_payloads_callback (
358
364
path. hops . iter ( ) ,
@@ -584,7 +590,6 @@ pub(super) fn construct_onion_packet(
584
590
)
585
591
}
586
592
587
- #[ allow( unused) ]
588
593
pub ( super ) fn construct_trampoline_onion_packet (
589
594
payloads : Vec < msgs:: OutboundTrampolinePayload > , onion_keys : Vec < OnionKeys > ,
590
595
prng_seed : [ u8 ; 32 ] , associated_data : & PaymentHash , length : Option < u16 > ,
@@ -1347,17 +1352,79 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
1347
1352
keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
1348
1353
prng_seed : [ u8 ; 32 ] ,
1349
1354
) -> Result < ( msgs:: OnionPacket , u64 , u32 ) , APIError > {
1350
- let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1351
- APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1352
- } ) ?;
1353
- let ( onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1355
+ let mut trampoline_payloads = vec ! [ ] ;
1356
+ let mut outer_total_msat = total_msat;
1357
+ let mut outer_starting_htlc_offset = cur_block_height;
1358
+ let mut outer_session_priv_override = None ;
1359
+
1360
+ if !path. trampoline_hops . is_empty ( ) {
1361
+ ( trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) =
1362
+ build_trampoline_onion_payloads (
1363
+ path,
1364
+ total_msat,
1365
+ recipient_onion,
1366
+ cur_block_height,
1367
+ keysend_preimage,
1368
+ ) ?;
1369
+ }
1370
+
1371
+ let ( mut onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1354
1372
& path,
1355
1373
total_msat,
1356
1374
recipient_onion,
1357
1375
cur_block_height,
1358
1376
keysend_preimage,
1359
1377
invoice_request,
1360
1378
) ?;
1379
+
1380
+ if !path. trampoline_hops . is_empty ( ) {
1381
+ let onion_keys =
1382
+ construct_trampoline_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1383
+ APIError :: InvalidRoute {
1384
+ err : "Pubkey along hop was maliciously selected" . to_owned ( ) ,
1385
+ }
1386
+ } ) ?;
1387
+ let trampoline_packet = construct_trampoline_onion_packet (
1388
+ trampoline_payloads,
1389
+ onion_keys,
1390
+ prng_seed,
1391
+ payment_hash,
1392
+ None ,
1393
+ )
1394
+ . map_err ( |_| APIError :: InvalidRoute {
1395
+ err : "Route size too large considering onion data" . to_owned ( ) ,
1396
+ } ) ?;
1397
+
1398
+ let last_payload = onion_payloads. pop ( ) . ok_or ( APIError :: InvalidRoute {
1399
+ err : "Non-Trampoline path needs at least one hop" . to_owned ( ) ,
1400
+ } ) ?;
1401
+
1402
+ match last_payload {
1403
+ OutboundOnionPayload :: Receive { .. } => {
1404
+ onion_payloads. push ( OutboundOnionPayload :: TrampolineEntrypoint {
1405
+ amt_to_forward : outer_total_msat,
1406
+ outgoing_cltv_value : outer_starting_htlc_offset,
1407
+ multipath_trampoline_data : None ,
1408
+ trampoline_packet,
1409
+ } ) ;
1410
+ } ,
1411
+ _ => {
1412
+ return Err ( APIError :: InvalidRoute {
1413
+ err : "Last non-Trampoline hop must be of type OutboundOnionPayload::Receive"
1414
+ . to_owned ( ) ,
1415
+ } ) ;
1416
+ } ,
1417
+ } ;
1418
+
1419
+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
1420
+ outer_session_priv_override =
1421
+ Some ( SecretKey :: from_slice ( & session_priv_hash[ ..] ) . expect ( "You broke SHA-256!" ) ) ;
1422
+ }
1423
+
1424
+ let outer_session_priv = outer_session_priv_override. as_ref ( ) . unwrap_or ( session_priv) ;
1425
+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, outer_session_priv) . map_err ( |_| {
1426
+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1427
+ } ) ?;
1361
1428
let onion_packet = construct_onion_packet ( onion_payloads, onion_keys, prng_seed, payment_hash)
1362
1429
. map_err ( |_| APIError :: InvalidRoute {
1363
1430
err : "Route size too large considering onion data" . to_owned ( ) ,
0 commit comments