Skip to content

Commit e08c06f

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 introduces a way to sort TLVs from a byte slice.
1 parent 52a6607 commit e08c06f

File tree

4 files changed

+128
-13
lines changed

4 files changed

+128
-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: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use bitcoin::hash_types::{Txid, BlockHash};
3232

3333
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
3434
use crate::ln::onion_utils;
35+
use crate::offers::{TlvStream, TlvRecord};
3536
use crate::onion_message;
3637

3738
use crate::prelude::*;
@@ -42,7 +43,7 @@ use crate::io_extras::read_to_end;
4243

4344
use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
4445
use crate::util::logger;
45-
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};
4647

4748
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
4849

@@ -1413,6 +1414,8 @@ mod fuzzy_internal_msgs {
14131414
payment_data: Option<FinalOnionHopData>,
14141415
payment_metadata: Option<Vec<u8>>,
14151416
keysend_preimage: Option<PaymentPreimage>,
1417+
/// Serialization will fail if this is not a valid TLV stream
1418+
custom_tlvs: Option<Vec<u8>>,
14161419
},
14171420
}
14181421

@@ -1934,20 +1937,36 @@ impl Writeable for OnionHopData {
19341937
(6, short_channel_id, required)
19351938
});
19361939
},
1937-
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage } => {
1940+
OnionHopDataFormat::FinalNode { ref payment_data, ref payment_metadata, ref keysend_preimage, ref custom_tlvs } => {
1941+
let mut custom_tlv_stream = custom_tlvs.clone().unwrap_or(Vec::new());
1942+
encode_tlv_stream!(&mut custom_tlv_stream, {
1943+
(5482373484, keysend_preimage, option)
1944+
});
1945+
let sorted_custom_tlvs = sorted_tlv_stream(&custom_tlv_stream)?;
19381946
_encode_varint_length_prefixed_tlv!(w, {
19391947
(2, HighZeroBytesDroppedBigSize(self.amt_to_forward), required),
19401948
(4, HighZeroBytesDroppedBigSize(self.outgoing_cltv_value), required),
19411949
(8, payment_data, option),
1942-
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
1943-
(5482373484, keysend_preimage, option)
1944-
});
1950+
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
1951+
}, &sorted_custom_tlvs);
19451952
},
19461953
}
19471954
Ok(())
19481955
}
19491956
}
19501957

