Merge pull request #1956 from TheBlueMatt/2023-01-ser-cleanups
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Wed, 18 Jan 2023 21:31:07 +0000 (21:31 +0000)
committerGitHub <noreply@github.com>
Wed, 18 Jan 2023 21:31:07 +0000 (21:31 +0000)
Trivial Serialization Tweaks

lightning/src/util/ser.rs
lightning/src/util/ser_macros.rs

index a656604d458c492c17e8fc9a206c20afe4faa5ef..84d1a2e084feb65e1472969875a586c81ccd08e5 100644 (file)
@@ -22,6 +22,8 @@ use core::cmp;
 use core::convert::TryFrom;
 use core::ops::Deref;
 
+use alloc::collections::BTreeMap;
+
 use bitcoin::secp256k1::{PublicKey, SecretKey};
 use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE, SCHNORR_SIGNATURE_SIZE};
 use bitcoin::secp256k1::ecdsa;
@@ -381,6 +383,40 @@ impl Readable for BigSize {
        }
 }
 
+/// The lightning protocol uses u16s for lengths in most cases. As our serialization framework
+/// primarily targets that, we must as well. However, because we may serialize objects that have
+/// more than 65K entries, we need to be able to store larger values. Thus, we define a variable
+/// length integer here that is backwards-compatible for values < 0xffff. We treat 0xffff as
+/// "read eight more bytes".
+///
+/// To ensure we only have one valid encoding per value, we add 0xffff to values written as eight
+/// bytes. Thus, 0xfffe is serialized as 0xfffe, whereas 0xffff is serialized as
+/// 0xffff0000000000000000 (i.e. read-eight-bytes then zero).
+struct CollectionLength(pub u64);
+impl Writeable for CollectionLength {
+       #[inline]
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+               if self.0 < 0xffff {
+                       (self.0 as u16).write(writer)
+               } else {
+                       0xffffu16.write(writer)?;
+                       (self.0 - 0xffff).write(writer)
+               }
+       }
+}
+
+impl Readable for CollectionLength {
+       #[inline]
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let mut val: u64 = <u16 as Readable>::read(r)? as u64;
+               if val == 0xffff {
+                       val = <u64 as Readable>::read(r)?
+                               .checked_add(0xffff).ok_or(DecodeError::InvalidValue)?;
+               }
+               Ok(CollectionLength(val))
+       }
+}
+
 /// In TLV we occasionally send fields which only consist of, or potentially end with, a
 /// variable-length integer which is simply truncated by skipping high zero bytes. This type
 /// encapsulates such integers implementing [`Readable`]/[`Writeable`] for them.
@@ -588,50 +624,54 @@ impl<'a, T> From<&'a Vec<T>> for WithoutLength<&'a Vec<T>> {
        fn from(v: &'a Vec<T>) -> Self { Self(v) }
 }
 
