Skip to content

Commit 3ee85d7

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. Likewise, subtypes of such records must include type lengths. Add a wrapper type that includes the length using a specified unsigned type.
1 parent 5a76cd5 commit 3ee85d7

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-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 HighZeroBytesDroppedVarInt<$val_type> {
456+
fn from(val: $val_type) -> Self { Self(val) }
457+
}
458+
impl From<&$val_type> for HighZeroBytesDroppedVarInt<$val_type> {
459+
fn from(val: &$val_type) -> Self { Self(*val) }
460+
}
461+
impl From<HighZeroBytesDroppedVarInt<$val_type>> for $val_type {
462+
fn from(val: HighZeroBytesDroppedVarInt<$val_type>) -> Self { val.0 }
463+
}
455464
}
456465
}
457466

@@ -553,6 +562,62 @@ impl<T: Readable> Readable for WithLength<Vec<T>, u8> {
553562
}
554563
}
555564

565+
/// For variable-length values within TLV record where the length is encoded as part of the record.
566+
/// Used to prevent encoding the length twice.
567+
#[derive(Clone)]
568+
pub(crate) struct WithoutLength<T>(pub T);
569+
570+
impl Writeable for WithoutLength<String> {
571+
#[inline]
572+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
573+
w.write_all(self.0.as_bytes())
574+
}
575+
}
576+
impl Writeable for WithoutLength<&String> {
577+
#[inline]
578+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
579+
w.write_all(self.0.as_bytes())
580+
}
581+
}
582+
impl Readable for WithoutLength<String> {
583+
#[inline]
584+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
585+
let v: VecReadWrapper<u8> = Readable::read(r)?;
586+
Ok(Self(String::from_utf8(v.0).map_err(|_| DecodeError::InvalidValue)?))
587+
}
588+
}
589+
impl<'a> From<&'a String> for WithoutLength<&'a String> {
590+
fn from(s: &'a String) -> Self { Self(s) }
591+
}
592+
impl From<WithoutLength<String>> for String {
593+
fn from(s: WithoutLength<String>) -> Self { s.0 }
594+
}
595+
596+
impl<T: Writeable> Writeable for WithoutLength<Vec<T>> {
597+
#[inline]
598+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
599+
VecWriteWrapper(&self.0).write(w)
600+
}
601+
}
602+
impl<T: Writeable> Writeable for WithoutLength<&Vec<T>> {
603+
#[inline]
604+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
605+
VecWriteWrapper(self.0).write(w)
606+
}
607+
}
608+
impl<T: Readable> Readable for WithoutLength<Vec<T>> {
609+
#[inline]
610+
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
611+
Ok(Self(<VecReadWrapper<T> as Readable>::read(r)?.0))
612+
}
613+
}
614+
impl<'a, T> From<&'a Vec<T>> for WithoutLength<&'a Vec<T>> {
615+
fn from(v: &'a Vec<T>) -> Self { Self(v) }
616+
}
617+
impl<T> From<WithoutLength<Vec<T>>> for Vec<T> {
618+
fn from(s: WithoutLength<Vec<T>>) -> Self { s.0 }
619+
}
620+
556621
// HashMap
557622
impl<K, V> Writeable for HashMap<K, V>
558623
where K: Writeable + Eq + Hash,

lightning/src/util/ser_macros.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,108 @@ 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+
mod reference {
445+
use super::*;
446+
447+
pub(super) struct $name<'a> {
448+
$(
449+
pub(super) $field: Option<tlv_record_ref_type!($fieldty$(<$gen>)?)>,
450+
)*
451+
}
452+
453+
impl<'a> ::util::ser::Writeable for $name<'a> {
454+
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
455+
encode_tlv_stream!(writer, { $(($type, self.$field, option)),* });
456+
Ok(())
457+
}
458+
}
459+
}
460+
461+
impl ::util::ser::Readable for $name {
462+
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
463+
$(
464+
init_tlv_field_var!($field, option);
465+
)*
466+
decode_tlv_stream!(reader, {$(($type, $field, option)),*});
467+
468+
Ok(Self {
469+
$(
470+
$field: init_tlv_based_struct_field!($field, option)
471+
),*
472+
})
473+
}
474+
}
475+
}
476+
}
477+
478+
macro_rules! tlv_record_type {
479+
(u8) => {
480+
u8
481+
};
482+
(u16) => {
483+
::util::ser::HighZeroBytesDroppedVarInt<u16>
484+
};
485+
(u32) => {
486+
::util::ser::HighZeroBytesDroppedVarInt<u32>
487+
};
488+
(u64) => {
489+
::util::ser::HighZeroBytesDroppedVarInt<u64>
490+
};
491+
(char) => {
492+
char
493+
};
494+
(String) => {
495+
::util::ser::WithoutLength<String>
496+
};
497+
(Vec<$type:ty>) => {
498+
::util::ser::WithoutLength<Vec<$type>>
499+
};
500+
($type:ident$(<$gen:ident>)?) => {
501+
$type$(<$gen>)?
502+
};
503+
}
504+
505+
macro_rules! tlv_record_ref_type {
506+
(u8) => {
507+
u8
508+
};
509+
(u16) => {
510+
::util::ser::HighZeroBytesDroppedVarInt<u16>
511+
};
512+
(u32) => {
513+
::util::ser::HighZeroBytesDroppedVarInt<u32>
514+
};
515+
(u64) => {
516+
::util::ser::HighZeroBytesDroppedVarInt<u64>
517+
};
518+
(char) => {
519+
char
520+
};
521+
(String) => {
522+
::util::ser::WithoutLength<&'a String>
523+
};
524+
(Vec<$type:ty>) => {
525+
::util::ser::WithoutLength<&'a Vec<$type>>
526+
};
527+
($type:ident$(<$gen:ident>)?) => {
528+
&'a $type$(<$gen>)?
529+
};
530+
}
531+
430532
macro_rules! _impl_writeable_tlv_based_enum_common {
431533
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
432534
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}

0 commit comments

Comments
 (0)