Skip to content

Serialize blinded Trampoline hops #3007

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 122 additions & 6 deletions lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1746,9 +1746,9 @@ pub struct FinalOnionHopData {

mod fuzzy_internal_msgs {
use bitcoin::secp256k1::PublicKey;
use crate::blinded_path::payment::{PaymentConstraints, PaymentContext, PaymentRelay};
use crate::blinded_path::payment::{BlindedPaymentPath, PaymentConstraints, PaymentContext, PaymentRelay};
use crate::types::payment::{PaymentPreimage, PaymentSecret};
use crate::types::features::BlindedHopFeatures;
use crate::types::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
use super::{FinalOnionHopData, TrampolineOnionPacket};

#[allow(unused_imports)]
Expand Down Expand Up @@ -1830,14 +1830,41 @@ mod fuzzy_internal_msgs {
}
}

pub(crate) enum OutboundTrampolinePayload {
pub(crate) enum OutboundTrampolinePayload<'a> {
#[allow(unused)]
Forward {
/// 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
/// The node id to which the trampoline node must find a route.
outgoing_node_id: PublicKey,
},
#[allow(unused)]
/// This is the last Trampoline hop, whereupon the Trampoline forward mechanism is exited,
/// and payment data is relayed using non-Trampoline blinded hops
LegacyBlindedPathEntry {
/// The value, in msat, of the payment after this hop's fee is deducted.
amt_to_forward: u64,
outgoing_cltv_value: u32,
/// List of blinded path options the last trampoline hop may choose to route through.
payment_paths: Vec<BlindedPaymentPath>,
/// If applicable, features of the BOLT12 invoice being paid.
invoice_features: Option<Bolt12InvoiceFeatures>,
},
#[allow(unused)]
BlindedForward {
encrypted_tlvs: &'a Vec<u8>,
intro_node_blinding_point: Option<PublicKey>,
},
#[allow(unused)]
BlindedReceive {
sender_intended_htlc_amt_msat: u64,
total_msat: u64,
cltv_expiry_height: u32,
encrypted_tlvs: &'a Vec<u8>,
intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
keysend_preimage: Option<PaymentPreimage>,
custom_tlvs: &'a Vec<(u64, Vec<u8>)>,
}
}

Expand Down Expand Up @@ -2754,7 +2781,7 @@ impl<'a> Writeable for OutboundOnionPayload<'a> {
}
}

