Skip to content

Commit 5db2070

Browse files
committed
De/serialize custom TLVs on OnionHopData::FinalNode
When serialized, the TLVs in `OnionHopData`, unlike a normal TLV stream, are prefixed with the length of the stream. To allow a user to add arbitrary custom TLVs, we aren't able to communicate to our serialization macros exactly which fields to expect, so this commit adds new macro variants to allow appending an extra set of bytes (and modifying the prefixed length accordingly). Because the keysend preimage TLV has a type number in the custom type range, and a user's TLVs may have type numbers above and/or below keysend's type number, and because TLV streams must be serialized in increasing order by type number, this commit also ensures the keysend TLV is properly sorted/serialized amongst the custom TLVs.
1 parent 98642a0 commit 5db2070

File tree

4 files changed

+134
-11
lines changed

4 files changed

+134
-11
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2458,7 +2458,7 @@ where
24582458
msg: "Got non final data with an HMAC of 0",
24592459
});
24602460
},
2461-
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata } => {
2461+
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata, custom_tlvs } => {
24622462
if let Some(payment_preimage) = keysend_preimage {
24632463
// We need to check that the sender knows the keysend preimage before processing this
24642464
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X

lightning/src/ln/msgs.rs

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use crate::io_extras::read_to_end;
4343

4444
use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
4545
use crate::util::logger;
46-
use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited};
46+
use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
4747

4848
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
4949

@@ -1429,6 +1429,8 @@ mod fuzzy_internal_msgs {
14291429
payment_data: Option<FinalOnionHopData>,
14301430
payment_metadata: Option<Vec<u8>>,
14311431
keysend_preimage: Option<PaymentPreimage>,
1432+
/// Serialization will fail if this is not a valid TLV stream
1433+
custom_tlvs: Option<Vec<(u64, Vec<u8>)>>,
14321434
},
14331435
}
14341436

@@ -1954,14 +1956,19 @@ impl Writeable for OnionHopData {
19541956
(6, short_channel_id, required)
19551957
});
19561958
},
1957-
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
1959+
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage, ref custom_tlvs } => {
1960+
let preimage = if let Some(ref preimage) = keysend_preimage {
1961+
Some((5482373484, preimage.encode()))
1962+
} else { None };
1963+
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().flatten().chain(preimage.iter()).collect();
1964+
custom_tlvs.sort_by_key(|(typ, _)| *typ);
1965+
19581966
_encode_varint_length_prefixed_tlv!(w, {
19591967
(2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
19601968
(4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
19611969
(8, payment_data, option),
1962-
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
1963-
(5482373484, keysend_preimage, option)
1964-
});
1970+
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
1971+
}, custom_tlvs.iter());
19651972
},
19661973
}
19671974
Ok(())
@@ -1976,14 +1983,24 @@ impl Readable for OnionHopData {
19761983
let mut payment_data: Option<FinalOnionHopData> = None;
19771984
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
19781985
let mut keysend_preimage: Option<PaymentPreimage> = None;
1979-
read_tlv_fields!(r, {
1986+
let mut custom_tlvs = Vec::new();
1987+
1988+
let tlv_len = BigSize::read(r)?;
1989+
let rd = FixedLengthReader::new(r, tlv_len.0);
1990+
decode_tlv_stream_with_custom_tlv_decode!(rd, {
19801991
(2, amt, required),
19811992
(4, cltv_value, required),
19821993
(6, short_id, option),
19831994
(8, payment_data, option),
19841995
(16, payment_metadata, option),
19851996
// See https://github.com/lightning/blips/blob/master/blip-0003.md
19861997
(5482373484, keysend_preimage, option)
1998+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
1999+
if msg_type < 1 << 16 { return Ok(false) }
2000+
let mut value = Vec::new();
2001+
msg_reader.read_to_end(&mut value)?;
2002+
custom_tlvs.push((msg_type, value));
2003+
Ok(true)
19872004
});
19882005

19892006
let format = if let Some(short_channel_id) = short_id {
@@ -2002,6 +2019,7 @@ impl Readable for OnionHopData {
20022019
payment_data,
20032020
payment_metadata: payment_metadata.map(|w| w.0),
20042021
keysend_preimage,
2022+
custom_tlvs: if custom_tlvs.is_empty() { None } else { Some(custom_tlvs) },
20052023
}
20062024
};
20072025

@@ -3543,6 +3561,7 @@ mod tests {
35433561
payment_data: None,
35443562
payment_metadata: None,
35453563
keysend_preimage: None,
3564+
custom_tlvs: None,
35463565
},
35473566
amt_to_forward: 0x0badf00d01020304,
35483567
outgoing_cltv_value: 0xffffffff,
@@ -3567,6 +3586,7 @@ mod tests {
35673586
}),
35683587
payment_metadata: None,
35693588
keysend_preimage: None,
3589+
custom_tlvs: None,
35703590
},
35713591
amt_to_forward: 0x0badf00d01020304,
35723592
outgoing_cltv_value: 0xffffffff,
@@ -3582,13 +3602,81 @@ mod tests {
35823602
}),
35833603
payment_metadata: None,
35843604
keysend_preimage: None,
3605+
custom_tlvs: None,
35853606
} = msg.format {
35863607
assert_eq!(payment_secret, expected_payment_secret);
35873608
} else { panic!(); }
35883609
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
35893610
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
35903611
}
35913612

