Skip to content

Commit 5cb1454

Browse files
committed
Trampoline payload construction method
Trampoline routing relies on serializing an inner onion within the hop data. In this commit, we introduce the method to serialize it based on the augmented path data introduced in the previous commit.
1 parent 0aaf9b1 commit 5cb1454

File tree

3 files changed

+328
-20
lines changed

3 files changed

+328
-20
lines changed

lightning/src/ln/msgs.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1834,7 +1834,6 @@ mod fuzzy_internal_msgs {
18341834
}
18351835

18361836
pub(crate) enum OutboundTrampolinePayload<'a> {
1837-
#[allow(unused)]
18381837
Forward {
18391838
/// The value, in msat, of the payment after this hop's fee is deducted.
18401839
amt_to_forward: u64,
@@ -1854,12 +1853,10 @@ mod fuzzy_internal_msgs {
18541853
/// If applicable, features of the BOLT12 invoice being paid.
18551854
invoice_features: Option<Bolt12InvoiceFeatures>,
18561855
},
1857-
#[allow(unused)]
18581856
BlindedForward {
18591857
encrypted_tlvs: &'a Vec<u8>,
18601858
intro_node_blinding_point: Option<PublicKey>,
18611859
},
1862-
#[allow(unused)]
18631860
BlindedReceive {
18641861
sender_intended_htlc_amt_msat: u64,
18651862
total_msat: u64,

lightning/src/ln/onion_route_tests.rs

+147-4
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
1919
use crate::ln::channelmanager::{HTLCForwardInfo, FailureCode, CLTV_FAR_FAR_AWAY, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA, PendingAddHTLCInfo, PendingHTLCInfo, PendingHTLCRouting, PaymentId, RecipientOnionFields};
2020
use crate::ln::onion_utils;
2121
use crate::routing::gossip::{NetworkUpdate, RoutingFees};
22-
use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop};
22+
use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop, Path, TrampolineHop, BlindedTail, RouteHop};
2323
use crate::types::features::{InitFeatures, Bolt11InvoiceFeatures};
2424
use crate::ln::functional_test_utils::test_default_channel_config;
2525
use crate::ln::msgs;
26-
use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate, OutboundTrampolinePayload};
26+
use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate, FinalOnionHopData, OutboundOnionPayload, OutboundTrampolinePayload};
2727
use crate::ln::wire::Encode;
2828
use crate::util::ser::{Writeable, Writer, BigSize};
2929
use crate::util::test_utils;
@@ -40,9 +40,11 @@ use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
4040

4141
use crate::io;
4242
use crate::prelude::*;
43-
use bitcoin::hex::FromHex;
44-
43+
use bitcoin::hex::{DisplayHex, FromHex};
44+
use types::features::{ChannelFeatures, Features, NodeFeatures};
45+
use crate::blinded_path::BlindedHop;
4546
use crate::ln::functional_test_utils::*;
47+
use crate::ln::onion_utils::{construct_trampoline_onion_keys, construct_trampoline_onion_packet};
4648

4749
fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>, expected_short_channel_id: Option<u64>, expected_htlc_destination: Option<HTLCDestination>)
4850
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
@@ -1012,6 +1014,147 @@ fn test_trampoline_onion_payload_serialization() {
10121014
assert_eq!(carol_payload_hex, "2e020405f5e10004030c35000e2102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145");
10131015
}
10141016