-// HashMap
-impl<K, V> Writeable for HashMap<K, V>
-       where K: Writeable + Eq + Hash,
-             V: Writeable
-{
-       #[inline]
-       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-       (self.len() as u16).write(w)?;
-               for (key, value) in self.iter() {
-                       key.write(w)?;
-                       value.write(w)?;
+macro_rules! impl_for_map {
+       ($ty: ident, $keybound: ident, $constr: expr) => {
+               impl<K, V> Writeable for $ty<K, V>
+                       where K: Writeable + Eq + $keybound, V: Writeable
+               {
+                       #[inline]
+                       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+                               CollectionLength(self.len() as u64).write(w)?;
+                               for (key, value) in self.iter() {
+                                       key.write(w)?;
+                                       value.write(w)?;
+                               }
+                               Ok(())
+                       }
                }
-               Ok(())
-       }
-}
 
-impl<K, V> Readable for HashMap<K, V>
-       where K: Readable + Eq + Hash,
-             V: MaybeReadable
-{
-       #[inline]
-       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let len: u16 = Readable::read(r)?;
-               let mut ret = HashMap::with_capacity(len as usize);
-               for _ in 0..len {
-                       let k = K::read(r)?;
-                       let v_opt = V::read(r)?;
-                       if let Some(v) = v_opt {
-                               if ret.insert(k, v).is_some() {
-                                       return Err(DecodeError::InvalidValue);
+               impl<K, V> Readable for $ty<K, V>
+                       where K: Readable + Eq + $keybound, V: MaybeReadable
+               {
+                       #[inline]
+                       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+                               let len: CollectionLength = Readable::read(r)?;
+                               let mut ret = $constr(len.0 as usize);
+                               for _ in 0..len.0 {
+                                       let k = K::read(r)?;
+                                       let v_opt = V::read(r)?;
+                                       if let Some(v) = v_opt {
+                                               if ret.insert(k, v).is_some() {
+                                                       return Err(DecodeError::InvalidValue);
+                                               }
+                                       }
                                }
+                               Ok(ret)
                        }
                }
-               Ok(ret)
        }
 }
 
+impl_for_map!(BTreeMap, Ord, |_| BTreeMap::new());
+impl_for_map!(HashMap, Hash, |len| HashMap::with_capacity(len));
+
 // HashSet
 impl<T> Writeable for HashSet<T>
 where T: Writeable + Eq + Hash
 {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               (self.len() as u16).write(w)?;
+               CollectionLength(self.len() as u64).write(w)?;
                for item in self.iter() {
                        item.write(w)?;
                }
@@ -644,9 +684,9 @@ where T: Readable + Eq + Hash
 {
        #[inline]
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let len: u16 = Readable::read(r)?;
-               let mut ret = HashSet::with_capacity(len as usize);
-               for _ in 0..len {
+               let len: CollectionLength = Readable::read(r)?;
+               let mut ret = HashSet::with_capacity(cmp::min(len.0 as usize, MAX_BUF_SIZE / core::mem::size_of::<T>()));
+               for _ in 0..len.0 {
                        if !ret.insert(T::read(r)?) {
                                return Err(DecodeError::InvalidValue)
                        }
@@ -656,51 +696,62 @@ where T: Readable + Eq + Hash
 }
 
 // Vectors
-impl Writeable for Vec<u8> {
-       #[inline]
-       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               (self.len() as u16).write(w)?;
-               w.write_all(&self)
-       }
-}
+macro_rules! impl_for_vec {
+       ($ty: ty $(, $name: ident)*) => {
+               impl<$($name : Writeable),*> Writeable for Vec<$ty> {
+                       #[inline]
+                       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+                               CollectionLength(self.len() as u64).write(w)?;
+                               for elem in self.iter() {
+                                       elem.write(w)?;
+                               }
+                               Ok(())
+                       }
+               }
 
-impl Readable for Vec<u8> {
-       #[inline]
-       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let len: u16 = Readable::read(r)?;
-               let mut ret = Vec::with_capacity(len as usize);
-               ret.resize(len as usize, 0);
-               r.read_exact(&mut ret)?;
-               Ok(ret)
+               impl<$($name : Readable),*> Readable for Vec<$ty> {
+                       #[inline]
+                       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+                               let len: CollectionLength = Readable::read(r)?;
+                               let mut ret = Vec::with_capacity(cmp::min(len.0 as usize, MAX_BUF_SIZE / core::mem::size_of::<$ty>()));
+                               for _ in 0..len.0 {
+                                       if let Some(val) = MaybeReadable::read(r)? {
+                                               ret.push(val);
+                                       }
+                               }
+                               Ok(ret)
+                       }
+               }
        }
 }
-impl Writeable for Vec<ecdsa::Signature> {
+
+impl Writeable for Vec<u8> {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               (self.len() as u16).write(w)?;
-               for e in self.iter() {
-                       e.write(w)?;
-               }
-               Ok(())
+               CollectionLength(self.len() as u64).write(w)?;
+               w.write_all(&self)
        }
 }
 
-impl Readable for Vec<ecdsa::Signature> {
+impl Readable for Vec<u8> {
        #[inline]
        fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
-               let len: u16 = Readable::read(r)?;
-               let byte_size = (len as usize)
-                               .checked_mul(COMPACT_SIGNATURE_SIZE)
-                               .ok_or(DecodeError::BadLengthDescriptor)?;
-               if byte_size > MAX_BUF_SIZE {
-                       return Err(DecodeError::BadLengthDescriptor);
+               let mut len: CollectionLength = Readable::read(r)?;
+               let mut ret = Vec::new();
+               while len.0 > 0 {
+                       let readamt = cmp::min(len.0 as usize, MAX_BUF_SIZE);
+                       let readstart = ret.len();
+                       ret.resize(readstart + readamt, 0);
+                       r.read_exact(&mut ret[readstart..])?;
+                       len.0 -= readamt as u64;
                }
-               let mut ret = Vec::with_capacity(len as usize);
-               for _ in 0..len { ret.push(Readable::read(r)?); }
                Ok(ret)
        }
 }
 
+impl_for_vec!(ecdsa::Signature);
+impl_for_vec!((A, B), A, B);
+
 impl Writeable for Script {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                (self.len() as u16).write(w)?;
@@ -1028,7 +1079,7 @@ impl Readable for () {
 impl Writeable for String {
        #[inline]
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
-               (self.len() as u16).write(w)?;
+               CollectionLength(self.len() as u64).write(w)?;
                w.write_all(self.as_bytes())
        }
 }
index 8d74a83a37365fc490ccf089449aad42bcb955ea..afd7fcb2e0fc93b3f536a052c15cec841f8b43b0 100644 (file)
@@ -39,6 +39,9 @@ macro_rules! _encode_tlv {
                        field.write($stream)?;
                }
        };
+       ($stream: expr, $type: expr, $field: expr, ignorable) => {
+               $crate::_encode_tlv!($stream, $type, $field, required);
+       };
        ($stream: expr, $type: expr, $field: expr, (option, encoding: ($fieldty: ty, $encoding: ident))) => {
                $crate::_encode_tlv!($stream, $type, $field.map(|f| $encoding(f)), option);
        };
@@ -155,6 +158,9 @@ macro_rules! _get_varint_length_prefixed_tlv_length {
                        $len.0 += field_len;
                }
        };
+       ($len: expr, $type: expr, $field: expr, ignorable) => {
+               $crate::_get_varint_length_prefixed_tlv_length!($len, $type, $field, required);
+       };
 }
 
 /// See the documentation of [`write_tlv_fields`].
@@ -581,6 +587,9 @@ macro_rules! _init_tlv_based_struct_field {
        ($field: ident, option) => {
                $field
        };
+       ($field: ident, ignorable) => {
+               if $field.is_none() { return Ok(None); } else { $field.unwrap() }
+       };
        ($field: ident, required) => {
                $field.0.unwrap()
        };
@@ -610,6 +619,9 @@ macro_rules! _init_tlv_field_var {
        ($field: ident, option) => {
                let mut $field = None;
        };
+       ($field: ident, ignorable) => {
+               let mut $field = None;
+       };
 }
 
 /// Equivalent to running [`_init_tlv_field_var`] then [`read_tlv_fields`].