Skip to content

Commit f560320

Browse files
committed
De/serialize custom TLVs on {Inbound,Outbound}OnionPayload
When serialized, the TLVs in `OutboundOnionPayload`, 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 d2e9cb4 commit f560320

File tree

4 files changed

+141
-13
lines changed

4 files changed

+141
-13
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,11 +2674,11 @@ where
26742674
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
26752675
counterparty_skimmed_fee_msat: Option<u64>,
26762676
) -> Result<PendingHTLCInfo, InboundOnionErr> {
2677-
let (payment_data, keysend_preimage, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2677+
let (payment_data, keysend_preimage, custom_tlvs, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
26782678
msgs::InboundOnionPayload::Receive {
2679-
payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata, ..
2679+
payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
26802680
} =>
2681-
(payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata),
2681+
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
26822682
_ =>
26832683
return Err(InboundOnionErr {
26842684
err_code: 0x4000|22,
@@ -10094,6 +10094,7 @@ mod tests {
1009410094
payment_data: Some(msgs::FinalOnionHopData {
1009510095
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
1009610096
}),
10097+
custom_tlvs: Vec::new(),
1009710098
};
1009810099
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
1009910100
// intended amount, we fail the payment.
@@ -10113,6 +10114,7 @@ mod tests {
1011310114
payment_data: Some(msgs::FinalOnionHopData {
1011410115
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
1011510116
}),
10117+
custom_tlvs: Vec::new(),
1011610118
};
1011710119
assert!(node[0].node.construct_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
1011810120
sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat)).is_ok());

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

@@ -1441,6 +1441,7 @@ mod fuzzy_internal_msgs {
14411441
payment_data: Option<FinalOnionHopData>,
14421442
payment_metadata: Option<Vec<u8>>,
14431443
keysend_preimage: Option<PaymentPreimage>,
1444+
custom_tlvs: Vec<(u64, Vec<u8>)>,
14441445
amt_msat: u64,
14451446
outgoing_cltv_value: u32,
14461447
},
@@ -1457,6 +1458,7 @@ mod fuzzy_internal_msgs {
14571458
payment_data: Option<FinalOnionHopData>,
14581459
payment_metadata: Option<Vec<u8>>,
14591460
keysend_preimage: Option<PaymentPreimage>,
1461+
custom_tlvs: Vec<(u64, Vec<u8>)>,
14601462
amt_msat: u64,
14611463
outgoing_cltv_value: u32,
14621464
},
@@ -1979,15 +1981,23 @@ impl Writeable for OutboundOnionPayload {
19791981
});
19801982
},
19811983
Self::Receive {
1982-
ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat, outgoing_cltv_value
1984+
ref payment_data, ref payment_metadata, ref keysend_preimage, amt_msat,
1985+
outgoing_cltv_value, ref custom_tlvs,
19831986
} => {
1987+
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`]
1988+
// to reject any reserved types in the experimental range if new ones are ever
1989+
// standardized.
1990+
let preimage = if let Some(ref preimage) = keysend_preimage {
1991+
Some((5482373484, preimage.encode()))
1992+
} else { None };
1993+
let mut custom_tlvs: Vec<&(u64, Vec<u8>)> = custom_tlvs.iter().chain(preimage.iter()).collect();
1994+
custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ);
19841995
_encode_varint_length_prefixed_tlv!(w, {
19851996
(2, HighZeroBytesDroppedBigSize(*amt_msat), required),
19861997
(4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
19871998
(8, payment_data, option),
1988-
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option),
1989-
(5482373484, keysend_preimage, option)
1990-
});
1999+
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
2000+
}, custom_tlvs.iter());
19912001
},
19922002
}
19932003
Ok(())
@@ -2002,14 +2012,24 @@ impl Readable for InboundOnionPayload {
20022012
let mut payment_data: Option<FinalOnionHopData> = None;
20032013
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
20042014
let mut keysend_preimage: Option<PaymentPreimage> = None;
2005-
read_tlv_fields!(r, {
2015+
let mut custom_tlvs = Vec::new();
2016+
2017+
let tlv_len = BigSize::read(r)?;
2018+
let rd = FixedLengthReader::new(r, tlv_len.0);
2019+
decode_tlv_stream_with_custom_tlv_decode!(rd, {
20062020
(2, amt, required),
20072021
(4, cltv_value, required),
20082022
(6, short_id, option),
20092023
(8, payment_data, option),
20102024
(16, payment_metadata, option),
20112025
// See https://github.com/lightning/blips/blob/master/blip-0003.md
20122026
(5482373484, keysend_preimage, option)
2027+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
2028+
if msg_type < 1 << 16 { return Ok(false) }
2029+
let mut value = Vec::new();
2030+
msg_reader.read_to_end(&mut value)?;
2031+
custom_tlvs.push((msg_type, value));
2032+
Ok(true)
20132033
});
20142034

20152035
if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
@@ -2033,6 +2053,7 @@ impl Readable for InboundOnionPayload {
20332053
keysend_preimage,
20342054
amt_msat: amt.0,
20352055
outgoing_cltv_value: cltv_value.0,
2056+
custom_tlvs,
20362057
})
20372058
}
20382059
}
@@ -3566,6 +3587,7 @@ mod tests {
35663587
keysend_preimage: None,
35673588
amt_msat: 0x0badf00d01020304,
35683589
outgoing_cltv_value: 0xffffffff,
3590+
custom_tlvs: vec![],
35693591
};
35703592
let encoded_value = outbound_msg.encode();
35713593
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
@@ -3590,6 +3612,7 @@ mod tests {
35903612
keysend_preimage: None,
35913613
amt_msat: 0x0badf00d01020304,
35923614
outgoing_cltv_value: 0xffffffff,
3615+
custom_tlvs: vec![],
35933616
};
35943617
let encoded_value = outbound_msg.encode();
35953618
let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
@@ -3604,10 +3627,78 @@ mod tests {
36043627
amt_msat, outgoing_cltv_value,
36053628
payment_metadata: None,
36063629
keysend_preimage: None,
3630+
custom_tlvs,
36073631
} = inbound_msg {
36083632
assert_eq!(payment_secret, expected_payment_secret);
36093633
assert_eq!(amt_msat, 0x0badf00d01020304);
36103634
assert_eq!(outgoing_cltv_value, 0xffffffff);
3635+
assert_eq!(custom_tlvs, vec![]);
3636+
} else { panic!(); }
3637+
}
3638+
3639+
#[test]
3640+
fn encoding_final_onion_hop_data_with_bad_custom_tlvs() {
3641+
// If custom TLVs have type number within the range reserved for protocol, treat them as if
3642+
// they're unknown
3643+
let bad_type_range_tlvs = vec![
3644+
((1 << 16) - 4, vec![42]),
3645+
((1 << 16) - 2, vec![42; 32]),
3646+
];
3647+
let mut msg = msgs::OutboundOnionPayload::Receive {
3648+
payment_data: None,
3649+
payment_metadata: None,
3650+
keysend_preimage: None,
3651+
custom_tlvs: bad_type_range_tlvs,
3652+
amt_msat: 0x0badf00d01020304,
3653+
outgoing_cltv_value: 0xffffffff,
3654+
};
3655+
let encoded_value = msg.encode();
3656+
assert!(msgs::InboundOnionPayload::read(&mut Cursor::new(&encoded_value[..])).is_err());
3657+
let good_type_range_tlvs = vec![
3658+
((1 << 16) - 3, vec![42]),
3659+
((1 << 16) - 1, vec![42; 32]),
3660+
];
3661+
if let msgs::OutboundOnionPayload::Receive { ref mut custom_tlvs, .. } = msg {
3662+
*custom_tlvs = good_type_range_tlvs.clone();
3663+
}
3664+
let encoded_value = msg.encode();
3665+
let inbound_msg = Readable::read(&mut Cursor::new(&encoded_value[..])).unwrap();
3666+
match inbound_msg {
3667+
msgs::InboundOnionPayload::Receive { custom_tlvs, .. } => assert!(custom_tlvs.is_empty()),
3668+
_ => panic!(),
3669+
}
3670+
}
3671+
3672+
#[test]
3673+
fn encoding_final_onion_hop_data_with_custom_tlvs() {
3674+
let expected_custom_tlvs = vec![
3675+
(5482373483, vec![0x12, 0x34]),
3676+
(5482373487, vec![0x42u8; 8]),
3677+
];
3678+
let msg = msgs::OutboundOnionPayload::Receive {
3679+
payment_data: None,
3680+
payment_metadata: None,
3681+
keysend_preimage: None,
3682+
custom_tlvs: expected_custom_tlvs.clone(),
3683+
amt_msat: 0x0badf00d01020304,
3684+
outgoing_cltv_value: 0xffffffff,
3685+
};
3686+
let encoded_value = msg.encode();
3687+
let target_value = hex::decode("2e02080badf00d010203040404ffffffffff0000000146c6616b021234ff0000000146c6616f084242424242424242").unwrap();
3688+
assert_eq!(encoded_value, target_value);
3689+
let inbound_msg: msgs::InboundOnionPayload = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3690+
if let msgs::InboundOnionPayload::Receive {
3691+
payment_data: None,
3692+
payment_metadata: None,
3693+
keysend_preimage: None,
3694+
custom_tlvs,
3695+
amt_msat,
3696+
outgoing_cltv_value,
3697+
..
3698+
} = inbound_msg {
3699+
assert_eq!(custom_tlvs, expected_custom_tlvs);
3700+
assert_eq!(amt_msat, 0x0badf00d01020304);
3701+
assert_eq!(outgoing_cltv_value, 0xffffffff);
36113702
} else { panic!(); }
36123703
}
36133704

lightning/src/ln/onion_utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
171171
} else { None },
172172
payment_metadata: recipient_onion.payment_metadata.take(),
173173
keysend_preimage: *keysend_preimage,
174+
custom_tlvs: recipient_onion.custom_tlvs.clone(),
174175
amt_msat: value_msat,
175176
outgoing_cltv_value: cltv,
176177
}

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)