Skip to content

Commit 16606af

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 3942efd commit 16606af

File tree

2 files changed

+210
-0
lines changed

2 files changed

+210
-0
lines changed

lightning/src/util/ser.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,12 @@ 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<HighZeroBytesDroppedBigSize<$val_type>> for $val_type {
459+
fn from(val: HighZeroBytesDroppedBigSize<$val_type>) -> Self { val.0 }
460+
}
455461
}
456462
}
457463

@@ -524,6 +530,50 @@ impl_array!(PUBLIC_KEY_SIZE); // for PublicKey
524530
impl_array!(COMPACT_SIGNATURE_SIZE); // for Signature
525531
impl_array!(1300); // for OnionPacket.hop_data
526532

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

lightning/src/util/ser_macros.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ macro_rules! encode_tlv {
2626
field.write($stream)?;
2727
}
2828
};
29+
($stream: expr, $type: expr, $field: expr, (tlv_record, $fieldty:ident$(<$gen:ident>)?)) => {
30+
if let Some(field) = $field {
31+
let field: encoded_tlv_record_ref_type!($fieldty$(<$gen>)?) = From::from(field);
32+
BigSize($type).write($stream)?;
33+
BigSize(field.serialized_length() as u64).write($stream)?;
34+
field.write($stream)?;
35+
}
36+
};
2937
}
3038

3139
macro_rules! encode_tlv_stream {
@@ -121,6 +129,9 @@ macro_rules! check_tlv_order {
121129
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
122130
// no-op
123131
}};
132+
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (tlv_record, $fieldty:ident$(<$gen:ident>)?)) => {{
133+
// no-op
134+
}};
124135
}
125136

126137
macro_rules! check_missing_tlv {
@@ -150,6 +161,9 @@ macro_rules! check_missing_tlv {
150161
($last_seen_type: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
151162
// no-op
152163
}};
164+
($last_seen_type: expr, $type: expr, $field: ident, (tlv_record, $fieldty:ident$(<$gen:ident>)?)) => {{
165+
// no-op
166+
}};
153167
}
154168

155169
macro_rules! decode_tlv {
@@ -172,6 +186,13 @@ macro_rules! decode_tlv {
172186
($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
173187
$field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
174188
}};
189+
($reader: expr, $field: ident, (tlv_record, $fieldty:ident$(<$gen:ident>)?)) => {{
190+
$field = {
191+
let field: encoded_tlv_record_type!($fieldty$(<$gen>)?) =
192+
ser::Readable::read(&mut $reader)?;
193+
Some(field.into())
194+
};
195+
}};
175196
}
176197

