From c974225fa4783c3fcd9568db56952abd06ae932d Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Nov 2023 13:23:15 -0800 Subject: [PATCH 1/3] Add trampoline feature flag. --- lightning/src/ln/features.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lightning/src/ln/features.rs b/lightning/src/ln/features.rs index 79f869a4c58..04ce90445f9 100644 --- a/lightning/src/ln/features.rs +++ b/lightning/src/ln/features.rs @@ -66,6 +66,8 @@ //! for more info). //! - `Keysend` - send funds to a node without an invoice //! (see the [`Keysend` feature assignment proposal](https://github.com/lightning/bolts/issues/605#issuecomment-606679798) for more information). +//! - `Trampoline` - supports receiving and forwarding Trampoline payments +//! (see the [`Trampoline` feature proposal](https://github.com/lightning/bolts/pull/836) for more information). //! //! LDK knows about the following features, but does not support them: //! - `AnchorsNonzeroFeeHtlcTx` - the initial version of anchor outputs, which was later found to be @@ -152,6 +154,8 @@ mod sealed { ChannelType | SCIDPrivacy, // Byte 6 ZeroConf, + // Byte 7 + Trampoline, ]); define_context!(NodeContext, [ // Byte 0 @@ -168,6 +172,8 @@ mod sealed { ChannelType | SCIDPrivacy, // Byte 6 ZeroConf | Keysend, + // Byte 7 + Trampoline, ]); define_context!(ChannelContext, []); define_context!(Bolt11InvoiceContext, [ @@ -185,6 +191,8 @@ mod sealed { , // Byte 6 PaymentMetadata, + // Byte 7 + Trampoline, ]); define_context!(OfferContext, []); define_context!(InvoiceRequestContext, []); @@ -420,6 +428,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, [InitContext, NodeContext, Bolt11InvoiceContext], + "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 c84d721c99618c273666298ba4633c4e273820ea Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Thu, 21 Mar 2024 17:44:40 -0700 Subject: [PATCH 2/3] Introduce TrampolineOnionPacket. --- lightning/src/ln/msgs.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index d291ac8664a..cf569f4c493 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1765,6 +1765,52 @@ impl fmt::Debug for OnionPacket { } } +/// BOLT 4 onion packet including hop data for the next peer. +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct TrampolineOnionPacket { + /// Bolt 04 version number + pub version: u8, + /// A random sepc256k1 point, used to build the ECDH shared secret to decrypt hop_data + pub public_key: PublicKey, + /// Encrypted payload for the next hop + // + // Unlike the onion packets used for payments, Trampoline onion packets have to be shorter than + // 1300 bytes. The expected default is 650 bytes. + // TODO: if 650 ends up being the most common size, optimize this to be: + // enum { ThirteenHundred([u8; 650]), VarLen(Vec) } + pub hop_data: Vec, + /// HMAC to verify the integrity of hop_data + pub hmac: [u8; 32], +} + +impl onion_utils::Packet for TrampolineOnionPacket { + type Data = Vec; + fn new(public_key: PublicKey, hop_data: Vec, hmac: [u8; 32]) -> Self { + Self { + version: 0, + public_key, + hop_data, + hmac, + } + } +} + +impl Writeable for TrampolineOnionPacket { + fn write(&self, w: &mut W) -> Result<(), io::Error> { + self.version.write(w)?; + self.public_key.write(w)?; + w.write_all(&self.hop_data)?; + self.hmac.write(w)?; + Ok(()) + } +} + +impl Debug for TrampolineOnionPacket { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("TrampolineOnionPacket version {} with hmac {:?}", self.version, &self.hmac[..])) + } +} + #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub(crate) struct OnionErrorPacket { // This really should be a constant size slice, but the spec lets these things be up to 128KB? From f15c538e1f3668793089320643637ab94db6e62c Mon Sep 17 00:00:00 2001 From: Arik Sosman Date: Tue, 28 Nov 2023 12:17:17 -0800 Subject: [PATCH 3/3] Add Trampoline variant to OutboundOnionPayload. --- lightning/src/ln/msgs.rs | 82 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index cf569f4c493..bb6d0256060 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -1654,7 +1654,7 @@ mod fuzzy_internal_msgs { use crate::prelude::*; use crate::ln::{PaymentPreimage, PaymentSecret}; use crate::ln::features::BlindedHopFeatures; - use super::FinalOnionHopData; + use super::{FinalOnionHopData, TrampolineOnionPacket}; // These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize // them from untrusted input): @@ -1698,6 +1698,13 @@ mod fuzzy_internal_msgs { amt_to_forward: u64, outgoing_cltv_value: u32, }, + #[allow(unused)] + TrampolineEntrypoint { + amt_to_forward: u64, + outgoing_cltv_value: u32, + multipath_trampoline_data: Option, + trampoline_packet: TrampolineOnionPacket, + }, Receive { payment_data: Option, payment_metadata: Option>, @@ -2524,6 +2531,17 @@ impl Writeable for OutboundOnionPayload { (6, short_channel_id, required) }); }, + Self::TrampolineEntrypoint { + amt_to_forward, outgoing_cltv_value, ref multipath_trampoline_data, + ref trampoline_packet + } => { + _encode_varint_length_prefixed_tlv!(w, { + (2, HighZeroBytesDroppedBigSize(*amt_to_forward), required), + (4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required), + (8, multipath_trampoline_data, option), + (20, trampoline_packet, required) + }); + }, Self::Receive { ref payment_data, ref payment_metadata, ref keysend_preimage, sender_intended_htlc_amt_msat, cltv_expiry_height, ref custom_tlvs, @@ -3089,10 +3107,10 @@ mod tests { use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret}; use crate::ln::ChannelId; use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; - use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields}; + use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket}; use crate::ln::msgs::SocketAddress; use crate::routing::gossip::{NodeAlias, NodeId}; - use crate::util::ser::{Writeable, Readable, ReadableArgs, Hostname, TransactionU16LenLimited}; + use crate::util::ser::{BigSize, Hostname, Readable, ReadableArgs, TransactionU16LenLimited, Writeable}; use crate::util::test_utils; use bitcoin::hashes::hex::FromHex; @@ -4384,6 +4402,64 @@ 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 = TrampolineOnionPacket { + 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::TrampolineEntrypoint { + multipath_trampoline_data: None, + amt_to_forward: 0x0badf00d01020304, + outgoing_cltv_value: 0xffffffff, + trampoline_packet, + }; + let encoded_payload = msg.encode(); + + 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, 20); + + 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); + } + + #[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 = TrampolineOnionPacket { + 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![