Skip to content

Commit eaf6a95

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 d232a69 commit eaf6a95

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

lightning/src/util/ser.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,15 @@ macro_rules! impl_writeable_primitive {
452452
}
453453
}
454454
}
455+
impl From<$val_type> for HighZeroBytesDroppedBigSize<$val_type> {
456+
fn from(val: $val_type) -> Self { Self(val) }
457+
}
458+
impl From<&$val_type> for HighZeroBytesDroppedBigSize<$val_type> {
459+
fn from(val: &$val_type) -> Self { Self(*val) }
460+
}
461+
impl From<HighZeroBytesDroppedBigSize<$val_type>> for $val_type {
462+
fn from(val: HighZeroBytesDroppedBigSize<$val_type>) -> Self { val.0 }
463+
}
455464
}
456465
}
457466

@@ -524,6 +533,62 @@ impl_array!(PUBLIC_KEY_SIZE); // for PublicKey
524533
impl_array!(COMPACT_SIGNATURE_SIZE); // for Signature
525534
impl_array!(1300); // for OnionPacket.hop_data
526535

536+
/// For variable-length values within TLV record where the length is encoded as part of the record.
537+
/// Used to prevent encoding the length twice.
538+
#[derive(Clone)]
539+
pub(crate) struct WithoutLength<T>(pub T);
540+
541+
impl Writeable for WithoutLength<String> {
542+
#[inline]
543+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
544+
w.write_all(self.0.as_bytes())
545+
}
546+
}
547+
impl Writeable for WithoutLength<&String> {
548+
#[inline]
549+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
550+
w.write_all(self.0.as_bytes())
551+
}
552+
}
553+
impl Readable for WithoutLength<String> {
554+
#[inline]
555+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
556+
let v: VecReadWrapper<u8> = Readable::read(r)?;
557+
Ok(Self(String::from_utf8(v.0).map_err(|_| DecodeError::InvalidValue)?))
558+
}
559+
}
560+
impl<'a> From<&'a String> for WithoutLength<&'a String> {
561+
fn from(s: &'a String) -> Self { Self(s) }
562+
}
563+
impl From<WithoutLength<String>> for String {
564+
fn from(s: WithoutLength<String>) -> Self { s.0 }
565+
}
566+
567+
impl<T: Writeable> Writeable for WithoutLength<Vec<T>> {
568+
#[inline]
569+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
570+
VecWriteWrapper(&self.0).write(w)
571+
}
572+
}
573+
impl<T: Writeable> Writeable for WithoutLength<&Vec<T>> {
574+
#[inline]
575+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
576+
VecWriteWrapper(self.0).write(w)
577+
}
578+
}
579+
impl<T: Readable> Readable for WithoutLength<Vec<T>> {
580+
#[inline]
581+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
582+
Ok(Self(<VecReadWrapper<T> as Readable>::read(r)?.0))
583+
}
584+
}
585+
impl<'a, T> From<&'a Vec<T>> for WithoutLength<&'a Vec<T>> {
586+
fn from(v: &'a Vec<T>) -> Self { Self(v) }
587+
}
588+
impl<T> From<WithoutLength<Vec<T>>> for Vec<T> {
589+
fn from(s: WithoutLength<Vec<T>>) -> Self { s.0 }
590+
}
591+
527592
// HashMap
528593
impl<K, V> Writeable for HashMap<K, V>
529594
where K: Writeable + Eq + Hash,

lightning/src/util/ser_macros.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,129 @@ macro_rules! impl_writeable_tlv_based {
427427
}
428428
}
429429

430+
/// Defines a struct for a TLV stream and a similar struct using references for non-primitive types,
431+
/// implementing [`Readable`] for the former and [`Writeable`] for the latter. Useful as an
432+
/// intermediary format when reading or writing a type encoded as a TLV stream.
433+
macro_rules! tlv_stream {
434+
(struct $name:ident {
435+
$(($type:expr, $field:ident : $fieldty:ident$(<$gen:ident>)?)),* $(,)*
436+
}) => {
437+
#[derive(Debug)]
438+
struct $name {
439+
$(
440+
$field: Option<tlv_record_type!($fieldty$(<$gen>)?)>,
441+
)*
442+
}
443+
444+
pub(crate) mod reference {
445+
$(
446+
tlv_record_import!($fieldty$(<$gen>)?);
447+
)*
448+
449+
pub(crate) struct $name<'a> {
450+
$(
451+
pub(crate) $field: Option<tlv_record_ref_type!($fieldty$(<$gen>)?)>,
452+
)*
453+
}
454+
455+
impl<'a> ::util::ser::Writeable for $name<'a> {
456+
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
457+
encode_tlv_stream!(writer, { $(($type, self.$field, option)),* });
458+
Ok(())
459+
}
460+
}
461+
}
462+
463+
impl ::util::ser::Readable for $name {
464+
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
465+
$(
466+
init_tlv_field_var!($field, option);
467+
)*
468+
decode_tlv_stream!(reader, {$(($type, $field, option)),*});
469+
470+
Ok(Self {
471+
$(
472+
$field: init_tlv_based_struct_field!($field, option)
473+
),*
474+
})
475+
}
476+
}
477+
}
478+
}
479+
480+
macro_rules! tlv_record_type {
481+
(u8) => {
482+
u8
483+
};
484+
(u16) => {
485+
::util::ser::HighZeroBytesDroppedBigSize<u16>
486+
};
487+
(u32) => {
488+
::util::ser::HighZeroBytesDroppedBigSize<u32>
489+
};
490+
(u64) => {
491+
::util::ser::HighZeroBytesDroppedBigSize<u64>
492+
};
493+
(char) => {
494+
char
495+
};
496+
(String) => {
497+
::util::ser::WithoutLength<String>
498+
};
499+
(Vec<$type:ty>) => {
500+
::util::ser::WithoutLength<Vec<$type>>
501+
};
502+
($type:ident$(<$gen:ident>)?) => {
503+
$type$(<$gen>)?
504+
};
505+
}
506+
507+
macro_rules! tlv_record_ref_type {
508+
(u8) => {
509+
u8
510+
};
511+
(u16) => {
512+
::util::ser::HighZeroBytesDroppedBigSize<u16>
513+
};
514+
(u32) => {
515+
::util::ser::HighZeroBytesDroppedBigSize<u32>
516+
};
517+
(u64) => {
518+
::util::ser::HighZeroBytesDroppedBigSize<u64>
519+
};
520+
(char) => {
521+
char
522+
};
523+
(String) => {
524+
::util::ser::WithoutLength<&'a String>
525+
};
526+
(Vec<$type:ty>) => {
527+
::util::ser::WithoutLength<&'a Vec<$type>>
528+
};
529+
($type:ident$(<$gen:ident>)?) => {
530+
&'a $type$(<$gen>)?
531+
};
532+
}
533+
534+
macro_rules! tlv_record_import {
535+
(u8) => {};
536+
(u16) => {};
537+
(u32) => {};
538+
(u64) => {};
539+
(char) => {};
540+
(String) => {};
541+
(Vec<$type:ident>) => {
542+
tlv_record_import!($type);
543+
};
544+
($type:ident<$gen:ident>) => {
545+
tlv_record_import!($type);
546+
tlv_record_import!($gen);
547+
};
548+
($type:ident) => {
549+
use super::$type;
550+
};
551+
}
552+
430553
macro_rules! _impl_writeable_tlv_based_enum_common {
431554
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
432555
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}

0 commit comments

Comments
 (0)