Skip to content

Commit fcbad48

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 a new macro variant 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 3feb4a6 commit fcbad48

File tree

4 files changed

+115
-13
lines changed

4 files changed

+115
-13
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2341,7 +2341,7 @@ where
23412341
msg: "Got non final data with an HMAC of 0",
23422342
});
23432343
},
2344-
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata } => {
2344+
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata, custom_tlvs } => {
23452345
if payment_data.is_some() && keysend_preimage.is_some() {
23462346
return Err(ReceiveError {
23472347
err_code: 0x4000|22,

lightning/src/ln/msgs.rs

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::io_extras::read_to_end;
4242

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

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

@@ -1413,6 +1413,8 @@ mod fuzzy_internal_msgs {
14131413
payment_data: Option<FinalOnionHopData>,
14141414
payment_metadata: Option<Vec<u8>>,
14151415
keysend_preimage: Option<PaymentPreimage>,
1416+
/// Serialization will fail if this is not a valid TLV stream
1417+
custom_tlvs: Option<Vec<(u64, Vec<u8>)>>,
14161418
},
14171419
}
14181420

@@ -1934,14 +1936,27 @@ impl Writeable for OnionHopData {
19341936
(6, short_channel_id, required)
19351937
});
19361938
},
1937-
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
1939+
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage, ref custom_tlvs } => {
1940+
let mut custom_tlvs = custom_tlvs.clone().unwrap_or(Vec::new());
1941+
if let &Some(preimage) = keysend_preimage {
1942+
let mut preimage_bytes = Vec::new();
1943+
preimage.write(&mut preimage_bytes)?;
1944+
custom_tlvs.push((5482373484, preimage_bytes));
1945+
}
1946+
custom_tlvs.sort_by_key(|(typ, _)| *typ);
1947+
let mut custom_tlv_stream = Vec::new();
1948+
for (typ, value) in custom_tlvs {
1949+
BigSize(typ).write(&mut custom_tlv_stream)?;
1950+
BigSize(value.len() as u64).write(&mut custom_tlv_stream)?;
1951+
custom_tlv_stream.write_all(&value)?;
1952+
}
1953+
19381954
_encode_varint_length_prefixed_tlv!(w, {
19391955
(2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
19401956
(4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
19411957
(8, payment_data, option),
1942-
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
1943-
(5482373484, keysend_preimage, option)
1944-
});
1958+
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
1959+
}, &custom_tlv_stream);
19451960
},
19461961
}
19471962
Ok(())
@@ -1956,14 +1971,24 @@ impl Readable for OnionHopData {
19561971
let mut payment_data: Option<FinalOnionHopData> = None;
19571972
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
19581973
let mut keysend_preimage: Option<PaymentPreimage> = None;
1959-
read_tlv_fields!(r, {
1974+
let mut custom_tlvs = Vec::new();
1975+
1976+
let tlv_len = BigSize::read(r)?;
1977+
let rd = FixedLengthReader::new(r, tlv_len.0);
1978+
decode_tlv_stream_with_custom_tlv_decode!(rd, {
19601979
(2, amt, required),
19611980
(4, cltv_value, required),
19621981
(6, short_id, option),
19631982
(8, payment_data, option),
19641983
(16, payment_metadata, option),
19651984
// See https://github.com/lightning/blips/blob/master/blip-0003.md
19661985
(5482373484, keysend_preimage, option)
1986+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
1987+
if msg_type < 1 << 16 { return Ok(false) }
1988+
let mut value = Vec::new();
1989+
msg_reader.read_to_end(&mut value)?;
1990+
custom_tlvs.push((msg_type, value));
1991+
Ok(true)
19671992
});
19681993

19691994
let format = if let Some(short_channel_id) = short_id {
@@ -1982,6 +2007,7 @@ impl Readable for OnionHopData {
19822007
payment_data,
19832008
payment_metadata: payment_metadata.map(|w| w.0),
19842009
keysend_preimage,
2010+
custom_tlvs: if custom_tlvs.is_empty() { None } else { Some(custom_tlvs) },
19852011
}
19862012
};
19872013

@@ -2417,7 +2443,7 @@ mod tests {
24172443
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
24182444
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
24192445
use crate::routing::gossip::{NodeAlias, NodeId};
2420-
use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited};
2446+
use crate::util::ser::{Writeable, Writer, Readable, Hostname, TransactionU16LenLimited};
24212447

24222448
use bitcoin::hashes::hex::FromHex;
24232449
use bitcoin::util::address::Address;
@@ -3513,6 +3539,7 @@ mod tests {
35133539
payment_data: None,
35143540
payment_metadata: None,
35153541
keysend_preimage: None,
3542+
custom_tlvs: None,
35163543
},
35173544
amt_to_forward: 0x0badf00d01020304,
35183545
outgoing_cltv_value: 0xffffffff,
@@ -3537,6 +3564,7 @@ mod tests {
35373564
}),
35383565
payment_metadata: None,
35393566
keysend_preimage: None,
3567+
custom_tlvs: None,
35403568
},
35413569
amt_to_forward: 0x0badf00d01020304,
35423570
outgoing_cltv_value: 0xffffffff,
@@ -3552,13 +3580,81 @@ mod tests {
35523580
}),
35533581
payment_metadata: None,
35543582
keysend_preimage: None,
3583+
custom_tlvs: None,
35553584
} = msg.format {
35563585
assert_eq!(payment_secret, expected_payment_secret);
35573586
} else { panic!(); }
35583587
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
35593588
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
35603589
}
35613590