177198
macro_rules! decode_tlv_stream {
@@ -360,6 +381,9 @@ macro_rules! init_tlv_based_struct_field {
360381
($field: ident, vec_type) => {
361382
$field.unwrap()
362383
};
384+
($field: ident, tlv_record) => {
385+
$field
386+
};
363387
}
364388

365389
macro_rules! init_tlv_field_var {
@@ -375,6 +399,9 @@ macro_rules! init_tlv_field_var {
375399
($field: ident, option) => {
376400
let mut $field = None;
377401
};
402+
($field: ident, tlv_record) => {
403+
let mut $field = None;
404+
};
378405
}
379406

380407
/// Implements Readable/Writeable for a struct storing it as a set of TLVs
@@ -427,6 +454,139 @@ macro_rules! impl_writeable_tlv_based {
427454
}
428455
}
429456

457+
/// Defines a struct for a TLV stream and a similar struct using references for non-primitive types,
458+
/// implementing [`Readable`] for the former and [`Writeable`] for the latter. Useful as an
459+
/// intermediary format when reading or writing a type encoded as a TLV stream. Note that each field
460+
/// representing a TLV record has its type wrapped with an [`Option`].
461+
///
462+
/// [`Readable`]: crate::util::ser::Readable
463+
/// [`Writeable`]: crate::util::ser::Writeable
464+
macro_rules! tlv_stream {
465+
($name:ident, $nameref:ident, {
466+
$(($type:expr, $field:ident : $fieldty:ident$(<$gen:ident>)?)),* $(,)*
467+
}) => {
468+
#[derive(Debug)]
469+
struct $name {
470+
$(
471+
$field: Option<$fieldty$(<$gen>)?>,
472+
)*
473+
}
474+
475+
pub(crate) struct $nameref<'a> {
476+
$(
477+
pub(crate) $field: Option<tlv_record_ref_type!($fieldty$(<$gen>)?)>,
478+
)*
479+
}
480+
481+
impl<'a> ::util::ser::Writeable for $nameref<'a> {
482+
fn write<W: ::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
483+
encode_tlv_stream!(writer, {
484+
$(($type, self.$field, (tlv_record, $fieldty$(<$gen>)?))),*
485+
});
486+
Ok(())
487+
}
488+
}
489+
490+
impl ::util::ser::Readable for $name {
491+
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
492+
$(
493+
init_tlv_field_var!($field, tlv_record);
494+
)*
495+
decode_tlv_stream!(reader, {
496+
$(($type, $field, (tlv_record, $fieldty$(<$gen>)?))),*
497+
});
498+
499+
Ok(Self {
500+
$(
501+
$field: init_tlv_based_struct_field!($field, tlv_record)
502+
),*
503+
})
504+
}
505+
}
506+
}
507+
}
508+
509+
macro_rules! tlv_record_ref_type {
510+
(u8) => {
511+
u8
512+
};
513+
(u16) => {
514+
u16
515+
};
516+
(u32) => {
517+
u32
518+
};
519+
(u64) => {
520+
u64
521+
};
522+
(char) => {
523+
char
524+
};
525+
(String) => {
526+
&'a crate::prelude::String
527+
};
528+
(Vec<$type:ty>) => {
529+
&'a crate::prelude::Vec<$type>
530+
};
531+
($type:ident$(<$gen:ident>)?) => {
532+
&'a $type$(<$gen>)?
533+
};
534+
}
535+
536+
macro_rules! encoded_tlv_record_type {
537+
(u8) => {
538+
u8
539+
};
540+
(u16) => {
541+
::util::ser::HighZeroBytesDroppedBigSize<u16>
542+
};
543+
(u32) => {
544+
::util::ser::HighZeroBytesDroppedBigSize<u32>
545+
};
546+
(u64) => {
547+
::util::ser::HighZeroBytesDroppedBigSize<u64>
548+
};
549+
(char) => {
550+
char
551+
};
552+
(String) => {
553+
::util::ser::WithoutLength<crate::prelude::String>
554+
};
555+
(Vec<$type:ty>) => {
556+
::util::ser::WithoutLength<crate::prelude::Vec<$type>>
557+
};
558+
($type:ident$(<$gen:ident>)?) => {
559+
$type$(<$gen>)?
560+
};
561+
}
562+
563+
macro_rules! encoded_tlv_record_ref_type {
564+
(u8) => {
565+
u8
566+
};
567+
(u16) => {
568+
::util::ser::HighZeroBytesDroppedBigSize<u16>
569+
};
570+
(u32) => {
571+
::util::ser::HighZeroBytesDroppedBigSize<u32>
572+
};
573+
(u64) => {
574+
::util::ser::HighZeroBytesDroppedBigSize<u64>
575+
};
576+
(char) => {
577+
char
578+
};
579+
(String) => {
580+
::util::ser::WithoutLength<&crate::prelude::String>
581+
};
582+
(Vec<$type:ty>) => {
583+
::util::ser::WithoutLength<&crate::prelude::Vec<$type>>
584+
};
585+
($type:ident$(<$gen:ident>)?) => {
586+
&$type$(<$gen>)?
587+
};
588+
}
589+
430590
macro_rules! _impl_writeable_tlv_based_enum_common {
431591
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
432592
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}

0 commit comments

Comments
 (0)