3613+
#[test]
3614+
fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
3615+
// If custom TLVs have type number within the range reserved for protocol, treat them as if
3616+
// they're unknown
3617+
let bad_type_range_tlvs = vec![
3618+
((1 << 16) - 4, vec![42]),
3619+
((1 << 16) - 2, vec![42; 32]),
3620+
];
3621+
let mut msg = msgs::OnionHopData {
3622+
format: OnionHopDataFormat::FinalNode {
3623+
payment_data: None,
3624+
payment_metadata: None,
3625+
keysend_preimage: None,
3626+
custom_tlvs: Some(bad_type_range_tlvs),
3627+
},
3628+
amt_to_forward: 0x0badf00d01020304,
3629+
outgoing_cltv_value: 0xffffffff,
3630+
};
3631+
let encoded_value = msg.encode();
3632+
assert!(msgs::OnionHopData::read(&mut Cursor::new(&encoded_value[..])).is_err());
3633+
let good_type_range_tlvs = vec![
3634+
((1 << 16) - 3, vec![42]),
3635+
((1 << 16) - 1, vec![42; 32]),
3636+
];
3637+
if let OnionHopDataFormat::FinalNode { ref mut custom_tlvs, .. } = msg.format {
3638+
*custom_tlvs = Some(good_type_range_tlvs.clone());
3639+
}
3640+
let encoded_value = msg.encode();
3641+
msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
3642+
match msg.format {
3643+
OnionHopDataFormat::FinalNode { custom_tlvs, .. } => assert!(custom_tlvs.is_none()),
3644+
_ => panic!(),
3645+
}
3646+
}
3647+
3648+
#[test]
3649+
fn encoding_final_onion_hop_data_with_custom_tlvs() {
3650+
let expected_custom_tlvs = vec![
3651+
(5482373483, vec![0x12, 0x34]),
3652+
(5482373487, vec![0x42u8; 8]),
3653+
];
3654+
let mut msg = msgs::OnionHopData {
3655+
format: OnionHopDataFormat::FinalNode {
3656+
payment_data: None,
3657+
payment_metadata: None,
3658+
keysend_preimage: None,
3659+
custom_tlvs: Some(expected_custom_tlvs.clone()),
3660+
},
3661+
amt_to_forward: 0x0badf00d01020304,
3662+
outgoing_cltv_value: 0xffffffff,
3663+
};
3664+
let encoded_value = msg.encode();
3665+
let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
3666+
assert_eq!(encoded_value, target_value);
3667+
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3668+
if let OnionHopDataFormat::FinalNode {
3669+
payment_data: None,
3670+
payment_metadata: None,
3671+
keysend_preimage: None,
3672+
custom_tlvs,
3673+
} = msg.format {
3674+
assert_eq!(custom_tlvs.unwrap(), expected_custom_tlvs);
3675+
} else { panic!(); }
3676+
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3677+
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
3678+
}
3679+
35923680
#[test]
35933681
fn query_channel_range_end_blocknum() {
35943682
let tests: Vec<(u32, u32, u32)> = vec![

lightning/src/ln/onion_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
172172
} else { None },
173173
payment_metadata: recipient_onion.payment_metadata.take(),
174174
keysend_preimage: *keysend_preimage,
175+
custom_tlvs: recipient_onion.custom_tlvs.take(),
175176
}
176177
} else {
177178
msgs::OnionHopDataFormat::NonFinalNode {

lightning/src/util/ser_macros.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,16 @@ macro_rules! _check_encoded_tlv_order {
117117
/// [`Writer`]: crate::util::ser::Writer
118118
#[macro_export]
119119
macro_rules! encode_tlv_stream {
120+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => {
121+
$crate::_encode_tlv_stream!($stream, {$(($type, $field, $fieldty)),*})
122+
}
123+
}
124+
125+
/// Implementation of [`encode_tlv_stream`].
126+
/// This is exported for use by other exported macros, do not use directly.
127+
#[doc(hidden)]
128+
#[macro_export]
129+
macro_rules! _encode_tlv_stream {
120130
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => { {
121131
#[allow(unused_imports)]
122132
use $crate::{
@@ -138,7 +148,21 @@ macro_rules! encode_tlv_stream {
138148
$crate::_check_encoded_tlv_order!(last_seen, $type, $fieldty);
139149
)*
140150
}
141-
} }
151+
} };
152+
($stream: expr, $tlvs: expr) => { {
153+
for tlv in $tlvs {
154+
let (typ, value): &&(u64, Vec<u8>) = tlv;
155+
$crate::_encode_tlv!($stream, *typ, *value, vec_type);
156+
}
157+
158+
#[cfg(debug_assertions)] {
159+
let mut last_seen: Option<u64> = None;
160+
for tlv in $tlvs {
161+
let (typ, _): &&(u64, Vec<u8>) = tlv;
162+
$crate::_check_encoded_tlv_order!(last_seen, *typ, vec_type);
163+
}
164+
}
165+
} };
142166
}
143167