3591+
#[test]
3592+
fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
3593+
// If custom TLVs have type number within the range reserved for protocol, treat them as if
3594+
// they're unknown
3595+
let bad_type_range_tlvs = vec![
3596+
((1 << 16) - 4, vec![42]),
3597+
((1 << 16) - 2, vec![42; 32]),
3598+
];
3599+
let mut msg = msgs::OnionHopData {
3600+
format: OnionHopDataFormat::FinalNode {
3601+
payment_data: None,
3602+
payment_metadata: None,
3603+
keysend_preimage: None,
3604+
custom_tlvs: Some(bad_type_range_tlvs),
3605+
},
3606+
amt_to_forward: 0x0badf00d01020304,
3607+
outgoing_cltv_value: 0xffffffff,
3608+
};
3609+
let encoded_value = msg.encode();
3610+
assert!(msgs::OnionHopData::read(&mut Cursor::new(&encoded_value[..])).is_err());
3611+
let good_type_range_tlvs = vec![
3612+
((1 << 16) - 3, vec![42]),
3613+
((1 << 16) - 1, vec![42; 32]),
3614+
];
3615+
if let OnionHopDataFormat::FinalNode { ref mut custom_tlvs, .. } = msg.format {
3616+
*custom_tlvs = Some(good_type_range_tlvs.clone());
3617+
}
3618+
let encoded_value = msg.encode();
3619+
msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
3620+
match msg.format {
3621+
OnionHopDataFormat::FinalNode { custom_tlvs, .. } => assert!(custom_tlvs.is_none()),
3622+
_ => panic!(),
3623+
}
3624+
}
3625+
3626+
#[test]
3627+
fn encoding_final_onion_hop_data_with_custom_tlvs() {
3628+
let expected_custom_tlvs = vec![
3629+
(5482373483, vec![0x12, 0x34]),
3630+
(5482373487, vec![0x42u8; 8]),
3631+
];
3632+
let mut msg = msgs::OnionHopData {
3633+
format: OnionHopDataFormat::FinalNode {
3634+
payment_data: None,
3635+
payment_metadata: None,
3636+
keysend_preimage: None,
3637+
custom_tlvs: Some(expected_custom_tlvs.clone()),
3638+
},
3639+
amt_to_forward: 0x0badf00d01020304,
3640+
outgoing_cltv_value: 0xffffffff,
3641+
};
3642+
let encoded_value = msg.encode();
3643+
let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
3644+
assert_eq!(encoded_value, target_value);
3645+
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3646+
if let OnionHopDataFormat::FinalNode {
3647+
payment_data: None,
3648+
payment_metadata: None,
3649+
keysend_preimage: None,
3650+
custom_tlvs,
3651+
} = msg.format {
3652+
assert_eq!(custom_tlvs.unwrap(), expected_custom_tlvs);
3653+
} else { panic!(); }
3654+
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3655+
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
3656+
}
3657+
35623658
#[test]
35633659
fn query_channel_range_end_blocknum() {
35643660
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: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,19 +191,24 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
191191
#[doc(hidden)]
192192
#[macro_export]
193193
macro_rules! _encode_varint_length_prefixed_tlv {
194-
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => { {
194+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => {
195+
_encode_varint_length_prefixed_tlv!($stream, {$(($type, $field, $fieldty)),*}, &[0u8; 0])
196+
};
197+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}, $extra: expr) => { {
198+
// $extra should be a byte slice holding a valid TLV stream
195199
use $crate::util::ser::BigSize;
196200
let len = {
197201
#[allow(unused_mut)]
198202
let mut len = $crate::util::ser::LengthCalculatingWriter(0);
199203
$(
200204
$crate::_get_varint_length_prefixed_tlv_length!(len, $type, $field, $fieldty);
201205
)*
202-
len.0
206+
len.0 + $extra.len()
203207
};
204208
BigSize(len as u64).write($stream)?;
205209
$crate::encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
206-
} }
210+
$stream.write_all($extra)?;
211+
} };
207212
}
208213

209214
/// Errors if there are missing required TLV types between the last seen type and the type currently being processed.
@@ -766,7 +771,7 @@ macro_rules! _init_and_read_tlv_fields {
766771
///
767772
/// For example,
768773
/// ```
769-
/// # use lightning::impl_writeable_tlv_based;
774+
/// # use lightning::{impl_writeable_tlv_based, _encode_varint_length_prefixed_tlv};
770775
/// struct LightningMessage {
771776
/// tlv_integer: u32,
772777
/// tlv_default_integer: u32,
@@ -1047,7 +1052,7 @@ mod tests {
10471052
use crate::io::{self, Cursor};
10481053
use crate::prelude::*;
10491054
use crate::ln::msgs::DecodeError;
1050-
use crate::util::ser::{Writeable, HighZeroBytesDroppedBigSize, VecWriter};
1055+
use crate::util::ser::{Writer, Writeable, HighZeroBytesDroppedBigSize, VecWriter};
10511056
use bitcoin::secp256k1::PublicKey;
10521057

10531058
// The BOLT TLV test cases don't include any tests which use our "required-value" logic since

0 commit comments

Comments
 (0)