($stream: expr, $type: expr, $field: expr, (default_value, $default: expr)) => {
encode_tlv!($stream, $type, $field, required)
};
+ ($stream: expr, $type: expr, $field: expr, (static_value, $value: expr)) => {
+ let _ = &$field; // Ensure we "use" the $field
+ };
($stream: expr, $type: expr, $field: expr, required) => {
BigSize($type).write($stream)?;
BigSize($field.serialized_length() as u64).write($stream)?;
};
}
+
+macro_rules! check_encoded_tlv_order {
+ ($last_type: expr, $type: expr, (static_value, $value: expr)) => { };
+ ($last_type: expr, $type: expr, $fieldty: tt) => {
+ if let Some(t) = $last_type {
+ debug_assert!(t <= $type);
+ }
+ $last_type = Some($type);
+ };
+}
+
macro_rules! encode_tlv_stream {
($stream: expr, {$(($type: expr, $field: expr, $fieldty: tt)),* $(,)*}) => { {
#[allow(unused_imports)]
{
let mut last_seen: Option<u64> = None;
$(
- if let Some(t) = last_seen {
- debug_assert!(t <= $type);
- }
- last_seen = Some($type);
+ check_encoded_tlv_order!(last_seen, $type, $fieldty);
)*
}
} }
($len: expr, $type: expr, $field: expr, (default_value, $default: expr)) => {
get_varint_length_prefixed_tlv_length!($len, $type, $field, required)
};
+ ($len: expr, $type: expr, $field: expr, (static_value, $value: expr)) => {
+ };
($len: expr, $type: expr, $field: expr, required) => {
BigSize($type).write(&mut $len).expect("No in-memory data may fail to serialize");
let field_len = $field.serialized_length();
} }
}
-macro_rules! check_tlv_order {
+macro_rules! check_decoded_tlv_order {
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (default_value, $default: expr)) => {{
#[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always true
let invalid_order = ($last_seen_type.is_none() || $last_seen_type.unwrap() < $type) && $typ.0 > $type;
$field = $default.into();
}
}};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (static_value, $value: expr)) => {
+ };
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, required) => {{
#[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always true
let invalid_order = ($last_seen_type.is_none() || $last_seen_type.unwrap() < $type) && $typ.0 > $type;
$field = $default.into();
}
}};
+ ($last_seen_type: expr, $type: expr, $field: expr, (static_value, $value: expr)) => {
+ $field = $value;
+ };
($last_seen_type: expr, $type: expr, $field: ident, required) => {{
#[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always true
let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type;
($reader: expr, $field: ident, (default_value, $default: expr)) => {{
decode_tlv!($reader, $field, required)
}};
+ ($reader: expr, $field: ident, (static_value, $value: expr)) => {{
+ }};
($reader: expr, $field: ident, required) => {{
$field = $crate::util::ser::Readable::read(&mut $reader)?;
}};
}};
}
+macro_rules! _decode_tlv_stream_match_check {
+ ($val: ident, $type: expr, (static_value, $value: expr)) => { false };
+ ($val: ident, $type: expr, $fieldty: tt) => { $val == $type }
+}
+
// `$decode_custom_tlv` is a closure that may be optionally provided to handle custom message types.
// If it is provided, it will be called with the custom type and the `FixedLengthReader` containing
// the message contents. It should return `Ok(true)` if the custom message is successfully parsed,
}
// As we read types, make sure we hit every required type:
$({
- check_tlv_order!(last_seen_type, typ, $type, $field, $fieldty);
+ check_decoded_tlv_order!(last_seen_type, typ, $type, $field, $fieldty);
})*
last_seen_type = Some(typ.0);
let length: ser::BigSize = $crate::util::ser::Readable::read(&mut stream_ref)?;
let mut s = ser::FixedLengthReader::new(&mut stream_ref, length.0);
match typ.0 {
- $($type => {
+ $(_t if _decode_tlv_stream_match_check!(_t, $type, $fieldty) => {
decode_tlv!(s, $field, $fieldty);
if s.bytes_remain() {
s.eat_remaining()?; // Return ShortRead if there's actually not enough bytes
($field: ident, (default_value, $default: expr)) => {
$field.0.unwrap()
};
+ ($field: ident, (static_value, $value: expr)) => {
+ $field
+ };
($field: ident, option) => {
$field
};
($field: ident, (default_value, $default: expr)) => {
let mut $field = $crate::util::ser::OptionDeserWrapper(None);
};
+ ($field: ident, (static_value, $value: expr)) => {
+ let $field;
+ };
($field: ident, required) => {
let mut $field = $crate::util::ser::OptionDeserWrapper(None);
};
};
}
+macro_rules! init_and_read_tlv_fields {
+ ($reader: ident, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => {
+ $(
+ init_tlv_field_var!($field, $fieldty);
+ )*
+
+ read_tlv_fields!($reader, {
+ $(($type, $field, $fieldty)),*
+ });
+ }
+}
+
/// Implements Readable/Writeable for a struct storing it as a set of TLVs
/// If $fieldty is `required`, then $field is a required field that is not an Option nor a Vec.
/// If $fieldty is `option`, then $field is optional field.
impl $crate::util::ser::Readable for $st {
fn read<R: $crate::io::Read>(reader: &mut R) -> Result<Self, $crate::ln::msgs::DecodeError> {
- $(
- init_tlv_field_var!($field, $fieldty);
- )*
- read_tlv_fields!(reader, {
+ init_and_read_tlv_fields!(reader, {
$(($type, $field, $fieldty)),*
});
Ok(Self {
$(($type:expr, $field:ident : $fieldty:tt)),* $(,)*
}) => {
#[derive(Debug)]
- pub(crate) struct $name {
+ pub(super) struct $name {
$(
- $field: Option<tlv_record_type!($fieldty)>,
+ pub(super) $field: Option<tlv_record_type!($fieldty)>,
)*
}
- pub(crate) struct $nameref<'a> {
+ #[derive(Debug, PartialEq)]
+ pub(super) struct $nameref<'a> {
$(
- pub(crate) $field: Option<tlv_record_ref_type!($fieldty)>,
+ pub(super) $field: Option<tlv_record_ref_type!($fieldty)>,
)*
}
// Because read_tlv_fields creates a labeled loop, we cannot call it twice
// in the same function body. Instead, we define a closure and call it.
let f = || {
- $(
- init_tlv_field_var!($field, $fieldty);
- )*
- read_tlv_fields!(reader, {
+ init_and_read_tlv_fields!(reader, {
$(($type, $field, $fieldty)),*
});
Ok(Some($st::$variant_name {
// Because read_tlv_fields creates a labeled loop, we cannot call it twice
// in the same function body. Instead, we define a closure and call it.
let f = || {
- $(
- init_tlv_field_var!($field, $fieldty);
- )*
- read_tlv_fields!(reader, {
+ init_and_read_tlv_fields!(reader, {
$(($type, $field, $fieldty)),*
});
Ok($st::$variant_name {
Ok($st::$tuple_variant_name(Readable::read(reader)?))
}),*
_ => {
- Err(DecodeError::UnknownRequiredFeature)
+ Err($crate::ln::msgs::DecodeError::UnknownRequiredFeature)
},
}
}