Skip to content

Commit 904d322

Browse files
committed
Serialization macro for TLV streams
BOLT 12's offer message is encoded as a TLV stream (i.e., a sequence of TLV records). impl_writeable_tlv_based can't be used because it writes the overall length of the struct, whereas TLV streams only include the length of each TLV record. Add a `tlv_stream` macro for defining structs used in encoding. TLV records containing a single variable-length type should not encode the types length in the value since it is redundant. Add a wrapper type that can be used within a TLV stream to support the correct behavior during serialization and de-serialization.
1 parent 227fd51 commit 904d322

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

lightning/src/util/ser.rs

+20
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,9 @@ macro_rules! impl_writeable_primitive {
419419
}
420420
}
421421
}
422+
impl From<$val_type> for HighZeroBytesDroppedBigSize<$val_type> {
423+
fn from(val: $val_type) -> Self { Self(val) }
424+
}
422425
}
423426
}
424427

@@ -518,6 +521,23 @@ impl Readable for [u16; 8] {
518521
/// Used to prevent encoding the length twice.
519522
pub(crate) struct WithoutLength<T>(pub T);
520523

524+
impl Writeable for WithoutLength<&String> {
525+
#[inline]
526+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
527+
w.write_all(self.0.as_bytes())
528+
}
529+
}
530+
impl Readable for WithoutLength<String> {
531+
#[inline]
532+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
533+
let v: WithoutLength<Vec<u8>> = Readable::read(r)?;
534+
Ok(Self(String::from_utf8(v.0).map_err(|_| DecodeError::InvalidValue)?))
535+
}
536+
}
537+
impl<'a> From<&'a String> for WithoutLength<&'a String> {
538+
fn from(s: &'a String) -> Self { Self(s) }
539+
}
540+
521541
impl<'a, T: Writeable> Writeable for WithoutLength<&'a Vec<T>> {
522542
#[inline]
523543
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {

lightning/src/util/ser_macros.rs

+90
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ macro_rules! encode_tlv {
2626
field.write($stream)?;
2727
}
2828
};
29+
($stream: expr, $type: expr, $field: expr, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
30+
encode_tlv!($stream, $type, $field.map(|f| $encoding(f)), option);
31+
};
32+
($stream: expr, $type: expr, $field: expr, (option, encoding: $fieldty: ty)) => {
33+
encode_tlv!($stream, $type, $field, option);
34+
};
2935
}
3036

3137
macro_rules! encode_tlv_stream {
@@ -121,6 +127,9 @@ macro_rules! check_tlv_order {
121127
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
122128
// no-op
123129
}};
130+
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (option, encoding: $encoding: tt)) => {{
131+
// no-op
132+
}};
124133
}
125134

126135
macro_rules! check_missing_tlv {
@@ -150,6 +159,9 @@ macro_rules! check_missing_tlv {
150159
($last_seen_type: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
151160
// no-op
152161
}};
162+
($last_seen_type: expr, $type: expr, $field: ident, (option, encoding: $encoding: tt)) => {{
163+
// no-op
164+
}};
153165
}
154166

155167
macro_rules! decode_tlv {
@@ -172,6 +184,15 @@ macro_rules! decode_tlv {
172184
($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
173185
$field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
174186
}};
187+
($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
188+
$field = {
189+
let field: $encoding<$fieldty> = ser::Readable::read(&mut $reader)?;
190+
Some(field.0)
191+
};
192+
}};
193+
($reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
194+
decode_tlv!($reader, $field, option);
195+
}};
175196
}
176197

177198
// `$decode_custom_tlv` is a closure that may be optionally provided to handle custom message types.
@@ -441,6 +462,75 @@ macro_rules! impl_writeable_tlv_based {
441462
}
442463
}
443464

465+
/// Defines a struct for a TLV stream and a similar struct using references for non-primitive types,
466+
/// implementing [`Readable`] for the former and [`Writeable`] for the latter. Useful as an
467+
/// intermediary format when reading or writing a type encoded as a TLV stream. Note that each field
468+
/// representing a TLV record has its type wrapped with an [`Option`]. A tuple consisting of a type
469+
/// and a serialization wrapper may be given in place of a type when custom serialization is
470+
/// required.
471+
///
472+
/// [`Readable`]: crate::util::ser::Readable
473+
/// [`Writeable`]: crate::util::ser::Writeable
474+
macro_rules! tlv_stream {
475+
($name:ident, $nameref:ident, {
476+
$(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
477+
}) => {
478+
#[derive(Debug)]
479+
struct $name {
480+
$(
481+
$field: Option<tlv_record_type!($fieldty)>,
482+
)*
483+
}
484+
485+
pub(crate) struct $nameref<'a> {
486+
$(
487+
pub(crate) $field: Option<tlv_record_ref_type!($fieldty)>,
488+
)*
489+
}
490+
491+
impl<'a> $crate::util::ser::Writeable for $nameref<'a> {
492+
fn write<W: $crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
493+
encode_tlv_stream!(writer, {
494+
$(($type, self.$field, (option, encoding: $fieldty))),*
495+
});
496+
Ok(())
497+
}
498+
}
499+
500+
impl $crate::util::ser::Readable for $name {
501+
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, $crate::ln::msgs::DecodeError> {
502+
$(
503+
init_tlv_field_var!($field, option);
504+
)*
505+
decode_tlv_stream!(reader, {
506+
$(($type, $field, (option, encoding: $fieldty))),*
507+
});
508+
509+
Ok(Self {
510+
$(
511+
$field: $field
512+
),*
513+
})
514+
}
515+
}
516+
}
517+
}
518+
519+
macro_rules! tlv_record_type {
520+
(($type:ty, $wrapper:ident)) => { $type };
521+
($type:ty) => { $type };
522+
}
523+
524+
macro_rules! tlv_record_ref_type {
525+
(char) => { char };
526+
(u8) => { u8 };
527+
((u16, $wrapper: ident)) => { u16 };
528+
((u32, $wrapper: ident)) => { u32 };
529+
((u64, $wrapper: ident)) => { u64 };
530+
(($type:ty, $wrapper:ident)) => { &'a $type };
531+
($type:ty) => { &'a $type };
532+
}
533+
444534
macro_rules! _impl_writeable_tlv_based_enum_common {
445535
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
446536
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}

0 commit comments

Comments
 (0)