X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Futil%2Fser_macros.rs;fp=lightning%2Fsrc%2Futil%2Fser_macros.rs;h=b8119ddd5bd26a9ddb1580f2ef09c660110a7a2f;hb=df829a85263e0bc675ca696c0f6ff0a49d89e623;hp=8729018935c944b10a24acefa24e52bf8debbf49;hpb=f30694bd8c39a967848a84752e3b7d8d520cd0b1;p=rust-lightning diff --git a/lightning/src/util/ser_macros.rs b/lightning/src/util/ser_macros.rs index 87290189..b8119ddd 100644 --- a/lightning/src/util/ser_macros.rs +++ b/lightning/src/util/ser_macros.rs @@ -115,8 +115,12 @@ macro_rules! decode_tlv { _ => {}, } // As we read types, make sure we hit every required type: - $(if (last_seen_type.is_none() || last_seen_type.unwrap() < $reqtype) && typ.0 > $reqtype { - Err(DecodeError::InvalidValue)? + $({ + #[allow(unused_comparisons)] // Note that $reqtype may be 0 making the second comparison always true + let invalid_order = (last_seen_type.is_none() || last_seen_type.unwrap() < $reqtype) && typ.0 > $reqtype; + if invalid_order { + Err(DecodeError::InvalidValue)? + } })* last_seen_type = Some(typ.0); @@ -146,8 +150,12 @@ macro_rules! decode_tlv { s.eat_remaining()?; } // Make sure we got to each required type after we've read every TLV: - $(if last_seen_type.is_none() || last_seen_type.unwrap() < $reqtype { - Err(DecodeError::InvalidValue)? + $({ + #[allow(unused_comparisons)] // Note that $reqtype may be 0 making the second comparison always true + let missing_req_type = last_seen_type.is_none() || last_seen_type.unwrap() < $reqtype; + if missing_req_type { + Err(DecodeError::InvalidValue)? + } })* } } } @@ -246,7 +254,7 @@ macro_rules! write_ver_prefix { /// This is the preferred method of adding new fields that old nodes can ignore and still function /// correctly. macro_rules! write_tlv_fields { - ($stream: expr, {$(($type: expr, $field: expr)),*}, {$(($optional_type: expr, $optional_field: expr)),*}) => { + ($stream: expr, {$(($type: expr, $field: expr)),* $(,)*}, {$(($optional_type: expr, $optional_field: expr)),* $(,)*}) => { encode_varint_length_prefixed_tlv!($stream, {$(($type, $field)),*} , {$(($optional_type, $optional_field)),*}); } } @@ -267,7 +275,7 @@ macro_rules! read_ver_prefix { /// Reads a suffix added by write_tlv_fields. macro_rules! read_tlv_fields { - ($stream: expr, {$(($reqtype: expr, $reqfield: ident)),*}, {$(($type: expr, $field: ident)),*}) => { { + ($stream: expr, {$(($reqtype: expr, $reqfield: ident)),* $(,)*}, {$(($type: expr, $field: ident)),* $(,)*}) => { { let tlv_len = ::util::ser::BigSize::read($stream)?; let mut rd = ::util::ser::FixedLengthReader::new($stream, tlv_len.0); decode_tlv!(&mut rd, {$(($reqtype, $reqfield)),*}, {$(($type, $field)),*}); @@ -275,6 +283,101 @@ macro_rules! read_tlv_fields { } } } +// If we naively create a struct in impl_writeable_tlv_based below, we may end up returning +// `Self { ,,vecfield: vecfield }` which is obviously incorrect. Instead, we have to match here to +// detect at least one empty field set and skip the potentially-extra comma. +macro_rules! _init_tlv_based_struct { + ({}, {$($field: ident),*}, {$($vecfield: ident),*}) => { + Ok(Self { + $($field),*, + $($vecfield: $vecfield.unwrap().0),* + }) + }; + ({$($reqfield: ident),*}, {}, {$($vecfield: ident),*}) => { + Ok(Self { + $($reqfield: $reqfield.0.unwrap()),*, + $($vecfield: $vecfield.unwrap().0),* + }) + }; + ({$($reqfield: ident),*}, {$($field: ident),*}, {}) => { + Ok(Self { + $($reqfield: $reqfield.0.unwrap()),*, + $($field),* + }) + }; + ({$($reqfield: ident),*}, {$($field: ident),*}, {$($vecfield: ident),*}) => { + Ok(Self { + $($reqfield: $reqfield.0.unwrap()),*, + $($field),*, + $($vecfield: $vecfield.unwrap().0),* + }) + } +} + +// If we don't have any optional types below, but do have some vec types, we end up calling +// `write_tlv_field!($stream, {..}, {, (vec_ty, vec_val)})`, which is obviously broken. +// Instead, for write and read we match the missing values and skip the extra comma. +macro_rules! _write_tlv_fields { + ($stream: expr, {$(($type: expr, $field: expr)),* $(,)*}, {}, {$(($optional_type: expr, $optional_field: expr)),* $(,)*}) => { + write_tlv_fields!($stream, {$(($type, $field)),*} , {$(($optional_type, $optional_field)),*}); + }; + ($stream: expr, {$(($type: expr, $field: expr)),* $(,)*}, {$(($optional_type: expr, $optional_field: expr)),* $(,)*}, {$(($optional_type_2: expr, $optional_field_2: expr)),* $(,)*}) => { + write_tlv_fields!($stream, {$(($type, $field)),*} , {$(($optional_type, $optional_field)),*, $(($optional_type_2, $optional_field_2)),*}); + } +} +macro_rules! _read_tlv_fields { + ($stream: expr, {$(($reqtype: expr, $reqfield: ident)),* $(,)*}, {}, {$(($type: expr, $field: ident)),* $(,)*}) => { + read_tlv_fields!($stream, {$(($reqtype, $reqfield)),*}, {$(($type, $field)),*}); + }; + ($stream: expr, {$(($reqtype: expr, $reqfield: ident)),* $(,)*}, {$(($type: expr, $field: ident)),* $(,)*}, {$(($type_2: expr, $field_2: ident)),* $(,)*}) => { + read_tlv_fields!($stream, {$(($reqtype, $reqfield)),*}, {$(($type, $field)),*, $(($type_2, $field_2)),*}); + } +} + +/// Implements Readable/Writeable for a struct storing it as a set of TLVs +/// First block includes all the required fields including a dummy value which is used during +/// deserialization but which will never be exposed to other code. +/// The second block includes optional fields. +/// The third block includes any Vecs which need to have their individual elements serialized. +macro_rules! impl_writeable_tlv_based { + ($st: ident, {$(($reqtype: expr, $reqfield: ident)),* $(,)*}, {$(($type: expr, $field: ident)),* $(,)*}, {$(($vectype: expr, $vecfield: ident)),* $(,)*}) => { + impl ::util::ser::Writeable for $st { + fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { + _write_tlv_fields!(writer, { + $(($reqtype, self.$reqfield)),* + }, { + $(($type, self.$field)),* + }, { + $(($vectype, Some(::util::ser::VecWriteWrapper(&self.$vecfield)))),* + }); + Ok(()) + } + } + + impl ::util::ser::Readable for $st { + fn read(reader: &mut R) -> Result { + $( + let mut $reqfield = ::util::ser::OptionDeserWrapper(None); + )* + $( + let mut $field = None; + )* + $( + let mut $vecfield = Some(::util::ser::VecReadWrapper(Vec::new())); + )* + _read_tlv_fields!(reader, { + $(($reqtype, $reqfield)),* + }, { + $(($type, $field)),* + }, { + $(($vectype, $vecfield)),* + }); + _init_tlv_based_struct!({$($reqfield),*}, {$($field),*}, {$($vecfield),*}) + } + } + } +} + #[cfg(test)] mod tests { use prelude::*;