Skip to content

Commit b1ca515

Browse files
committed
Introduce custom TLV support for OnionMessage
1 parent 78ac48c commit b1ca515

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed

lightning/src/blinded_path/message.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub(crate) struct ReceiveTlvs {
3030
/// sending to. This is useful for receivers to check that said blinded path is being used in
3131
/// the right context.
3232
pub(crate) path_id: Option<[u8; 32]>,
33+
/// Custom Tlvs
34+
pub(crate) custom_tlvs: Vec<(u64, Vec<u8>)>,
3335
}
3436

3537
impl Writeable for ForwardTlvs {
@@ -47,6 +49,7 @@ impl Writeable for ReceiveTlvs {
4749
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
4850
// TODO: write padding
4951
encode_tlv_stream!(writer, {
52+
(1, self.custom_tlvs, optional_vec),
5053
(6, self.path_id, option),
5154
});
5255
Ok(())
@@ -62,7 +65,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
6265
.map(|pk| {
6366
ControlTlvs::Forward(ForwardTlvs { next_node_id: *pk, next_blinding_override: None })
6467
})
65-
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
68+
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None, custom_tlvs: Vec::new() })));
6669

6770
utils::construct_blinded_hops(secp_ctx, unblinded_path.iter(), blinded_tlvs, session_priv)
6871
}

lightning/src/onion_message/messenger.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -627,12 +627,12 @@ where
627627
(control_tlvs_ss, custom_handler.deref(), logger.deref())
628628
) {
629629
Ok((Payload::Receive::<ParsedOnionMessageContents<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage>> {
630-
message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
630+
message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id, custom_tlvs: _ }), reply_path,
631631
}, None)) => {
632632
Ok(PeeledOnion::Receive(message, path_id, reply_path))
633633
},
634634
Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
635-
next_node_id, next_blinding_override
635+
next_node_id, next_blinding_override,
636636
})), Some((next_hop_hmac, new_packet_bytes)))) => {
637637
// TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
638638
// blinded hop and this onion message is destined for us. In this situation, we should keep
@@ -1152,7 +1152,7 @@ fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + sec
11521152
}, prev_control_tlvs_ss.unwrap()));
11531153
} else {
11541154
payloads.push((Payload::Receive {
1155-
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, }),
1155+
control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id: None, custom_tlvs: Vec::new() }),
11561156
reply_path: reply_path.take(),
11571157
message,
11581158
}, prev_control_tlvs_ss.unwrap()));

lightning/src/onion_message/packet.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,22 @@ pub(crate) enum ControlTlvs {
282282

283283
impl Readable for ControlTlvs {
284284
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
285-
_init_and_read_tlv_stream!(r, {
285+
let mut custom_tlvs = Vec::new();
286+
287+
let tlv_len = BigSize::read(r)?;
288+
let rd = FixedLengthReader::new(r, tlv_len.0);
289+
_init_and_read_tlv_stream_with_custom_tlv_decode!(rd, {
286290
(1, _padding, option),
287291
(2, _short_channel_id, option),
288292
(4, next_node_id, option),
289293
(6, path_id, option),
290294
(8, next_blinding_override, option),
295+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
296+
if msg_type < 1 << 16 { return Ok(false) }
297+
let mut value = Vec::new();
298+
msg_reader.read_to_end(&mut value)?;
299+
custom_tlvs.push((msg_type, value));
300+
Ok(true)
291301
});
292302
let _padding: Option<Padding> = _padding;
293303
let _short_channel_id: Option<u64> = _short_channel_id;
@@ -296,13 +306,15 @@ impl Readable for ControlTlvs {
296306
let valid_recv_fmt = next_node_id.is_none() && next_blinding_override.is_none();
297307

298308
let payload_fmt = if valid_fwd_fmt {
309+
if !custom_tlvs.is_empty() { return Err(DecodeError::InvalidValue) }
299310
ControlTlvs::Forward(ForwardTlvs {
300311
next_node_id: next_node_id.unwrap(),
301312
next_blinding_override,
302313
})
303314
} else if valid_recv_fmt {
304315
ControlTlvs::Receive(ReceiveTlvs {
305316
path_id,
317+
custom_tlvs,
306318
})
307319
} else {
308320
return Err(DecodeError::InvalidValue)

lightning/src/util/ser_macros.rs

+19
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ macro_rules! decode_tlv_stream {
472472
///
473473
/// [`FixedLengthReader`]: crate::util::ser::FixedLengthReader
474474
/// [`DecodeError`]: crate::ln::msgs::DecodeError
475+
#[macro_export]
475476
macro_rules! decode_tlv_stream_with_custom_tlv_decode {
476477
($stream: expr, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}
477478
$(, $decode_custom_tlv: expr)?) => { {
@@ -825,6 +826,24 @@ macro_rules! _init_and_read_tlv_stream {
825826
}
826827
}
827828

829+
/// Equivalent to running [`_init_tlv_field_var`] then [`decode_tlv_stream_with_custom_tlv_decode`].
830+
///
831+
/// If any unused values are read, their type MUST be specified or else `rustc` will read them as an
832+
/// `i64`.
833+
834+
macro_rules! _init_and_read_tlv_stream_with_custom_tlv_decode {
835+
($reader: ident, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}
836+
$(, $decode_custom_tlv: expr)?) => {
837+
$(
838+
$crate::_init_tlv_field_var!($field, $fieldty);
839+
)*
840+
841+
$crate::decode_tlv_stream_with_custom_tlv_decode!(
842+
$reader, {$(($type, $field, $fieldty)),*} $(, $decode_custom_tlv)?
843+
);
844+
}
845+
}
846+
828847
/// Implements [`Readable`]/[`Writeable`] for a struct storing it as a set of TLVs
829848
/// If `$fieldty` is `required`, then `$field` is a required field that is not an [`Option`] nor a [`Vec`].
830849
/// If `$fieldty` is `(default_value, $default)`, then `$field` will be set to `$default` if not present.

0 commit comments

Comments
 (0)