Skip to content

Commit a4bab8f

Browse files
committed
Trampoline payload construction method
1 parent 50dfb9f commit a4bab8f

File tree

3 files changed

+327
-20
lines changed

3 files changed

+327
-20
lines changed

lightning/src/ln/msgs.rs

Lines changed: 0 additions & 3 deletions
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

Lines changed: 146 additions & 4 deletions
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>)
4850
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
@@ -1001,6 +1003,146 @@ fn test_trampoline_onion_payload_serialization() {
10011003
assert_eq!(carol_payload_hex, "2e020405f5e10004030c35000e2102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145");
10021004
}
10031005

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

10061148
let chanmon_cfgs = create_chanmon_cfgs(2);

0 commit comments

Comments
 (0)