Skip to content

Commit b54f20d

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 9f89be1 commit b54f20d

File tree

4 files changed

+137
-11
lines changed

4 files changed

+137
-11
lines changed

lightning/src/ln/channelmanager.rs

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

lightning/src/ln/msgs.rs

Lines changed: 97 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

@@ -1438,6 +1438,8 @@ mod fuzzy_internal_msgs {
14381438
payment_data: Option<FinalOnionHopData>,
14391439
payment_metadata: Option<Vec<u8>>,
14401440
keysend_preimage: Option<PaymentPreimage>,
1441+
/// Serialization will fail if this is not a valid TLV stream
1442+
custom_tlvs: Vec<(u64, Vec<u8>)>,
14411443
},
14421444
}
14431445

@@ -1965,14 +1967,22 @@ impl Writeable for OnionHopData {
19651967
(6, short_channel_id, required)
19661968
});
19671969
},
1968-
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
1970+
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage, ref custom_tlvs } => {
1971+
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
1972+
// to reject any reserved types in the experimental range if new ones are ever
1973+
// standardized.
1974+
let preimage = if let Some(ref preimage) = keysend_preimage {
1975+
Some((5482373484, preimage.encode()))
1976+
} else { None };
1977+
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(preimage.iter()).collect();
1978+
custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
1979+
19691980
_encode_varint_length_prefixed_tlv!(w, {
19701981
(2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
19711982
(4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
19721983
(8, payment_data, option),
1973-
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
1974-
(5482373484, keysend_preimage, option)
1975-
});
1984+
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
1985+
}, custom_tlvs.iter());
19761986
},
19771987
}
19781988
Ok(())
@@ -1987,14 +1997,24 @@ impl Readable for OnionHopData {
19871997
let mut payment_data: Option<FinalOnionHopData> = None;
19881998
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
19891999
let mut keysend_preimage: Option<PaymentPreimage> = None;
1990-
read_tlv_fields!(r, {
2000+
let mut custom_tlvs = Vec::new();
2001+
2002+
let tlv_len = BigSize::read(r)?;
2003+
let rd = FixedLengthReader::new(r, tlv_len.0);
2004+
decode_tlv_stream_with_custom_tlv_decode!(rd, {
19912005
(2, amt, required),
19922006
(4, cltv_value, required),
19932007
(6, short_id, option),
19942008
(8, payment_data, option),
19952009
(16, payment_metadata, option),
19962010
// See https://github.com/lightning/blips/blob/master/blip-0003.md
19972011
(5482373484, keysend_preimage, option)
2012+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
2013+
if msg_type < 1 << 16 { return Ok(false) }
2014+
let mut value = Vec::new();
2015+
msg_reader.read_to_end(&mut value)?;
2016+
custom_tlvs.push((msg_type, value));
2017+
Ok(true)
19982018
});
19992019

20002020
let format = if let Some(short_channel_id) = short_id {
@@ -2013,6 +2033,7 @@ impl Readable for OnionHopData {
20132033
payment_data,
20142034
payment_metadata: payment_metadata.map(|w| w.0),
20152035
keysend_preimage,
2036+
custom_tlvs,
20162037
}
20172038
};
20182039

@@ -3555,6 +3576,7 @@ mod tests {
35553576
payment_data: None,
35563577
payment_metadata: None,
35573578
keysend_preimage: None,
3579+
custom_tlvs: vec![],
35583580
},
35593581
amt_to_forward: 0x0badf00d01020304,
35603582
outgoing_cltv_value: 0xffffffff,
@@ -3579,6 +3601,7 @@ mod tests {
35793601
}),
35803602
payment_metadata: None,
35813603
keysend_preimage: None,
3604+
custom_tlvs: vec![],
35823605
},
35833606
amt_to_forward: 0x0badf00d01020304,
35843607
outgoing_cltv_value: 0xffffffff,
@@ -3594,13 +3617,81 @@ mod tests {
35943617
}),
35953618
payment_metadata: None,
35963619
keysend_preimage: None,
3620+
custom_tlvs: _,
35973621
} = msg.format {
35983622
assert_eq!(payment_secret, expected_payment_secret);
35993623
} else { panic!(); }
36003624
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
36013625
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
36023626
}
36033627

