From ddf66bf572087cb070b00134ef2701c01363b236 Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Nov 2023 13:23:15 -0800 Subject: [PATCH 1/5] Add trampoline feature flag. --- lightning/src/ln/features.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 79f869a4c58..722107989ed 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -168,6 +168,8 @@ mod sealed { ChannelType | SCIDPrivacy, // Byte 6 ZeroConf | Keysend, + // Byte 7 + Trampoline, ]); define_context!(ChannelContext, []); define_context!(Bolt11InvoiceContext, [ @@ -420,6 +422,9 @@ mod sealed { define_feature!(55, Keysend, [NodeContext], "Feature flags for keysend payments.", set_keysend_optional, set_keysend_required, supports_keysend, requires_keysend); + define_feature!(57, Trampoline, [NodeContext], + "Feature flags for Trampoline routing.", set_trampoline_routing_optional, set_trampoline_routing_required, + supports_trampoline_routing, requires_trampoline_routing); // Note: update the module-level docs when a new feature bit is added! #[cfg(test)] From 92cfc437de3d0feb5beae39d8cd68cac036f337a Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Nov 2023 12:17:17 -0800 Subject: [PATCH 2/5] Add trampoline packet field to `OutboundOnionPayload::Receive`. --- lightning/src/ln/msgs.rs | 14 ++++++++++++-- lightning/src/ln/onion_utils.rs | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index f0db28949ed..688f57072f5 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1645,6 +1645,7 @@ mod fuzzy_internal_msgs { use crate::ln::{PaymentPreimage, PaymentSecret}; use crate::ln::features::BlindedHopFeatures; use super::FinalOnionHopData; + use crate::ln::msgs::OnionPacket; // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize // them from untrusted input): @@ -1695,6 +1696,7 @@ mod fuzzy_internal_msgs { custom_tlvs: Vec<(u64, Vec)>, sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, + trampoline_packet: Option }, BlindedForward { encrypted_tlvs: Vec, @@ -2470,13 +2472,17 @@ impl Writeable for OutboundOnionPayload { }, Self::Receive { ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat, - cltv_expiry_height, ref custom_tlvs, + cltv_expiry_height, ref trampoline_packet, ref custom_tlvs, } => { // We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] // to reject any reserved types in the experimental range if new ones are ever // standardized. let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter().chain(keysend_tlv.iter()).collect(); + let trampoline_tlv = trampoline_packet.as_ref().map(|trampoline| (66100, trampoline.encode())); + let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter() + .chain(keysend_tlv.iter()) + .chain(trampoline_tlv.iter()) + .collect(); custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), @@ -4208,6 +4214,7 @@ mod tests { sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, custom_tlvs: vec![], + trampoline_packet: None }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("1002080badf00d010203040404ffffffff").unwrap(); @@ -4236,6 +4243,7 @@ mod tests { sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, custom_tlvs: vec![], + trampoline_packet: None }; let encoded_value = outbound_msg.encode(); let target_value = >::from_hex("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap(); @@ -4275,6 +4283,7 @@ mod tests { custom_tlvs: bad_type_range_tlvs, sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, + trampoline_packet: None }; let encoded_value = msg.encode(); let node_signer = test_utils::TestKeysInterface::new(&[42; 32], Network::Testnet); @@ -4307,6 +4316,7 @@ mod tests { custom_tlvs: expected_custom_tlvs.clone(), sender_intended_htlc_amt_msat: 0x0badf00d01020304, cltv_expiry_height: 0xffffffff, + trampoline_packet: None }; let encoded_value = msg.encode(); let target_value = >::from_hex("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap(); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 4b39276c066..15088442a2f 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -216,6 +216,7 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o custom_tlvs: recipient_onion.custom_tlvs.clone(), sender_intended_htlc_amt_msat: value_msat, cltv_expiry_height: cltv, + trampoline_packet: None }); } } else { From 2cee03c015dfb855efd8f6ec958ef943656b0ced Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Mon, 4 Dec 2023 10:28:39 -0800 Subject: [PATCH 3/5] Use onion::Packet for inner Trampoline onion. --- lightning/src/ln/msgs.rs | 67 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 688f57072f5..038ce9d10bf 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1645,7 +1645,6 @@ mod fuzzy_internal_msgs { use crate::ln::{PaymentPreimage, PaymentSecret}; use crate::ln::features::BlindedHopFeatures; use super::FinalOnionHopData; - use crate::ln::msgs::OnionPacket; // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize // them from untrusted input): @@ -1696,7 +1695,7 @@ mod fuzzy_internal_msgs { custom_tlvs: Vec<(u64, Vec)>, sender_intended_htlc_amt_msat: u64, cltv_expiry_height: u32, - trampoline_packet: Option + trampoline_packet: Option }, BlindedForward { encrypted_tlvs: Vec, @@ -3042,7 +3041,7 @@ mod tests { use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields}; use crate::ln::msgs::SocketAddress; use crate::routing::gossip::{NodeAlias, NodeId}; - use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited}; + use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited, BigSize}; use crate::util::test_utils; use bitcoin::hashes::hex::FromHex; @@ -3066,6 +3065,7 @@ mod tests { use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}; #[cfg(feature = "std")] use crate::ln::msgs::SocketAddressParseError; + use crate::onion_message::packet::Packet; #[test] fn encoding_channel_reestablish() { @@ -4338,6 +4338,67 @@ mod tests { } else { panic!(); } } + #[test] + fn encoding_final_onion_hop_data_with_trampoline_packet() { + let secp_ctx = Secp256k1::new(); + let (_private_key, public_key) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); + + let compressed_public_key = public_key.serialize(); + assert_eq!(compressed_public_key.len(), 33); + + let trampoline_packet = Packet { + version: 0, + public_key, + hop_data: vec![1; 650], // this should be the standard encoded length + hmac: [2; 32], + }; + let encoded_trampoline_packet = trampoline_packet.encode(); + assert_eq!(encoded_trampoline_packet.len(), 716); + + let msg = msgs::OutboundOnionPayload::Receive { + payment_data: None, + payment_metadata: None, + keysend_preimage: None, + custom_tlvs: Vec::new(), + sender_intended_htlc_amt_msat: 0x0badf00d01020304, + cltv_expiry_height: 0xffffffff, + trampoline_packet: Some(trampoline_packet), + }; + let encoded_payload = msg.encode(); + + let trampoline_type_bytes = &encoded_payload[19..=23]; + let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes); + let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap(); + assert_eq!(trampoline_type_big_size.0, 66100); + + let trampoline_length_bytes = &encoded_payload[24..=26]; + let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes); + let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap(); + assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64); + } + + #[test] + fn encoding_final_onion_hop_data_with_eclair_trampoline_packet() { + let public_key = PublicKey::from_slice(&>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap(); + let hop_data = >::from_hex("cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4c").unwrap(); + let hmac_vector = >::from_hex("bb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap(); + let mut hmac = [0; 32]; + hmac.copy_from_slice(&hmac_vector); + + let compressed_public_key = public_key.serialize(); + assert_eq!(compressed_public_key.len(), 33); + + let trampoline_packet = Packet { + version: 0, + public_key, + hop_data, + hmac, + }; + let encoded_trampoline_packet = trampoline_packet.encode(); + let expected_eclair_trampoline_packet = >::from_hex("0002eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619cff34152f3a36e52ca94e74927203a560392b9cc7ce3c45809c6be52166c24a595716880f95f178bf5b30ca63141f74db6e92795c6130877cfdac3d4bd3087ee73c65d627ddd709112a848cc99e303f3706509aa43ba7c8a88cba175fccf9a8f5016ef06d3b935dbb15196d7ce16dc1a7157845566901d7b2197e52cab4ce487014b14816e5805f9fcacb4f8f88b8ff176f1b94f6ce6b00bc43221130c17d20ef629db7c5f7eafaa166578c720619561dd14b3277db557ec7dcdb793771aef0f2f667cfdbeae3ac8d331c5994779dffb31e5fc0dbdedc0c592ca6d21c18e47fe3528d6975c19517d7e2ea8c5391cf17d0fe30c80913ed887234ccb48808f7ef9425bcd815c3b586210979e3bb286ef2851bf9ce04e28c40a203df98fd648d2f1936fd2f1def0e77eecb277229b4b682322371c0a1dbfcd723a991993df8cc1f2696b84b055b40a1792a29f710295a18fbd351b0f3ff34cd13941131b8278ba79303c89117120eea691738a9954908195143b039dbeed98f26a92585f3d15cf742c953799d3272e0545e9b744be9d3b4cbb079bfc4b35190eee9f59a1d7b41ba2f773179f322dafb4b1af900c289ebd6c").unwrap(); + assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet); + } + #[test] fn query_channel_range_end_blocknum() { let tests: Vec<(u32, u32, u32)> = vec![ From 9cfb56e8f6a3309c5c0c799eeb21ad5f5eccbbee Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Wed, 7 Feb 2024 15:51:58 -0800 Subject: [PATCH 4/5] Switch Trampoline onion ID to 20. --- lightning/src/ln/msgs.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 038ce9d10bf..0238f62b201 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -2477,17 +2477,16 @@ impl Writeable for OutboundOnionPayload { // to reject any reserved types in the experimental range if new ones are ever // standardized. let keysend_tlv = keysend_preimage.map(|preimage| (5482373484, preimage.encode())); - let trampoline_tlv = trampoline_packet.as_ref().map(|trampoline| (66100, trampoline.encode())); let mut custom_tlvs: Vec<&(u64, Vec)> = custom_tlvs.iter() .chain(keysend_tlv.iter()) - .chain(trampoline_tlv.iter()) .collect(); custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); _encode_varint_length_prefixed_tlv!(w, { (2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required), (4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required), (8, payment_data, option), - (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option) + (16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option), + (20, trampoline_packet, option) }, custom_tlvs.iter()); }, Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point } => { @@ -4366,12 +4365,12 @@ mod tests { }; let encoded_payload = msg.encode(); - let trampoline_type_bytes = &encoded_payload[19..=23]; + let trampoline_type_bytes = &encoded_payload[19..=19]; let mut trampoline_type_cursor = Cursor::new(trampoline_type_bytes); let trampoline_type_big_size: BigSize = Readable::read(&mut trampoline_type_cursor).unwrap(); - assert_eq!(trampoline_type_big_size.0, 66100); + assert_eq!(trampoline_type_big_size.0, 20); - let trampoline_length_bytes = &encoded_payload[24..=26]; + let trampoline_length_bytes = &encoded_payload[20..=22]; let mut trampoline_length_cursor = Cursor::new(trampoline_length_bytes); let trampoline_length_big_size: BigSize = Readable::read(&mut trampoline_length_cursor).unwrap(); assert_eq!(trampoline_length_big_size.0, encoded_trampoline_packet.len() as u64); From 1f6362967ef098781bc56c3a6bfb38b71c1eb25d Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Fri, 16 Feb 2024 09:27:46 -0800 Subject: [PATCH 5/5] Trampoline onion construction and vector tests. --- lightning/src/ln/msgs.rs | 15 +++ lightning/src/ln/onion_route_tests.rs | 149 +++++++++++++++++++++++++- lightning/src/ln/onion_utils.rs | 21 ++++ 3 files changed, 181 insertions(+), 4 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 0238f62b201..0190634f60b 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1688,6 +1688,14 @@ mod fuzzy_internal_msgs { amt_to_forward: u64, outgoing_cltv_value: u32, }, + // This should only be used for nested Trampoline onions + TrampolineForward { + /// The value, in msat, of the payment after this hop's fee is deducted. + amt_to_forward: u64, + outgoing_cltv_value: u32, + /// The node id to which the trampoline node must find a route + outgoing_node_id: PublicKey + }, Receive { payment_data: Option, payment_metadata: Option>, @@ -2469,6 +2477,13 @@ impl Writeable for OutboundOnionPayload { (6, short_channel_id, required) }); }, + Self::TrampolineForward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => { + _encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required), + (14, outgoing_node_id, required) + }); + }, Self::Receive { ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat, cltv_expiry_height, ref trampoline_packet, ref custom_tlvs, diff --git a/lightning/src/ln/onion_route_tests.rs b/lightning/src/ln/onion_route_tests.rs index cfdba07a2f2..5642203f5f3 100644 --- a/lightning/src/ln/onion_route_tests.rs +++ b/lightning/src/ln/onion_route_tests.rs @@ -19,10 +19,10 @@ use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS; use crate::ln::channelmanager::{HTLCForwardInfo, FailureCode, CLTV_FAR_FAR_AWAY, DISABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA, PendingAddHTLCInfo, PendingHTLCInfo, PendingHTLCRouting, PaymentId, RecipientOnionFields}; use crate::ln::onion_utils; use crate::routing::gossip::{NetworkUpdate, RoutingFees}; -use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop}; -use crate::ln::features::{InitFeatures, Bolt11InvoiceFeatures}; +use crate::routing::router::{get_route, PaymentParameters, Route, RouteParameters, RouteHint, RouteHintHop, Path, RouteHop}; +use crate::ln::features::{InitFeatures, Bolt11InvoiceFeatures, NodeFeatures, ChannelFeatures}; use crate::ln::msgs; -use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate}; +use crate::ln::msgs::{ChannelMessageHandler, ChannelUpdate, FinalOnionHopData, OutboundOnionPayload}; use crate::ln::wire::Encode; use crate::util::ser::{Writeable, Writer, BigSize}; use crate::util::test_utils; @@ -35,11 +35,12 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1; -use bitcoin::secp256k1::{Secp256k1, SecretKey}; +use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey}; use crate::io; use crate::prelude::*; use core::default::Default; +use bitcoin::hashes::hex::FromHex; use crate::ln::functional_test_utils::*; @@ -966,6 +967,146 @@ fn test_always_create_tlv_format_onion_payloads() { } } +#[test] +fn test_trampoline_onion_payload_construction_vectors() { + // As per https://github.com/lightning/bolts/blob/c01d2e6267d4a8d1095f0f1188970055a9a22d29/bolt04/trampoline-payment-onion-test.json#L3 + + let trampoline_payloads = vec![ + // Carol + OutboundOnionPayload::TrampolineForward { + amt_to_forward: 100000000, + outgoing_cltv_value: 800000, + outgoing_node_id: PublicKey::from_slice(&>::from_hex("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()).unwrap(), + }, + + // Eve + OutboundOnionPayload::Receive { + payment_data: Some(FinalOnionHopData { + payment_secret: PaymentSecret([0x2a; 32]), + total_msat: 100000000 + }), + payment_metadata: None, + keysend_preimage: None, + custom_tlvs: vec![], + sender_intended_htlc_amt_msat: 100000000, + cltv_expiry_height: 800000, + trampoline_packet: None, + } + ]; + + let trampoline_hops = Path { hops: vec![ + // Carol + RouteHop { + pubkey: PublicKey::from_slice(&>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()).unwrap(), + node_features: NodeFeatures::empty(), + short_channel_id: 0, + channel_features: ChannelFeatures::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + maybe_announced_channel: false, + }, + + // Eve + RouteHop { + pubkey: PublicKey::from_slice(&>::from_hex("02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145").unwrap()).unwrap(), + node_features: NodeFeatures::empty(), + short_channel_id: 0, + channel_features: ChannelFeatures::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + maybe_announced_channel: false, + } + ], blinded_tail: None }; + + let slice_to_hex = |slice: &[u8]| { + slice.iter() + .map(|b| format!("{:02x}", b).to_string()) + .collect::() + }; + + let carol_payload_hex = slice_to_hex(&trampoline_payloads[0].encode()); + println!("Carol Payload Hex: {}", carol_payload_hex); + assert_eq!(carol_payload_hex, "2e020405f5e10004030c35000e2102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145"); + + let eve_payload_hex = slice_to_hex(&trampoline_payloads[1].encode()); + println!("Eve Payload Hex: {}", eve_payload_hex); + assert_eq!(eve_payload_hex, "31020405f5e10004030c350008242a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a05f5e100"); + + let trampoline_session_key = SecretKey::from_slice(&[0x03; 32]).unwrap(); + let trampoline_onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &trampoline_hops, &trampoline_session_key).unwrap(); + let associated_data = PaymentHash([0x42; 32]); + let trampoline_onion_packet = onion_utils::construct_variable_length_onion_packet(trampoline_payloads, trampoline_onion_keys, [0u8; 32], &associated_data, 161).unwrap(); + let trampoline_onion_packet_hex = slice_to_hex(&trampoline_onion_packet.encode()); + println!("Trampoline Onion Packet Hex: {}", trampoline_onion_packet_hex); + assert_eq!(trampoline_onion_packet_hex, "0002531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe3371860c0749bfd613056cfc5718beecc25a2f255fc7abbea3cd75ff820e9d30807d19b30f33626452fa54bb2d822e918558ed3e6714deb3f9a2a10895e7553c6f088c9a852043530dbc9abcc486030894364b205f5de60171b451ff462664ebce23b672579bf2a444ebfe0a81875c26d2fa16d426795b9b02ccbc4bdf909c583f0c2ebe9136510645917153ecb05181ca0c1b207824578ee841804a148f4c3df7306dcea52d94222907c9187bc31c0880fc084f0d88716e195c0abe7672d15217623"); + + let outer_payloads = vec![ + // Bob + OutboundOnionPayload::Forward { + short_channel_id: 1105, + amt_to_forward: 100005000, + outgoing_cltv_value: 800250, + }, + + // Carol + OutboundOnionPayload::Receive { + payment_data: Some(FinalOnionHopData { + payment_secret: PaymentSecret([0x2b; 32]), + total_msat: 100005000, + }), + payment_metadata: None, + keysend_preimage: None, + custom_tlvs: vec![], + sender_intended_htlc_amt_msat: 100005000, + cltv_expiry_height: 800250, + trampoline_packet: Some(trampoline_onion_packet), + } + ]; + + let outer_hops = Path { + hops: vec![ + // Bob + RouteHop { + pubkey: PublicKey::from_slice(&>::from_hex("0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c").unwrap()).unwrap(), + node_features: NodeFeatures::empty(), + short_channel_id: 0, + channel_features: ChannelFeatures::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + maybe_announced_channel: false, + }, + + // Carol + RouteHop { + pubkey: PublicKey::from_slice(&>::from_hex("027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007").unwrap()).unwrap(), + node_features: NodeFeatures::empty(), + short_channel_id: 0, + channel_features: ChannelFeatures::empty(), + fee_msat: 0, + cltv_expiry_delta: 0, + maybe_announced_channel: false, + }, + ], + blinded_tail: None, + }; + + let bob_payload_hex = slice_to_hex(&outer_payloads[0].encode()); + println!("Bob Payload Hex: {}", bob_payload_hex); + assert_eq!(bob_payload_hex, "15020405f5f48804030c35fa06080000000000000451"); + + let carol_payload_hex = slice_to_hex(&outer_payloads[1].encode()); + println!("Carol Outer Payload Hex: {}", carol_payload_hex); + assert_eq!(carol_payload_hex, "fd0116020405f5f48804030c35fa08242b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b05f5f48814e30002531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe3371860c0749bfd613056cfc5718beecc25a2f255fc7abbea3cd75ff820e9d30807d19b30f33626452fa54bb2d822e918558ed3e6714deb3f9a2a10895e7553c6f088c9a852043530dbc9abcc486030894364b205f5de60171b451ff462664ebce23b672579bf2a444ebfe0a81875c26d2fa16d426795b9b02ccbc4bdf909c583f0c2ebe9136510645917153ecb05181ca0c1b207824578ee841804a148f4c3df7306dcea52d94222907c9187bc31c0880fc084f0d88716e195c0abe7672d15217623"); + + let outer_session_key = SecretKey::from_slice(&[0x04; 32]).unwrap(); + let outer_onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &outer_hops, &outer_session_key).unwrap(); + let outer_onion_prng_seed = onion_utils::gen_pad_from_shared_secret(&outer_session_key.secret_bytes()); + let outer_onion_packet = onion_utils::construct_onion_packet(outer_payloads, outer_onion_keys, outer_onion_prng_seed, &associated_data).unwrap(); + let outer_onion_packet_hex = slice_to_hex(&outer_onion_packet.encode()); + println!("Outer Onion Packet Hex: {}", outer_onion_packet_hex); + assert_eq!(outer_onion_packet_hex, "0003462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b9149ce01cce1709194109ab5940379a84297ab6620025c770527dd8537997e2528082b984fe078a5667978a573abeaf7977d9b8b6ee4f124d3352f7eea52cc66c0e76b8f6d7a25d4501a04ae190b17baff8e6378b36f165815f714559dfef275278eba897f5f229be70fc8a1980cf859d1c25fe90c77f006419770e19d29ba80be8f613d039dd05600734e0d1e218af441fe30877e717a26b7b37c2c071d62bf6d61dd17f7abfb81546d2c722c9a6dc581aa97fb6f3b513e5fbaf0d669fbf0714b2b016a0a8e356d55f267fa144f7501792f2a59269c5a22e555a914e2eb71eba5af519564f246cf58983ea3fa2674e3ab7d9969d8dffbb2bda2b2752657417937d46601eb8ebf1837221d4bdf55a4d6a97ecffde5a09bd409717fa19e440e55d775890aed89f72e65af515757e94a9b501e6bad048af55e1583adb2960a84f60fb5efd0352e77a34045fc6b221498f62810bd8294d995d9f513696f8633f29faaa9668d0c6fa0d0dd7fa13e2c185572485762bd2810dc12187f521fbefa9c320762ac1e107f7988d81c6ee201ab68a95d45d578027e271b6526154317877037dca17134ccd955a22a8481b8e1996d896fc4bf006154ed18ef279d4f255e3f1233d037aea2560011069a0ae56d6bfdd8327054ded12d85d120b8982bff970986db333baae7c95f85677726a8f74cc8bd1e5aca3d433c113048305ecce8e35caf0485a53df00284b52b42291a9ffe686b96442135b3107d8856bc652d674ee9a148e79b02c9972d3ca8c2b02609f3b358c4a67c540ba6769c4d83169bceda640b1d18b74d12b6df605b417dacf6f82d79d43bb40920898f818dc8344c036ae9c8bbf9ef52ea1ccf225c8825a4d8503df190b999e15a4be34c9d7bbf60d3b93bb7d6559f4a5916f5e40c3defeeca9337ccd1280e46d6727c5c91c2d898b685543d4ca7cfee23981323c43260b6387e7febb0fffb200a8c974ef36b3253d0fb9fe0c1c6017f2dbbdc169f3f061d9781521e8118164aeec31c3e59c199016f1025c295d8f7bdeb627d357105a2708c4c3a856b9e83ff37ed69f59f2d2e464ed1db5882925ebe2493a7ddb707e1a308fa445172a24b3ea60732f75f5c69b41fc11467ee93f37c9a6f7285ba42f716e2a0e30909056ea3e4f7985d14ca9ab280cc184ce98e2a0722d0447aa1a2eedc5e53ddfa53731df7eced406b10627b0bebd768a30bde0d470c0f1d10adc070f8d3029cacceec74e4833f4dc8c52c3f41733f5f896fceb425d0737e717a63bfb033df46286d99594dd01e2bd0a942ab792874177b32842f4833bc0340ddb74852e9cd6f29f1d997a4a4bf05dd5d12011f95e6ce18928e3a9b83b24d15f989bdf43370bcc657c3ac6601eaf5e951efdbd7ee69b1623dc5039b2dfc640692378ef032f17bc36cc00293ad90b7e18f5feb8f287a7061ed9713929aed9b14b8d566199fc7822b1c38daa16b6d83077b10af0e2b6e531ccc34ea248ea593128c9ff17defcee6618c29cd2d93cfed99b90319104b1fdcfea91e98b41d792782840fb7b25280d8565b0bcd874e79b1b323139e7fc88eb5f80f690ce30fcd81111076adb31de6aeced879b538c0b5f2b74c027cc582a540133952cb021424510312f13e15d403f700f3e15b41d677c5a1e7c4e692c5880cb4522c48e993381996a29615d2956781509cd74aec6a3c73b8536d1817e473dad4cbb1787e046606b692a44e5d21ef6b5219658b002f674367e90a2b610924e9ac543362257d4567728f2e61f61231cb5d7816e100bb6f6bd9a42329b728b18d7a696711650c16fd476e2f471f38af0f6b00d45c6ecadbc5b61e5bb642046fd1c8ba6168b9499ec8acb3fda46cc37e2e442629cbdf"); +} + fn do_test_fail_htlc_backwards_with_reason(failure_code: FailureCode) { let chanmon_cfgs = create_chanmon_cfgs(2); diff --git a/lightning/src/ln/onion_utils.rs b/lightning/src/ln/onion_utils.rs index 15088442a2f..f7cba7a48f2 100644 --- a/lightning/src/ln/onion_utils.rs +++ b/lightning/src/ln/onion_utils.rs @@ -268,6 +268,19 @@ pub(super) fn construct_onion_packet( payloads, onion_keys, FixedSizeOnionPacket(packet_data), Some(associated_data)) } +pub(super) fn construct_variable_length_onion_packet( + payloads: Vec, onion_keys: Vec, prng_seed: [u8; 32], + associated_data: &PaymentHash, length: u16, +) -> Result { + let mut packet_data = vec![0u8; length as usize]; + + let mut chacha = ChaCha20::new(&prng_seed, &[0; 8]); + chacha.process(&vec![0u8; length as usize], &mut packet_data); + + construct_onion_packet_with_init_noise::<_, _>( + payloads, onion_keys, packet_data, Some(associated_data)) +} + #[cfg(test)] /// Used in testing to write bogus `BogusOnionHopData` as well as `RawOnionHopData`, which is /// otherwise not representable in `msgs::OnionHopData`. @@ -299,6 +312,14 @@ impl AsMut<[u8]> for FixedSizeOnionPacket { } } +pub(crate) struct VariableLengthOnionPacket(pub(crate) Vec); + +impl AsMut<[u8]> for VariableLengthOnionPacket { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0 + } +} + pub(crate) fn payloads_serialized_length(payloads: &Vec) -> usize { payloads.iter().map(|p| p.serialized_length() + 32 /* HMAC */).sum() }