1958+
fn sorted_tlv_stream(bytes: &[u8]) -> Result<Vec<u8>, io::Error> {
1959+
let mut tlv_stream = TlvStream::try_new(bytes)
1960+
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Could not serialize custom TLV stream"))?
1961+
.collect::<Vec<TlvRecord>>();
1962+
tlv_stream.sort_by_key(|tlv| tlv.get_type());
1963+
let mut sorted_bytes = Vec::with_capacity(bytes.len());
1964+
for tlv in tlv_stream {
1965+
tlv.write(&mut sorted_bytes)?;
1966+
}
1967+
Ok(sorted_bytes)
1968+
}
1969+
19511970
impl Readable for OnionHopData {
19521971
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
19531972
let mut amt = HighZeroBytesDroppedBigSize(0u64);
@@ -1956,14 +1975,27 @@ impl Readable for OnionHopData {
19561975
let mut payment_data: Option<FinalOnionHopData> = None;
19571976
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
19581977
let mut keysend_preimage: Option<PaymentPreimage> = None;
1959-
read_tlv_fields!(r, {
1978+
let mut custom_tlvs = Vec::new();
1979+
1980+
let tlv_len = BigSize::read(r)?;
1981+
let rd = FixedLengthReader::new(r, tlv_len.0);
1982+
decode_tlv_stream_with_custom_tlv_decode!(rd, {
19601983
(2, amt, required),
19611984
(4, cltv_value, required),
19621985
(6, short_id, option),
19631986
(8, payment_data, option),
19641987
(16, payment_metadata, option),
19651988
// See https://github.com/lightning/blips/blob/master/blip-0003.md
19661989
(5482373484, keysend_preimage, option)
1990+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
1991+
if msg_type < 1 << 16 { return Ok(false) }
1992+
// Re-serialize into TLV record
1993+
let mut buffer = Vec::new();
1994+
msg_reader.read_to_end(&mut buffer)?;
1995+
BigSize(msg_type).write(&mut custom_tlvs)?;
1996+
BigSize(buffer.len() as u64).write(&mut custom_tlvs)?;
1997+
custom_tlvs.append(&mut buffer);
1998+
Ok(true)
19671999
});
19682000

19692001
let format = if let Some(short_channel_id) = short_id {
@@ -1982,6 +2014,7 @@ impl Readable for OnionHopData {
19822014
payment_data,
19832015
payment_metadata: payment_metadata.map(|w| w.0),
19842016
keysend_preimage,
2017+
custom_tlvs: if custom_tlvs.is_empty() { None } else { Some(custom_tlvs) },
19852018
}
19862019
};
19872020

@@ -2417,7 +2450,7 @@ mod tests {
24172450
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
24182451
use crate::ln::msgs::{self, FinalOnionHopData, OnionErrorPacket, OnionHopDataFormat};
24192452
use crate::routing::gossip::{NodeAlias, NodeId};
2420-
use crate::util::ser::{Writeable, Readable, Hostname, TransactionU16LenLimited};
2453+
use crate::util::ser::{Writeable, Writer, Readable, Hostname, TransactionU16LenLimited};
24212454

24222455
use bitcoin::hashes::hex::FromHex;
24232456
use bitcoin::util::address::Address;
@@ -3513,6 +3546,7 @@ mod tests {
35133546
payment_data: None,
35143547
payment_metadata: None,
35153548
keysend_preimage: None,
3549+
custom_tlvs: None,
35163550
},
35173551
amt_to_forward: 0x0badf00d01020304,
35183552
outgoing_cltv_value: 0xffffffff,
@@ -3537,6 +3571,7 @@ mod tests {
35373571
}),
35383572
payment_metadata: None,
35393573
keysend_preimage: None,
3574+
custom_tlvs: None,
35403575
},
35413576
amt_to_forward: 0x0badf00d01020304,
35423577
outgoing_cltv_value: 0xffffffff,
@@ -3552,13 +3587,87 @@ mod tests {
35523587
}),
35533588
payment_metadata: None,
35543589
keysend_preimage: None,
3590+
custom_tlvs: None,
35553591
} = msg.format {
35563592
assert_eq!(payment_secret, expected_payment_secret);
35573593
} else { panic!(); }
35583594
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
35593595
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
35603596
}
35613597