1017+
#[test]
1018+
fn test_trampoline_onion_payload_construction_vectors() {
1019+
// As per https://github.com/lightning/bolts/blob/fa0594ac2af3531d734f1d707a146d6e13679451/bolt04/trampoline-to-blinded-path-payment-onion-test.json#L251
1020+
1021+
let trampoline_payload_carol = OutboundTrampolinePayload::Forward {
1022+
amt_to_forward: 150_150_500,
1023+
outgoing_cltv_value: 800_036,
1024+
outgoing_node_id: PublicKey::from_slice(&<Vec<u8>>::from_hex("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()).unwrap(),
1025+
};
1026+
let carol_payload = trampoline_payload_carol.encode().to_lower_hex_string();
1027+
assert_eq!(carol_payload, "2e020408f31d6404030c35240e21032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991");
1028+
1029+
let trampoline_payload_dave = OutboundTrampolinePayload::BlindedForward {
1030+
encrypted_tlvs: &<Vec<u8>>::from_hex("0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a").unwrap(),
1031+
intro_node_blinding_point: Some(PublicKey::from_slice(&<Vec<u8>>::from_hex("02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e").unwrap()).unwrap()),
1032+
};
1033+
let dave_payload = trampoline_payload_dave.encode().to_lower_hex_string();
1034+
assert_eq!(dave_payload, "690a440ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a0c2102988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e");
1035+
1036+
let trampoline_payload_eve = OutboundTrampolinePayload::BlindedReceive {
1037+
sender_intended_htlc_amt_msat: 150_000_000,
1038+
total_msat: 150_000_000,
1039+
cltv_expiry_height: 800_000,
1040+
encrypted_tlvs: &<Vec<u8>>::from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c").unwrap(),
1041+
intro_node_blinding_point: None,
1042+
keysend_preimage: None,
1043+
custom_tlvs: &vec![],
1044+
};
1045+
let eve_payload = trampoline_payload_eve.encode().to_lower_hex_string();
1046+
assert_eq!(eve_payload, "e4020408f0d18004030c35000ad1bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c120408f0d180");
1047+
1048+
let trampoline_payloads = vec![trampoline_payload_carol, trampoline_payload_dave, trampoline_payload_eve];
1049+
1050+
let trampoline_session_key = SecretKey::from_slice(&<Vec<u8>>::from_hex("a64feb81abd58e473df290e9e1c07dc3e56114495cadf33191f44ba5448ebe99").unwrap()).unwrap();
1051+
let associated_data_slice = SecretKey::from_slice(&<Vec<u8>>::from_hex("e89bc505e84aaca09613833fc58c9069078fb43bfbea0488f34eec9db99b5f82").unwrap()).unwrap();
1052+
let associated_data = PaymentHash(associated_data_slice.secret_bytes());
1053+
1054+
let trampoline_hops = Path {
1055+
hops: vec![],
1056+
trampoline_hops: vec![
1057+
// Carol's pubkey
1058+
TrampolineHop {
1059+
pubkey: PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()).unwrap(),
1060+
node_features: Features::empty(),
1061+
fee_msat: 0,
1062+
cltv_expiry_delta: 0,
1063+
},
1064+
// Dave's pubkey (the intro node needs to be duplicated)
1065+
TrampolineHop {
1066+
pubkey: PublicKey::from_slice(&<Vec<u8>>::from_hex("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()).unwrap(),
1067+
node_features: Features::empty(),
1068+
fee_msat: 0,
1069+
cltv_expiry_delta: 0,
1070+
}
1071+
],
1072+
blinded_tail: Some(BlindedTail {
1073+
hops: vec![
1074+
// Dave's blinded node id
1075+
BlindedHop {
1076+
blinded_node_id: PublicKey::from_slice(&<Vec<u8>>::from_hex("0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be").unwrap()).unwrap(),
1077+
encrypted_payload: vec![],
1078+
},
1079+
// Eve's blinded node id
1080+
BlindedHop {
1081+
blinded_node_id: PublicKey::from_slice(&<Vec<u8>>::from_hex("020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22").unwrap()).unwrap(),
1082+
encrypted_payload: vec![],
1083+
}
1084+
],
1085+
blinding_point: PublicKey::from_slice(&<Vec<u8>>::from_hex("02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e").unwrap()).unwrap(),
1086+
excess_final_cltv_expiry_delta: 0,
1087+
final_value_msat: 0
1088+
}),
1089+
};
1090+
1091+
let trampoline_onion_keys = construct_trampoline_onion_keys(&Secp256k1::new(), &trampoline_hops, &trampoline_session_key).unwrap();
1092+
let trampoline_onion_packet = construct_trampoline_onion_packet(trampoline_payloads, trampoline_onion_keys, [0u8; 32], &associated_data, None).unwrap();
1093+
let trampoline_onion_packet_hex = trampoline_onion_packet.encode().to_lower_hex_string();
1094+
assert_eq!(trampoline_onion_packet_hex, "0002bc59a9abc893d75a8d4f56a6572f9a3507323a8de22abe0496ea8d37da166a8b4bba0e560f1a9deb602bfd98fe9167141d0b61d669df90c0149096d505b85d3d02806e6c12caeb308b878b6bc7f1b15839c038a6443cd3bec3a94c2293165375555f6d7720862b525930f41fddcc02260d197abd93fb58e60835fd97d9dc14e7979c12f59df08517b02e3e4d50e1817de4271df66d522c4e9675df71c635c4176a8381bc22b342ff4e9031cede87f74cc039fca74aa0a3786bc1db2e158a9a520ecb99667ef9a6bbfaf5f0e06f81c27ca48134ba2103229145937c5dc7b8ecc5201d6aeb592e78faa3c05d3a035df77628f0be9b1af3ef7d386dd5cc87b20778f47ebd40dbfcf12b9071c5d7112ab84c3e0c5c14867e684d09a18bc93ac47d73b7343e3403ef6e3b70366835988920e7d772c3719d3596e53c29c4017cb6938421a557ce81b4bb26701c25bf622d4c69f1359dc85857a375c5c74987a4d3152f66987001c68a50c4bf9e0b1dab4ad1a64b0535319bbf6c4fbe4f9c50cb65f5ef887bfb91b0a57c0f86ba3d91cbeea1607fb0c12c6c75d03bbb0d3a3019c40597027f5eebca23083e50ec79d41b1152131853525bf3fc13fb0be62c2e3ce733f59671eee5c4064863fb92ae74be9ca68b9c716f9519fd268478ee27d91d466b0de51404de3226b74217d28250ead9d2c95411e0230570f547d4cc7c1d589791623131aa73965dccc5aa17ec12b442215ce5d346df664d799190df5dd04a13");
1095+
1096+
let outer_payloads = vec![
1097+
// Bob
1098+
OutboundOnionPayload::Forward {
1099+
short_channel_id: (572330 << 40) + (42 << 16) + 2821,
1100+
amt_to_forward: 150153000,
1101+
outgoing_cltv_value: 800060,
1102+
},
1103+
1104+
// Carol
1105+
OutboundOnionPayload::TrampolineEntrypoint {
1106+
amt_to_forward: 150153000,
1107+
outgoing_cltv_value: 800060,
1108+
trampoline_packet: trampoline_onion_packet,
1109+
multipath_trampoline_data: Some(FinalOnionHopData{
1110+
payment_secret: PaymentSecret(SecretKey::from_slice(&<Vec<u8>>::from_hex("7494b65bc092b48a75465e43e29be807eb2cc535ce8aaba31012b8ff1ceac5da").unwrap()).unwrap().secret_bytes()),
1111+
total_msat: 150153000
1112+
}),
1113+
}
1114+
];
1115+
1116+
let outer_hops = Path {
1117+
hops: vec![
1118+
// Bob
1119+
RouteHop {
1120+
pubkey: PublicKey::from_slice(&<Vec<u8>>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()).unwrap(),
1121+
node_features: NodeFeatures::empty(),
1122+
short_channel_id: 0,
1123+
channel_features: ChannelFeatures::empty(),
1124+
fee_msat: 0,
1125+
cltv_expiry_delta: 0,
1126+
maybe_announced_channel: false,
1127+
},
1128+
1129+
// Carol
1130+
RouteHop {
1131+
pubkey: PublicKey::from_slice(&<Vec<u8>>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()).unwrap(),
1132+
node_features: NodeFeatures::empty(),
1133+
short_channel_id: 0,
1134+
channel_features: ChannelFeatures::empty(),
1135+
fee_msat: 0,
1136+
cltv_expiry_delta: 0,
1137+
maybe_announced_channel: false,
1138+
},
1139+
],
1140+
trampoline_hops: vec![],
1141+
blinded_tail: None,
1142+
};
1143+
1144+
let bob_payload = outer_payloads[0].encode().to_lower_hex_string();
1145+
assert_eq!(bob_payload, "15020408f3272804030c353c060808bbaa00002a0b05");
1146+
1147+
let carol_payload = outer_payloads[1].encode().to_lower_hex_string();
1148+
assert_eq!(carol_payload, "fd0255020408f3272804030c353c08247494b65bc092b48a75465e43e29be807eb2cc535ce8aaba31012b8ff1ceac5da08f3272814fd02200002bc59a9abc893d75a8d4f56a6572f9a3507323a8de22abe0496ea8d37da166a8b4bba0e560f1a9deb602bfd98fe9167141d0b61d669df90c0149096d505b85d3d02806e6c12caeb308b878b6bc7f1b15839c038a6443cd3bec3a94c2293165375555f6d7720862b525930f41fddcc02260d197abd93fb58e60835fd97d9dc14e7979c12f59df08517b02e3e4d50e1817de4271df66d522c4e9675df71c635c4176a8381bc22b342ff4e9031cede87f74cc039fca74aa0a3786bc1db2e158a9a520ecb99667ef9a6bbfaf5f0e06f81c27ca48134ba2103229145937c5dc7b8ecc5201d6aeb592e78faa3c05d3a035df77628f0be9b1af3ef7d386dd5cc87b20778f47ebd40dbfcf12b9071c5d7112ab84c3e0c5c14867e684d09a18bc93ac47d73b7343e3403ef6e3b70366835988920e7d772c3719d3596e53c29c4017cb6938421a557ce81b4bb26701c25bf622d4c69f1359dc85857a375c5c74987a4d3152f66987001c68a50c4bf9e0b1dab4ad1a64b0535319bbf6c4fbe4f9c50cb65f5ef887bfb91b0a57c0f86ba3d91cbeea1607fb0c12c6c75d03bbb0d3a3019c40597027f5eebca23083e50ec79d41b1152131853525bf3fc13fb0be62c2e3ce733f59671eee5c4064863fb92ae74be9ca68b9c716f9519fd268478ee27d91d466b0de51404de3226b74217d28250ead9d2c95411e0230570f547d4cc7c1d589791623131aa73965dccc5aa17ec12b442215ce5d346df664d799190df5dd04a13");
1149+
1150+
let outer_session_key = SecretKey::from_slice(&<Vec<u8>>::from_hex("4f777e8dac16e6dfe333066d9efb014f7a51d11762ff76eca4d3a95ada99ba3e").unwrap()).unwrap();
1151+
let outer_onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &outer_hops, &outer_session_key).unwrap();
1152+
let outer_onion_prng_seed = onion_utils::gen_pad_from_shared_secret(&outer_session_key.secret_bytes());
1153+
let outer_onion_packet = onion_utils::construct_onion_packet(outer_payloads, outer_onion_keys, outer_onion_prng_seed, &associated_data).unwrap();
1154+
let outer_onion_packet_hex = outer_onion_packet.encode().to_lower_hex_string();
1155+
assert_eq!(outer_onion_packet_hex, "00025fd60556c134ae97e4baedba220a644037754ee67c54fd05e93bf40c17cbb73362fb9dee96001ff229945595b6edb59437a6bc143406d3f90f749892a84d8d430c6890437d26d5bfc599d565316ef51347521075bbab87c59c57bcf20af7e63d7192b46cf171e4f73cb11f9f603915389105d91ad630224bea95d735e3988add1e24b5bf28f1d7128db64284d90a839ba340d088c74b1fb1bd21136b1809428ec5399c8649e9bdf92d2dcfc694deae5046fa5b2bdf646847aaad73f5e95275763091c90e71031cae1f9a770fdea559642c9c02f424a2a28163dd0957e3874bd28a97bec67d18c0321b0e68bc804aa8345b17cb626e2348ca06c8312a167c989521056b0f25c55559d446507d6c491d50605cb79fa87929ce64b0a9860926eeaec2c431d926a1cadb9a1186e4061cb01671a122fc1f57602cbef06d6c194ec4b715c2e3dd4120baca3172cd81900b49fef857fb6d6afd24c983b608108b0a5ac0c1c6c52011f23b8778059ffadd1bb7cd06e2525417365f485a7fd1d4a9ba3818ede7cdc9e71afee8532252d08e2531ca52538655b7e8d912f7ec6d37bbcce8d7ec690709dbf9321e92c565b78e7fe2c22edf23e0902153d1ca15a112ad32fb19695ec65ce11ddf670da7915f05ad4b86c154fb908cb567315d1124f303f75fa075ebde8ef7bb12e27737ad9e4924439097338ea6d7a6fc3721b88c9b830a34e8d55f4c582b74a3895cc848fe57f4fe29f115dabeb6b3175be15d94408ed6771109cfaf57067ae658201082eae7605d26b1449af4425ae8e8f58cdda5c6265f1fd7a386fc6cea3074e4f25b909b96175883676f7610a00fdf34df9eb6c7b9a4ae89b839c69fd1f285e38cdceb634d782cc6d81179759bc9fd47d7fd060470d0b048287764c6837963274e708314f017ac7dc26d0554d59bfcfd3136225798f65f0b0fea337c6b256ebbb63a90b994c0ab93fd8b1d6bd4c74aebe535d6110014cd3d525394027dfe8faa98b4e9b2bee7949eb1961f1b026791092f84deea63afab66603dbe9b6365a102a1fef2f6b9744bc1bb091a8da9130d34d4d39f25dbad191649cfb67e10246364b7ce0c6ec072f9690cabb459d9fda0c849e17535de4357e9907270c75953fca3c845bb613926ecf73205219c7057a4b6bb244c184362bb4e2f24279dc4e60b94a5b1ec11c34081a628428ba5646c995b9558821053ba9c84a05afbf00dabd60223723096516d2f5668f3ec7e11612b01eb7a3a0506189a2272b88e89807943adb34291a17f6cb5516ffd6f945a1c42a524b21f096d66f350b1dad4db455741ae3d0e023309fbda5ef55fb0dc74f3297041448b2be76c525141963934c6afc53d263fb7836626df502d7c2ee9e79cbbd87afd84bbb8dfbf45248af3cd61ad5fac827e7683ca4f91dfad507a8eb9c17b2c9ac5ec051fe645a4a6cb37136f6f19b611e0ea8da7960af2d779507e55f57305bc74b7568928c5dd5132990fe54c22117df91c257d8c7b61935a018a28c1c3b17bab8e4294fa699161ec21123c9fc4e71079df31f300c2822e1246561e04765d3aab333eafd026c7431ac7616debb0e022746f4538e1c6348b600c988eeb2d051fc60c468dca260a84c79ab3ab8342dc345a764672848ea234e17332bc124799daf7c5fcb2e2358514a7461357e1c19c802c5ee32deccf1776885dd825bedd5f781d459984370a6b7ae885d4483a76ddb19b30f47ed47cd56aa5a079a89793dbcad461c59f2e002067ac98dd5a534e525c9c46c2af730741bf1f8629357ec0bfc0bc9ecb31af96777e507648ff4260dc3673716e098d9111dfd245f1d7c55a6de340deb8bd7a053e5d62d760f184dc70ca8fa255b9023b9b9aedfb6e419a5b5951ba0f83b603793830ee68d442d7b88ee1bbf6bbd1bcd6f68cc1af");
1156+
}
1157+
10151158
fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) {
10161159

10171160
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)