@@ -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) ]
@@ -301,12 +301,18 @@ pub(super) fn build_onion_payloads<'a>(
301
301
let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
302
302
path. hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
303
303
) ;
304
- let blinded_tail_with_hop_iter = path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
305
- hops : bt. hops . iter ( ) ,
306
- blinding_point : bt. blinding_point ,
307
- final_value_msat : bt. final_value_msat ,
308
- excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
309
- } ) ;
304
+
305
+ // don't include blinded tail when Trampoline hops are present
306
+ let blinded_tail_with_hop_iter = if path. trampoline_hops . is_empty ( ) {
307
+ path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
308
+ hops : bt. hops . iter ( ) ,
309
+ blinding_point : bt. blinding_point ,
310
+ final_value_msat : bt. final_value_msat ,
311
+ excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
312
+ } )
313
+ } else {
314
+ None
315
+ } ;
310
316
311
317
let ( value_msat, cltv) = build_onion_payloads_callback (
312
318
path. hops . iter ( ) ,
@@ -538,7 +544,6 @@ pub(super) fn construct_onion_packet(
538
544
)
539
545
}
540
546
541
- #[ allow( unused) ]
542
547
pub ( super ) fn construct_trampoline_onion_packet (
543
548
payloads : Vec < msgs:: OutboundTrampolinePayload > , onion_keys : Vec < OnionKeys > ,
544
549
prng_seed : [ u8 ; 32 ] , associated_data : & PaymentHash , length : Option < u16 > ,
@@ -1302,17 +1307,73 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
1302
1307
keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
1303
1308
prng_seed : [ u8 ; 32 ] ,
1304
1309
) -> Result < ( msgs:: OnionPacket , u64 , u32 ) , APIError > {
1305
- let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1306
- APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1307
- } ) ?;
1308
- let ( onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1310
+ let mut trampoline_payloads = vec ! [ ] ;
1311
+ let mut outer_total_msat = total_msat;
1312
+ let mut outer_starting_htlc_offset = cur_block_height;
1313
+ let outer_session_priv = session_priv;
1314
+
1315
+ if !path. trampoline_hops . is_empty ( ) {
1316
+ ( trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) =
1317
+ build_trampoline_onion_payloads (
1318
+ path,
1319
+ total_msat,
1320
+ recipient_onion,
1321
+ cur_block_height,
1322
+ keysend_preimage,
1323
+ ) ?;
1324
+ }
1325
+
1326
+ let ( mut onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1309
1327
& path,
1310
1328
total_msat,
1311
1329
recipient_onion,
1312
1330
cur_block_height,
1313
1331
keysend_preimage,
1314
1332
invoice_request,
1315
1333
) ?;
1334
+
1335
+ if !path. trampoline_hops . is_empty ( ) {
1336
+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1337
+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1338
+ } ) ?;
1339
+ let trampoline_packet = construct_trampoline_onion_packet (
1340
+ trampoline_payloads,
1341
+ onion_keys,
1342
+ prng_seed,
1343
+ payment_hash,
1344
+ None ,
1345
+ )
1346
+ . map_err ( |_| APIError :: InvalidRoute {
1347
+ err : "Route size too large considering onion data" . to_owned ( ) ,
1348
+ } ) ?;
1349
+
1350
+ let last_payload = onion_payloads. pop ( ) . ok_or ( APIError :: InvalidRoute {
1351
+ err : "Non-Trampoline path needs at least one hop" . to_owned ( ) ,
1352
+ } ) ?;
1353
+
1354
+ match last_payload {
1355
+ OutboundOnionPayload :: Receive { .. } => {
1356
+ onion_payloads. push ( OutboundOnionPayload :: TrampolineEntrypoint {
1357
+ amt_to_forward : outer_total_msat,
1358
+ outgoing_cltv_value : outer_starting_htlc_offset,
1359
+ multipath_trampoline_data : None ,
1360
+ trampoline_packet,
1361
+ } ) ;
1362
+ } ,
1363
+ _ => {
1364
+ return Err ( APIError :: InvalidRoute {
1365
+ err : "Last non-Trampoline hop must be of type OutboundOnionPayload::Receive"
1366
+ . to_owned ( ) ,
1367
+ } ) ;
1368
+ } ,
1369
+ } ;
1370
+
1371
+ // TODO: replace outer_session_priv with hash-based derivation
1372
+ }
1373
+
1374
+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, & outer_session_priv) . map_err ( |_| {
1375
+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1376
+ } ) ?;
1316
1377
let onion_packet = construct_onion_packet ( onion_payloads, onion_keys, prng_seed, payment_hash)
1317
1378
. map_err ( |_| APIError :: InvalidRoute {
1318
1379
err : "Route size too large considering onion data" . to_owned ( ) ,
0 commit comments