+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
+ $crate::_check_decoded_tlv_order!($last_seen_type, $typ, $type, $field, required);
+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, option) => {{
+ // no-op
+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, required_vec) => {{
+ $crate::_check_decoded_tlv_order!($last_seen_type, $typ, $type, $field, required);
+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, optional_vec) => {{
+ // no-op
+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, upgradable_required) => {{
+ _check_decoded_tlv_order!($last_seen_type, $typ, $type, $field, required)
+ }};
+ ($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, upgradable_option) => {{
+ // no-op
+ }};
+ ($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
+ }};
+}
+
+/// Errors if there are missing required TLV types after the last seen type.
+/// This is exported for use by other exported macros, do not use directly.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _check_missing_tlv {
+ ($last_seen_type: expr, $type: expr, $field: ident, (default_value, $default: expr)) => {{
+ #[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always false
+ let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type;
+ if missing_req_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 false
+ let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type;
+ if missing_req_type {
+ return Err(DecodeError::InvalidValue);
+ }
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
+ $crate::_check_missing_tlv!($last_seen_type, $type, $field, required);
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, required_vec) => {{
+ $crate::_check_missing_tlv!($last_seen_type, $type, $field, required);
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, option) => {{
+ // no-op
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, optional_vec) => {{
+ // no-op
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, upgradable_required) => {{
+ _check_missing_tlv!($last_seen_type, $type, $field, required)
+ }};
+ ($last_seen_type: expr, $type: expr, $field: ident, upgradable_option) => {{
+ // no-op
+ }};
+ ($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
+ }};
+}
+
+/// Implements deserialization for a single TLV record.
+/// This is exported for use by other exported macros, do not use directly.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _decode_tlv {
+ ($outer_reader: expr, $reader: expr, $field: ident, (default_value, $default: expr)) => {{
+ $crate::_decode_tlv!($outer_reader, $reader, $field, required)
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (static_value, $value: expr)) => {{
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, required) => {{
+ $field = $crate::util::ser::Readable::read(&mut $reader)?;
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (required: $trait: ident $(, $read_arg: expr)?)) => {{
+ $field = $trait::read(&mut $reader $(, $read_arg)*)?;
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, required_vec) => {{
+ let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
+ $field = f.0;
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, option) => {{
+ $field = Some($crate::util::ser::Readable::read(&mut $reader)?);
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, optional_vec) => {{
+ let f: $crate::util::ser::WithoutLength<Vec<_>> = $crate::util::ser::Readable::read(&mut $reader)?;
+ $field = Some(f.0);
+ }};
+ // `upgradable_required` indicates we're reading a required TLV that may have been upgraded
+ // without backwards compat. We'll error if the field is missing, and return `Ok(None)` if the
+ // field is present but we can no longer understand it.
+ // Note that this variant can only be used within a `MaybeReadable` read.
+ ($outer_reader: expr, $reader: expr, $field: ident, upgradable_required) => {{
+ $field = match $crate::util::ser::MaybeReadable::read(&mut $reader)? {
+ Some(res) => res,
+ None => {
+ // If we successfully read a value but we don't know how to parse it, we give up
+ // and immediately return `None`. However, we need to make sure we read the correct
+ // number of bytes for this TLV stream, which is implicitly the end of the stream.
+ // Thus, we consume everything left in the `$outer_reader` here, ensuring that if
+ // we're being read as a part of another TLV stream we don't spuriously fail to
+ // deserialize the outer object due to a TLV length mismatch.
+ $crate::io_extras::copy($outer_reader, &mut $crate::io_extras::sink()).unwrap();
+ return Ok(None)
+ },
+ };
+ }};
+ // `upgradable_option` indicates we're reading an Option-al TLV that may have been upgraded
+ // without backwards compat. $field will be None if the TLV is missing or if the field is present
+ // but we can no longer understand it.
+ ($outer_reader: expr, $reader: expr, $field: ident, upgradable_option) => {{
+ $field = $crate::util::ser::MaybeReadable::read(&mut $reader)?;
+ if $field.is_none() {
+ #[cfg(not(debug_assertions))] {
+ // In general, MaybeReadable implementations are required to consume all the bytes
+ // of the object even if they don't understand it, but due to a bug in the
+ // serialization format for `impl_writeable_tlv_based_enum_upgradable` we sometimes
+ // don't know how many bytes that is. In such cases, we'd like to spuriously allow
+ // TLV length mismatches, which we do here by calling `eat_remaining` so that the
+ // `s.bytes_remain()` check in `_decode_tlv_stream_range` doesn't fail.
+ $reader.eat_remaining()?;
+ }
+ }
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (option: $trait: ident $(, $read_arg: expr)?)) => {{
+ $field = Some($trait::read(&mut $reader $(, $read_arg)*)?);
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident, $encoder:ty))) => {{
+ $crate::_decode_tlv!($outer_reader, $reader, $field, (option, encoding: ($fieldty, $encoding)));
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: ($fieldty: ty, $encoding: ident))) => {{
+ $field = {
+ let field: $encoding<$fieldty> = ser::Readable::read(&mut $reader)?;
+ Some(field.0)
+ };
+ }};
+ ($outer_reader: expr, $reader: expr, $field: ident, (option, encoding: $fieldty: ty)) => {{
+ $crate::_decode_tlv!($outer_reader, $reader, $field, option);
+ }};
+}
+
+/// Checks if `$val` matches `$type`.
+/// This is exported for use by other exported macros, do not use directly.
+#[doc(hidden)]
+#[macro_export]
+macro_rules! _decode_tlv_stream_match_check {
+ ($val: ident, $type: expr, (static_value, $value: expr)) => { false };
+ ($val: ident, $type: expr, $fieldty: tt) => { $val == $type }
+}
+
+/// Implements the TLVs deserialization part in a [`Readable`] implementation of a struct.
+///
+/// This should be called inside a method which returns `Result<_, `[`DecodeError`]`>`, such as
+/// [`Readable::read`]. It will either return an `Err` or ensure all `required` fields have been
+/// read and optionally read `optional` fields.
+///
+/// `$stream` must be a [`Read`] and will be fully consumed, reading until no more bytes remain
+/// (i.e. it returns [`DecodeError::ShortRead`]).
+///
+/// Fields MUST be sorted in `$type`-order.
+///
+/// Note that the lightning TLV requirements require that a single type not appear more than once,
+/// that TLVs are sorted in type-ascending order, and that any even types be understood by the
+/// decoder.
+///
+/// For example,
+/// ```
+/// # use lightning::decode_tlv_stream;
+/// # fn read<R: lightning::io::Read> (stream: R) -> Result<(), lightning::ln::msgs::DecodeError> {
+/// let mut required_value = 0u64;
+/// let mut optional_value: Option<u64> = None;
+/// decode_tlv_stream!(stream, {
+/// (0, required_value, required),
+/// (2, optional_value, option),
+/// });
+/// // At this point, `required_value` has been overwritten with the TLV with type 0.
+/// // `optional_value` may have been overwritten, setting it to `Some` if a TLV with type 2 was
+/// // present.
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Readable`]: crate::util::ser::Readable
+/// [`DecodeError`]: crate::ln::msgs::DecodeError
+/// [`Readable::read`]: crate::util::ser::Readable::read
+/// [`Read`]: crate::io::Read
+/// [`DecodeError::ShortRead`]: crate::ln::msgs::DecodeError::ShortRead
+#[macro_export]
+macro_rules! decode_tlv_stream {
+ ($stream: expr, {$(($type: expr, $field: ident, $fieldty: tt)),* $(,)*}) => {
+ let rewind = |_, _| { unreachable!() };
+ $crate::_decode_tlv_stream_range!($stream, .., rewind, {$(($type, $field, $fieldty)),*});
+ }
+}