3598+
#[test]
3599+
fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
3600+
let bad_tlvs = vec![42; 32];
3601+
let mut msg = msgs::OnionHopData {
3602+
format: OnionHopDataFormat::FinalNode {
3603+
payment_data: None,
3604+
payment_metadata: None,
3605+
keysend_preimage: None,
3606+
custom_tlvs: Some(bad_tlvs),
3607+
},
3608+
amt_to_forward: 0x0badf00d01020304,
3609+
outgoing_cltv_value: 0xffffffff,
3610+
};
3611+
assert!(msg.write(&mut Vec::new()).is_err());
3612+
3613+
// If custom TLVs have type number within the range reserved for protocol, treat them as if
3614+
// they're unknown
3615+
let bad_type_range_tlvs = _get_encoded_tlv_stream!({
3616+
((1 << 16) - 4, 42, required),
3617+
((1 << 16) - 2, Some(vec![42; 32]), option),
3618+
});
3619+
if let OnionHopDataFormat::FinalNode { ref mut custom_tlvs, .. } = msg.format {
3620+
*custom_tlvs = Some(bad_type_range_tlvs.clone());
3621+
}
3622+
let encoded_value = msg.encode();
3623+
assert!(msgs::OnionHopData::read(&mut Cursor::new(&encoded_value[..])).is_err());
3624+
let good_type_range_tlvs = _get_encoded_tlv_stream!({
3625+
((1 << 16) - 3, 42, required),
3626+
((1 << 16) - 1, Some(vec![42; 32]), option),
3627+
});
3628+
if let OnionHopDataFormat::FinalNode { ref mut custom_tlvs, .. } = msg.format {
3629+
*custom_tlvs = Some(good_type_range_tlvs.clone());
3630+
}
3631+
let encoded_value = msg.encode();
3632+
msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
3633+
match msg.format {
3634+
OnionHopDataFormat::FinalNode { custom_tlvs, .. } => assert!(custom_tlvs.is_none()),
3635+
_ => panic!(),
3636+
}
3637+
}
3638+
3639+
#[test]
3640+
fn encoding_final_onion_hop_data_with_custom_tlvs() {
3641+
let expected_custom_tlvs = _get_encoded_tlv_stream!({
3642+
(5482373483, 0x1234, required),
3643+
(5482373487, Some(vec![0x42u8; 8]), option),
3644+
});
3645+
let mut msg = msgs::OnionHopData {
3646+
format: OnionHopDataFormat::FinalNode {
3647+
payment_data: None,
3648+
payment_metadata: None,
3649+
keysend_preimage: None,
3650+
custom_tlvs: Some(expected_custom_tlvs.clone()),
3651+
},
3652+
amt_to_forward: 0x0badf00d01020304,
3653+
outgoing_cltv_value: 0xffffffff,
3654+
};
3655+
let encoded_value = msg.encode();
3656+
let target_value = hex::decode("3202080badf00d010203040404ffffffffff0000000146c6616b0400001234ff0000000146c6616f0a00084242424242424242").unwrap();
3657+
assert_eq!(encoded_value, target_value);
3658+
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3659+
if let OnionHopDataFormat::FinalNode {
3660+
payment_data: None,
3661+
payment_metadata: None,
3662+
keysend_preimage: None,
3663+
custom_tlvs,
3664+
} = msg.format {
3665+
assert_eq!(custom_tlvs.unwrap(), expected_custom_tlvs);
3666+
} else { panic!(); }
3667+
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3668+
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
3669+
}
3670+
35623671
#[test]
35633672
fn query_channel_range_end_blocknum() {
35643673
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
@@ -207,19 +207,24 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
207207
#[doc(hidden)]
208208
#[macro_export]
209209
macro_rules! _encode_varint_length_prefixed_tlv {
210-
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => { {
210+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}) => {
211+
_encode_varint_length_prefixed_tlv!($stream, {$(($type, $field, $fieldty)),*}, &[0u8; 0])
212+
};
213+
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),*}, $extra: expr) => { {
214+
// $extra should be a byte slice holding a valid TLV stream
211215
use $crate::util::ser::BigSize;
212216
let len = {
213217
#[allow(unused_mut)]
214218
let mut len = $crate::util::ser::LengthCalculatingWriter(0);
215219
$(
216220
$crate::_get_varint_length_prefixed_tlv_length!(len, $type, $field, $fieldty);
217221
)*
218-
len.0
222+
len.0 + $extra.len()
219223
};
220224
BigSize(len as u64).write($stream)?;
221225
$crate::encode_tlv_stream!($stream, { $(($type, $field, $fieldty)),* });
222-
} }
226+
$stream.write_all($extra)?;
227+
} };
223228
}
224229

225230
/// Errors if there are missing required TLV types between the last seen type and the type currently being processed.
@@ -782,7 +787,7 @@ macro_rules! _init_and_read_tlv_fields {
782787
///
783788
/// For example,
784789
/// ```
785-
/// # use lightning::impl_writeable_tlv_based;
790+
/// # use lightning::{impl_writeable_tlv_based, _encode_varint_length_prefixed_tlv};
786791
/// struct LightningMessage {
787792
/// tlv_integer: u32,
788793
/// tlv_default_integer: u32,
@@ -1063,7 +1068,7 @@ mod tests {
10631068
use crate::io::{self, Cursor};
10641069
use crate::prelude::*;
10651070
use crate::ln::msgs::DecodeError;
1066-
use crate::util::ser::{Writeable, HighZeroBytesDroppedBigSize, VecWriter};
1071+
use crate::util::ser::{Writer, Writeable, HighZeroBytesDroppedBigSize, VecWriter};
10671072
use bitcoin::secp256k1::PublicKey;
10681073

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

0 commit comments

Comments
 (0)