mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
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.
This commit is contained in:
parent
227fd51cb4
commit
904d322923
2 changed files with 110 additions and 0 deletions
|
@ -419,6 +419,9 @@ macro_rules! impl_writeable_primitive {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl From<$val_type> for HighZeroBytesDroppedBigSize<$val_type> {
|
||||
fn from(val: $val_type) -> Self { Self(val) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,6 +521,23 @@ impl Readable for [u16; 8] {
|
|||
/// Used to prevent encoding the length twice.
|
||||
pub(crate) struct WithoutLength<T>(pub T);
|
||||
|
||||
impl Writeable for WithoutLength<&String> {
|
||||
#[inline]
|
||||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
||||
w.write_all(self.0.as_bytes())
|
||||
}
|
||||
}
|
||||
impl Readable for WithoutLength<String> {
|
||||
#[inline]
|
||||
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
|
||||
let v: WithoutLength<Vec<u8>> = Readable::read(r)?;
|
||||
Ok(Self(String::from_utf8(v.0).map_err(|_| DecodeError::InvalidValue)?))
|
||||
}
|
||||
}
|
||||
impl<'a> From<&'a String> for WithoutLength<&'a String> {
|
||||
fn from(s: &'a String) -> Self { Self(s) }
|
||||
}
|
||||
|
||||
impl<'a, T: Writeable> Writeable for WithoutLength<&'a Vec<T>> {
|
||||
#[inline]
|
||||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
|
||||
|
|
|
@ -26,6 +26,12 @@ macro_rules! encode_tlv {
|
|||
field.write($stream)?;
|
||||
}
|
||||
};
|
||||
($stream: expr, $type: expr, $field: expr, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
|
||||
encode_tlv!($stream, $type, $field.map(|f| $encoding(f)), option);
|
||||
};
|
||||
($stream: expr, $type: expr, $field: expr, (option, encoding: $fieldty: ty)) => {
|
||||
encode_tlv!($stream, $type, $field, option);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! encode_tlv_stream {
|
||||
|
@ -121,6 +127,9 @@ macro_rules! check_tlv_order {
|
|||
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
|
||||
// no-op
|
||||
}};
|
||||
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (option, encoding: $encoding: tt)) => {{
|
||||
// no-op
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! check_missing_tlv {
|
||||
|
@ -150,6 +159,9 @@ macro_rules! check_missing_tlv {
|
|||
($last_seen_type: expr, $type: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
|
||||
// no-op
|
||||
}};
|
||||
($last_seen_type: expr, $type: expr, $field: ident, (option, encoding: $encoding: tt)) => {{
|
||||
// no-op
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! decode_tlv {
|
||||
|
@ -172,6 +184,15 @@ macro_rules! decode_tlv {
|
|||
($reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
|
||||
$field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
|
||||
}};
|
||||
($reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
|
||||
$field = {
|
||||
let field: $encoding<$fieldty> = ser::Readable::read(&mut $reader)?;
|
||||
Some(field.0)
|
||||
};
|
||||
}};
|
||||
($reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
|
||||
decode_tlv!($reader, $field, option);
|
||||
}};
|
||||
}
|
||||
|
||||
// `$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 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Defines a struct for a TLV stream and a similar struct using references for non-primitive types,
|
||||
/// implementing [`Readable`] for the former and [`Writeable`] for the latter. Useful as an
|
||||
/// intermediary format when reading or writing a type encoded as a TLV stream. Note that each field
|
||||
/// representing a TLV record has its type wrapped with an [`Option`]. A tuple consisting of a type
|
||||
/// and a serialization wrapper may be given in place of a type when custom serialization is
|
||||
/// required.
|
||||
///
|
||||
/// [`Readable`]: crate::util::ser::Readable
|
||||
/// [`Writeable`]: crate::util::ser::Writeable
|
||||
macro_rules! tlv_stream {
|
||||
($name:ident, $nameref:ident, {
|
||||
$(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
|
||||
}) => {
|
||||
#[derive(Debug)]
|
||||
struct $name {
|
||||
$(
|
||||
$field: Option<tlv_record_type!($fieldty)>,
|
||||
)*
|
||||
}
|
||||
|
||||
pub(crate) struct $nameref<'a> {
|
||||
$(
|
||||
pub(crate) $field: Option<tlv_record_ref_type!($fieldty)>,
|
||||
)*
|
||||
}
|
||||
|
||||
impl<'a> $crate::util::ser::Writeable for $nameref<'a> {
|
||||
fn write<W: $crate::util::ser::Writer>(&self, writer: &mut W) -> Result<(), $crate::io::Error> {
|
||||
encode_tlv_stream!(writer, {
|
||||
$(($type, self.$field, (option, encoding: $fieldty))),*
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::util::ser::Readable for $name {
|
||||
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, $crate::ln::msgs::DecodeError> {
|
||||
$(
|
||||
init_tlv_field_var!($field, option);
|
||||
)*
|
||||
decode_tlv_stream!(reader, {
|
||||
$(($type, $field, (option, encoding: $fieldty))),*
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
$(
|
||||
$field: $field
|
||||
),*
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tlv_record_type {
|
||||
(($type:ty, $wrapper:ident)) => { $type };
|
||||
($type:ty) => { $type };
|
||||
}
|
||||
|
||||
macro_rules! tlv_record_ref_type {
|
||||
(char) => { char };
|
||||
(u8) => { u8 };
|
||||
((u16, $wrapper: ident)) => { u16 };
|
||||
((u32, $wrapper: ident)) => { u32 };
|
||||
((u64, $wrapper: ident)) => { u64 };
|
||||
(($type:ty, $wrapper:ident)) => { &'a $type };
|
||||
($type:ty) => { &'a $type };
|
||||
}
|
||||
|
||||
macro_rules! _impl_writeable_tlv_based_enum_common {
|
||||
($st: ident, $(($variant_id: expr, $variant_name: ident) =>
|
||||
{$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}
|
||||
|
|
Loading…
Add table
Reference in a new issue