144168
/// Adds the length of the serialized field to a [`LengthCalculatingWriter`].
@@ -192,18 +216,27 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
192216
#[macro_export]
193217
macro_rules! _encode_varint_length_prefixed_tlv {
194218
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => { {
219+
_encode_varint_length_prefixed_tlv!($stream, {$(($type, $field, $fieldty)),*}, &[])
220+
} };
221+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}, $extra_tlvs: expr) => { {
195222
use $crate::util::ser::BigSize;
223+
use alloc::vec::Vec;
196224
let len = {
197225
#[allow(unused_mut)]
198226
let mut len = $crate::util::ser::LengthCalculatingWriter(0);
199227
$(
200228
$crate::_get_varint_length_prefixed_tlv_length!(len, $type, $field, $fieldty);
201229
)*
230+
for tlv in $extra_tlvs {
231+
let (typ, value): &&(u64, Vec<u8>) = tlv;
232+
$crate::_get_varint_length_prefixed_tlv_length!(len, *typ, *value, vec_type);
233+
}
202234
len.0
203235
};
204236
BigSize(len as u64).write($stream)?;
205-
$crate::encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
206-
} }
237+
$crate::_encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
238+
$crate::_encode_tlv_stream!($stream, $extra_tlvs);
239+
} };
207240
}
208241

209242
/// Errors if there are missing required TLV types between the last seen type and the type currently being processed.
@@ -766,7 +799,8 @@ macro_rules! _init_and_read_tlv_fields {
766799
///
767800
/// For example,
768801
/// ```
769-
/// # use lightning::impl_writeable_tlv_based;
802+
/// # use lightning::{impl_writeable_tlv_based, _encode_varint_length_prefixed_tlv};
803+
/// # extern crate alloc;
770804
/// struct LightningMessage {
771805
/// tlv_integer: u32,
772806
/// tlv_default_integer: u32,

0 commit comments

Comments
 (0)