impl Writeable for OutboundTrampolinePayload {
impl<'a> Writeable for OutboundTrampolinePayload<'a> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
match self {
Self::Forward { amt_to_forward, outgoing_cltv_value, outgoing_node_id } => {
Expand All @@ -2763,6 +2790,41 @@ impl Writeable for OutboundTrampolinePayload {
(4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(14, outgoing_node_id, required)
});
},
Self::LegacyBlindedPathEntry { amt_to_forward, outgoing_cltv_value, payment_paths, invoice_features } => {
let mut blinded_path_serialization = [0u8; 2048]; // Fixed-length buffer on the stack
let serialization_length = {
let buffer_size = blinded_path_serialization.len();
let mut blinded_path_slice = &mut blinded_path_serialization[..];
for current_payment_path in payment_paths {
current_payment_path.inner_blinded_path().write(&mut blinded_path_slice)?;
current_payment_path.payinfo.write(&mut blinded_path_slice)?;
}
buffer_size - blinded_path_slice.len()
};
let blinded_path_serialization = &blinded_path_serialization[..serialization_length];
_encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedBigSize(*amt_to_forward), required),
(4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
(21, invoice_features.as_ref().map(|m| WithoutLength(m)), option),
(22, WithoutLength(blinded_path_serialization), required)
});
},
Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point} => {
_encode_varint_length_prefixed_tlv!(w, {
(10, **encrypted_tlvs, required_vec),
(12, intro_node_blinding_point, option)
});
},
Self::BlindedReceive { sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, encrypted_tlvs, intro_node_blinding_point, keysend_preimage, custom_tlvs } => {
_encode_varint_length_prefixed_tlv!(w, {
(2, HighZeroBytesDroppedBigSize(*sender_intended_htlc_amt_msat), required),
(4, HighZeroBytesDroppedBigSize(*cltv_expiry_height), required),
(10, **encrypted_tlvs, required_vec),
(12, intro_node_blinding_point, option),
(18, HighZeroBytesDroppedBigSize(*total_msat), required),
(20, keysend_preimage, option)
}, custom_tlvs.iter());
}
}
Ok(())
Expand Down Expand Up @@ -3302,7 +3364,7 @@ mod tests {
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, TrampolineOnionPacket};
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, CommonOpenChannelFields, CommonAcceptChannelFields, OutboundTrampolinePayload, TrampolineOnionPacket};
use crate::ln::msgs::SocketAddress;
use crate::routing::gossip::{NodeAlias, NodeId};
use crate::util::ser::{BigSize, FixedLengthReader, Hostname, LengthReadable, Readable, ReadableArgs, TransactionU16LenLimited, Writeable};
Expand All @@ -3328,6 +3390,8 @@ mod tests {

#[cfg(feature = "std")]
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
use types::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
use crate::blinded_path::payment::{BlindedPayInfo, BlindedPaymentPath};
#[cfg(feature = "std")]
use crate::ln::msgs::SocketAddressParseError;

Expand Down Expand Up @@ -4675,6 +4739,58 @@ mod tests {
assert_eq!(encoded_trampoline_packet, expected_eclair_trampoline_packet);
}

#[test]
fn encoding_outbound_trampoline_payload() {
let mut trampoline_features = Bolt12InvoiceFeatures::empty();
trampoline_features.set_basic_mpp_optional();
let introduction_node = PublicKey::from_slice(&<Vec<u8>>::from_hex("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()).unwrap();
let blinding_point = PublicKey::from_slice(&<Vec<u8>>::from_hex("02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619").unwrap()).unwrap();
let trampoline_payload = OutboundTrampolinePayload::LegacyBlindedPathEntry {
amt_to_forward: 150_000_000,
outgoing_cltv_value: 800_000,
payment_paths: vec![
BlindedPaymentPath::from_raw(
introduction_node,
blinding_point,
vec![],
BlindedPayInfo{
fee_base_msat: 500,
fee_proportional_millionths: 1_000,
cltv_expiry_delta: 36,
htlc_minimum_msat: 1,
htlc_maximum_msat: 500_000_000,
features: BlindedHopFeatures::empty(),
}
)
],
invoice_features: Some(trampoline_features),
};
let serialized_payload = trampoline_payload.encode().to_lower_hex_string();
assert_eq!(serialized_payload, "71020408f0d18004030c35001503020000165f032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e66868099102eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f28368661900000001f4000003e800240000000000000001000000001dcd65000000");
}

#[test]
fn encode_trampoline_blinded_path_payload() {
let trampoline_payload_eve = OutboundTrampolinePayload::BlindedReceive {
sender_intended_htlc_amt_msat: 150_000_000,
total_msat: 150_000_000,
cltv_expiry_height: 800_000,
encrypted_tlvs: &<Vec<u8>>::from_hex("bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c").unwrap(),
intro_node_blinding_point: None,
keysend_preimage: None,
custom_tlvs: &vec![],
};
let eve_payload = trampoline_payload_eve.encode().to_lower_hex_string();
assert_eq!(eve_payload, "e4020408f0d18004030c35000ad1bcd747394fbd4d99588da075a623316e15a576df5bc785cccc7cd6ec7b398acce6faf520175f9ec920f2ef261cdb83dc28cc3a0eeb970107b3306489bf771ef5b1213bca811d345285405861d08a655b6c237fa247a8b4491beee20c878a60e9816492026d8feb9dafa84585b253978db6a0aa2945df5ef445c61e801fb82f43d5f00716baf9fc9b3de50bc22950a36bda8fc27bfb1242e5860c7e687438d4133e058770361a19b6c271a2a07788d34dccc27e39b9829b061a4d960eac4a2c2b0f4de506c24f9af3868c0aff6dda27281c120408f0d180");

let trampoline_payload_dave = OutboundTrampolinePayload::BlindedForward {
encrypted_tlvs: &<Vec<u8>>::from_hex("0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a").unwrap(),
intro_node_blinding_point: Some(PublicKey::from_slice(&<Vec<u8>>::from_hex("02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e").unwrap()).unwrap()),
};
let dave_payload = trampoline_payload_dave.encode().to_lower_hex_string();
assert_eq!(dave_payload, "690a440ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a0c2102988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e")
}

#[test]
fn query_channel_range_end_blocknum() {
let tests: Vec<(u32, u32, u32)> = vec![
Expand Down
Loading