@@ -33,10 +33,11 @@ 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:: { FinalOnionHopData , OutboundOnionPayload } ;
38
37
#[ allow( unused_imports) ]
39
38
use crate :: prelude:: * ;
39
+ use core:: ops:: Deref ;
40
+ use types:: payment:: PaymentSecret ;
40
41
41
42
pub ( crate ) struct OnionKeys {
42
43
#[ cfg( test) ]
@@ -347,12 +348,18 @@ pub(super) fn build_onion_payloads<'a>(
347
348
let mut res: Vec < msgs:: OutboundOnionPayload > = Vec :: with_capacity (
348
349
path. hops . len ( ) + path. blinded_tail . as_ref ( ) . map_or ( 0 , |t| t. hops . len ( ) ) ,
349
350
) ;
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
- } ) ;
351
+
352
+ // don't include blinded tail when Trampoline hops are present
353
+ let blinded_tail_with_hop_iter = if path. trampoline_hops . is_empty ( ) {
354
+ path. blinded_tail . as_ref ( ) . map ( |bt| BlindedTailHopIter {
355
+ hops : bt. hops . iter ( ) ,
356
+ blinding_point : bt. blinding_point ,
357
+ final_value_msat : bt. final_value_msat ,
358
+ excess_final_cltv_expiry_delta : bt. excess_final_cltv_expiry_delta ,
359
+ } )
360
+ } else {
361
+ None
362
+ } ;
356
363
357
364
let ( value_msat, cltv) = build_onion_payloads_callback (
358
365
path. hops . iter ( ) ,
@@ -584,7 +591,6 @@ pub(super) fn construct_onion_packet(
584
591
)
585
592
}
586
593
587
- #[ allow( unused) ]
588
594
pub ( super ) fn construct_trampoline_onion_packet (
589
595
payloads : Vec < msgs:: OutboundTrampolinePayload > , onion_keys : Vec < OnionKeys > ,
590
596
prng_seed : [ u8 ; 32 ] , associated_data : & PaymentHash , length : Option < u16 > ,
@@ -1347,21 +1353,124 @@ pub fn create_payment_onion<T: secp256k1::Signing>(
1347
1353
keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
1348
1354
prng_seed : [ u8 ; 32 ] ,
1349
1355
) -> 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 (
1354
- & path,
1356
+ create_payment_onion_internal (
1357
+ secp_ctx,
1358
+ path,
1359
+ session_priv,
1355
1360
total_msat,
1356
1361
recipient_onion,
1357
1362
cur_block_height,
1363
+ payment_hash,
1358
1364
keysend_preimage,
1359
1365
invoice_request,
1360
- ) ?;
1361
- let onion_packet = construct_onion_packet ( onion_payloads, onion_keys, prng_seed, payment_hash)
1366
+ prng_seed,
1367
+ None ,
1368
+ None ,
1369
+ None ,
1370
+ )
1371
+ }
1372
+
1373
+ /// Build a payment onion, returning the first hop msat and cltv values as well.
1374
+ /// `cur_block_height` should be set to the best known block height + 1.
1375
+ pub ( crate ) fn create_payment_onion_internal < T : secp256k1:: Signing > (
1376
+ secp_ctx : & Secp256k1 < T > , path : & Path , session_priv : & SecretKey , total_msat : u64 ,
1377
+ recipient_onion : & RecipientOnionFields , cur_block_height : u32 , payment_hash : & PaymentHash ,
1378
+ keysend_preimage : & Option < PaymentPreimage > , invoice_request : Option < & InvoiceRequest > ,
1379
+ prng_seed : [ u8 ; 32 ] , secondary_payment_secret : Option < PaymentSecret > ,
1380
+ secondary_session_priv : Option < SecretKey > , secondary_prng_seed : Option < [ u8 ; 32 ] > ,
1381
+ ) -> Result < ( msgs:: OnionPacket , u64 , u32 ) , APIError > {
1382
+ let mut outer_total_msat = total_msat;
1383
+ let mut outer_starting_htlc_offset = cur_block_height;
1384
+ let mut outer_session_priv_override = None ;
1385
+ let mut trampoline_packet_option = None ;
1386
+
1387
+ if !path. trampoline_hops . is_empty ( ) {
1388
+ let trampoline_payloads;
1389
+ ( trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) =
1390
+ build_trampoline_onion_payloads (
1391
+ path,
1392
+ total_msat,
1393
+ recipient_onion,
1394
+ cur_block_height,
1395
+ keysend_preimage,
1396
+ ) ?;
1397
+
1398
+ let onion_keys =
1399
+ construct_trampoline_onion_keys ( & secp_ctx, & path, & session_priv) . map_err ( |_| {
1400
+ APIError :: InvalidRoute {
1401
+ err : "Pubkey along hop was maliciously selected" . to_owned ( ) ,
1402
+ }
1403
+ } ) ?;
1404
+ let trampoline_packet = construct_trampoline_onion_packet (
1405
+ trampoline_payloads,
1406
+ onion_keys,
1407
+ prng_seed,
1408
+ payment_hash,
1409
+ None ,
1410
+ )
1362
1411
. map_err ( |_| APIError :: InvalidRoute {
1363
1412
err : "Route size too large considering onion data" . to_owned ( ) ,
1364
1413
} ) ?;
1414
+
1415
+ trampoline_packet_option = Some ( trampoline_packet) ;
1416
+ }
1417
+
1418
+ let ( mut onion_payloads, htlc_msat, htlc_cltv) = build_onion_payloads (
1419
+ & path,
1420
+ outer_total_msat,
1421
+ recipient_onion,
1422
+ outer_starting_htlc_offset,
1423
+ keysend_preimage,
1424
+ invoice_request,
1425
+ ) ?;
1426
+
1427
+ if !path. trampoline_hops . is_empty ( ) {
1428
+ let last_payload = onion_payloads. pop ( ) . ok_or ( APIError :: InvalidRoute {
1429
+ err : "Non-Trampoline path needs at least one hop" . to_owned ( ) ,
1430
+ } ) ?;
1431
+
1432
+ match last_payload {
1433
+ OutboundOnionPayload :: Receive { payment_data, .. } => {
1434
+ let fee_delta = path. hops . last ( ) . map_or ( 0 , |h| h. fee_msat ) ;
1435
+ let cltv_delta = path. hops . last ( ) . map_or ( 0 , |h| h. cltv_expiry_delta ) ;
1436
+ let multipath_trampoline_data = payment_data. map ( |d| {
1437
+ let trampoline_payment_secret = secondary_payment_secret. unwrap_or_else ( || {
1438
+ PaymentSecret ( Sha256 :: hash ( & d. payment_secret . 0 ) . to_byte_array ( ) )
1439
+ } ) ;
1440
+ let total_msat = fee_delta;
1441
+ FinalOnionHopData { payment_secret : trampoline_payment_secret, total_msat }
1442
+ } ) ;
1443
+ onion_payloads. push ( OutboundOnionPayload :: TrampolineEntrypoint {
1444
+ amt_to_forward : fee_delta,
1445
+ outgoing_cltv_value : outer_starting_htlc_offset + cltv_delta,
1446
+ multipath_trampoline_data,
1447
+ trampoline_packet : trampoline_packet_option. unwrap ( ) ,
1448
+ } ) ;
1449
+ } ,
1450
+ _ => {
1451
+ return Err ( APIError :: InvalidRoute {
1452
+ err : "Last non-Trampoline hop must be of type OutboundOnionPayload::Receive"
1453
+ . to_owned ( ) ,
1454
+ } ) ;
1455
+ } ,
1456
+ } ;
1457
+
1458
+ outer_session_priv_override = Some ( secondary_session_priv. unwrap_or_else ( || {
1459
+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
1460
+ SecretKey :: from_slice ( & session_priv_hash[ ..] ) . expect ( "You broke SHA-256!" )
1461
+ } ) ) ;
1462
+ }
1463
+
1464
+ let outer_session_priv = outer_session_priv_override. as_ref ( ) . unwrap_or ( session_priv) ;
1465
+ let onion_keys = construct_onion_keys ( & secp_ctx, & path, outer_session_priv) . map_err ( |_| {
1466
+ APIError :: InvalidRoute { err : "Pubkey along hop was maliciously selected" . to_owned ( ) }
1467
+ } ) ?;
1468
+ let outer_onion_prng_seed = secondary_prng_seed. unwrap_or ( prng_seed) ;
1469
+ let onion_packet =
1470
+ construct_onion_packet ( onion_payloads, onion_keys, outer_onion_prng_seed, payment_hash)
1471
+ . map_err ( |_| APIError :: InvalidRoute {
1472
+ err : "Route size too large considering onion data" . to_owned ( ) ,
1473
+ } ) ?;
1365
1474
Ok ( ( onion_packet, htlc_msat, htlc_cltv) )
1366
1475
}
1367
1476
0 commit comments