3628+
#[test]
3629+
fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
3630+
// If custom TLVs have type number within the range reserved for protocol, treat them as if
3631+
// they're unknown
3632+
let bad_type_range_tlvs = vec![
3633+
((1 << 16) - 4, vec![42]),
3634+
((1 << 16) - 2, vec![42; 32]),
3635+
];
3636+
let mut msg = msgs::OnionHopData {
3637+
format: OnionHopDataFormat::FinalNode {
3638+
payment_data: None,
3639+
payment_metadata: None,
3640+
keysend_preimage: None,
3641+
custom_tlvs: bad_type_range_tlvs,
3642+
},
3643+
amt_to_forward: 0x0badf00d01020304,
3644+
outgoing_cltv_value: 0xffffffff,
3645+
};
3646+
let encoded_value = msg.encode();
3647+
assert!(msgs::OnionHopData::read(&mut Cursor::new(&encoded_value[..])).is_err());
3648+
let good_type_range_tlvs = vec![
3649+
((1 << 16) - 3, vec![42]),
3650+
((1 << 16) - 1, vec![42; 32]),
3651+
];
3652+
if let OnionHopDataFormat::FinalNode { ref mut custom_tlvs, .. } = msg.format {
3653+
*custom_tlvs = good_type_range_tlvs.clone();
3654+
}
3655+
let encoded_value = msg.encode();
3656+
msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
3657+
match msg.format {
3658+
OnionHopDataFormat::FinalNode { custom_tlvs, .. } => assert!(custom_tlvs.is_empty()),
3659+
_ => panic!(),
3660+
}
3661+
}
3662+
3663+
#[test]
3664+
fn encoding_final_onion_hop_data_with_custom_tlvs() {
3665+
let expected_custom_tlvs = vec![
3666+
(5482373483, vec![0x12, 0x34]),
3667+
(5482373487, vec![0x42u8; 8]),
3668+
];
3669+
let mut msg = msgs::OnionHopData {
3670+
format: OnionHopDataFormat::FinalNode {
3671+
payment_data: None,
3672+
payment_metadata: None,
3673+
keysend_preimage: None,
3674+
custom_tlvs: expected_custom_tlvs.clone(),
3675+
},
3676+
amt_to_forward: 0x0badf00d01020304,
3677+
outgoing_cltv_value: 0xffffffff,
3678+
};
3679+
let encoded_value = msg.encode();
3680+
let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
3681+
assert_eq!(encoded_value, target_value);
3682+
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3683+
if let OnionHopDataFormat::FinalNode {
3684+
payment_data: None,
3685+
payment_metadata: None,
3686+
keysend_preimage: None,
3687+
custom_tlvs,
3688+
} = msg.format {
3689+
assert_eq!(custom_tlvs, expected_custom_tlvs);
3690+
} else { panic!(); }
3691+
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3692+
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
3693+
}
3694+
36043695
#[test]
36053696
fn query_channel_range_end_blocknum() {
36063697
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.clone(),
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
@@ -132,6 +132,16 @@ macro_rules! _check_encoded_tlv_order {
132132
/// [`Writer`]: crate::util::ser::Writer
133133
#[macro_export]
134134
macro_rules! encode_tlv_stream {
135+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => {
136+
$crate::_encode_tlv_stream!($stream, {$(($type, $field, $fieldty)),*})
137+
}
138+
}
139+
140+
/// Implementation of [`encode_tlv_stream`].
141+
/// This is exported for use by other exported macros, do not use directly.
142+
#[doc(hidden)]
143+
#[macro_export]
144+
macro_rules! _encode_tlv_stream {
135145
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => { {
136146
#[allow(unused_imports)]
137147
use $crate::{
@@ -153,7 +163,21 @@ macro_rules! encode_tlv_stream {
153163
$crate::_check_encoded_tlv_order!(last_seen, $type, $fieldty);
154164
)*
155165
}
156-
} }
166+
} };
167+
($stream: expr, $tlvs: expr) => { {
168+
for tlv in $tlvs {
169+
let (typ, value): &&(u64, Vec<u8>) = tlv;
170+
$crate::_encode_tlv!($stream, *typ, *value, required_vec);
171+
}
172+
173+
#[cfg(debug_assertions)] {
174+
let mut last_seen: Option<u64> = None;
175+
for tlv in $tlvs {
176+
let (typ, _): &&(u64, Vec<u8>) = tlv;
177+
$crate::_check_encoded_tlv_order!(last_seen, *typ, required_vec);
178+
}
179+
}
180+
} };
157181
}
158182

159183
/// Adds the length of the serialized field to a [`LengthCalculatingWriter`].
@@ -210,18 +234,27 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
210234
#[macro_export]
211235
macro_rules! _encode_varint_length_prefixed_tlv {
212236
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => { {
237+
_encode_varint_length_prefixed_tlv!($stream, {$(($type, $field, $fieldty)),*}, &[])
238+
} };
239+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}, $extra_tlvs: expr) => { {
213240
use $crate::util::ser::BigSize;
241+
use alloc::vec::Vec;
214242
let len = {
215243
#[allow(unused_mut)]
216244
let mut len = $crate::util::ser::LengthCalculatingWriter(0);
217245
$(
218246
$crate::_get_varint_length_prefixed_tlv_length!(len, $type, $field, $fieldty);
219247
)*
248+
for tlv in $extra_tlvs {
249+
let (typ, value): &&(u64, Vec<u8>) = tlv;
250+
$crate::_get_varint_length_prefixed_tlv_length!(len, *typ, *value, required_vec);
251+
}
220252
len.0
221253
};
222254
BigSize(len as u64).write($stream)?;
223-
$crate::encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
224-
} }
255+
$crate::_encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
256+
$crate::_encode_tlv_stream!($stream, $extra_tlvs);
257+
} };
225258
}
226259

227260
/// Errors if there are missing required TLV types between the last seen type and the type currently being processed.
@@ -785,7 +818,8 @@ macro_rules! _init_and_read_tlv_fields {
785818
///
786819
/// For example,
787820
/// ```
788-
/// # use lightning::impl_writeable_tlv_based;
821+
/// # use lightning::{impl_writeable_tlv_based, _encode_varint_length_prefixed_tlv};
822+
/// # extern crate alloc;
789823
/// struct LightningMessage {
790824
/// tlv_integer: u32,
791825
/// tlv_default_integer: u32,

0 commit comments

Comments
 (0)