Skip to main content

fedimint_core/encoding/
mod.rs

1//! Binary encoding interface suitable for
2//! consensus critical encoding.
3//!
4//! Over time all structs that ! need to be encoded to binary will be migrated
5//! to this interface.
6//!
7//! This code is based on corresponding `rust-bitcoin` types.
8//!
9//! See [`Encodable`] and [`Decodable`] for two main traits.
10
11pub mod as_base64;
12pub mod as_hex;
13mod bls12_381;
14pub mod btc;
15mod collections;
16mod iroh;
17mod secp256k1;
18mod threshold_crypto;
19
20use std::borrow::Cow;
21use std::cmp;
22use std::fmt::{Debug, Display};
23use std::io::{self, Error, Read, Write};
24use std::time::{Duration, SystemTime, UNIX_EPOCH};
25
26use bitcoin::hashes::sha256;
27pub use fedimint_derive::{Decodable, Encodable};
28use hex::{FromHex, ToHex};
29use lightning::util::ser::BigSize;
30use serde::{Deserialize, Serialize};
31use thiserror::Error;
32
33use crate::core::ModuleInstanceId;
34use crate::module::registry::ModuleDecoderRegistry;
35use crate::util::SafeUrl;
36
37/// A writer counting number of bytes written to it
38///
39/// Copy&pasted from <https://github.com/SOF3/count-write> which
40/// uses Apache license (and it's a trivial amount of code, repeating
41/// on stack overflow).
42pub struct CountWrite<W> {
43    inner: W,
44    count: u64,
45}
46
47impl<W> CountWrite<W> {
48    /// Returns the number of bytes successfully written so far
49    pub fn count(&self) -> u64 {
50        self.count
51    }
52}
53
54impl<W> From<W> for CountWrite<W> {
55    fn from(inner: W) -> Self {
56        Self { inner, count: 0 }
57    }
58}
59
60impl<W: Write> io::Write for CountWrite<W> {
61    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
62        let written = self.inner.write(buf)?;
63        self.count += written as u64;
64        Ok(written)
65    }
66
67    fn flush(&mut self) -> io::Result<()> {
68        self.inner.flush()
69    }
70}
71
72/// Object-safe trait for things that can encode themselves
73///
74/// Like `rust-bitcoin`'s `consensus_encode`, but without generics,
75/// so can be used in `dyn` objects.
76pub trait DynEncodable {
77    fn consensus_encode_dyn(&self, writer: &mut dyn std::io::Write) -> Result<(), std::io::Error>;
78}
79
80impl Encodable for dyn DynEncodable {
81    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
82        self.consensus_encode_dyn(writer)
83    }
84}
85
86impl<T> DynEncodable for T
87where
88    T: Encodable,
89{
90    fn consensus_encode_dyn(
91        &self,
92        mut writer: &mut dyn std::io::Write,
93    ) -> Result<(), std::io::Error> {
94        <Self as Encodable>::consensus_encode(self, &mut writer)
95    }
96}
97
98impl Encodable for Box<dyn DynEncodable> {
99    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
100        (**self).consensus_encode_dyn(writer)
101    }
102}
103
104impl<T> Encodable for &T
105where
106    T: Encodable,
107{
108    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
109        (**self).consensus_encode(writer)
110    }
111}
112
113/// Data which can be encoded in a consensus-consistent way
114pub trait Encodable {
115    /// Encode an object with a well-defined format.
116    /// Returns the number of bytes written on success.
117    ///
118    /// The only errors returned are errors propagated from the writer.
119    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error>;
120
121    /// [`Self::consensus_encode`] to newly allocated `Vec<u8>`
122    fn consensus_encode_to_vec(&self) -> Vec<u8> {
123        let mut bytes = vec![];
124        self.consensus_encode(&mut bytes)
125            .expect("encoding to bytes can't fail for io reasons");
126        bytes
127    }
128
129    /// Encode and convert to hex string representation
130    fn consensus_encode_to_hex(&self) -> String {
131        // TODO: This double allocation offends real Rustaceans. We should
132        // be able to go straight to String, but this use case seems under-served
133        // by hex encoding crates.
134        self.consensus_encode_to_vec().encode_hex()
135    }
136
137    /// Encode without storing the encoding, return the size
138    fn consensus_encode_to_len(&self) -> u64 {
139        let mut writer = CountWrite::from(io::sink());
140        self.consensus_encode(&mut writer)
141            .expect("encoding to bytes can't fail for io reasons");
142
143        writer.count()
144    }
145
146    /// Generate a SHA256 hash of the consensus encoding using the default hash
147    /// engine for `H`.
148    ///
149    /// Can be used to validate all federation members agree on state without
150    /// revealing the object
151    fn consensus_hash<H>(&self) -> H
152    where
153        H: bitcoin::hashes::Hash,
154        H::Engine: std::io::Write,
155    {
156        let mut engine = H::engine();
157        self.consensus_encode(&mut engine)
158            .expect("writing to HashEngine cannot fail");
159        H::from_engine(engine)
160    }
161
162    /// [`Self::consensus_hash`] for [`bitcoin::hashes::sha256::Hash`]
163    fn consensus_hash_sha256(&self) -> sha256::Hash {
164        self.consensus_hash()
165    }
166}
167
168/// Maximum size, in bytes, of data we are allowed to ever decode
169/// for a single value.
170pub const MAX_DECODE_SIZE: usize = 16_000_000;
171
172/// Data which can be encoded in a consensus-consistent way
173pub trait Decodable: Sized {
174    /// Decode `Self` from a size-limited reader.
175    ///
176    /// Like `consensus_decode_partial` but relies on the reader being limited
177    /// in the amount of data it returns, e.g. by being wrapped in
178    /// [`std::io::Take`].
179    ///
180    /// Failing to abide to this requirement might lead to memory exhaustion
181    /// caused by malicious inputs.
182    ///
183    /// Users should default to `consensus_decode_partial`, but when data to be
184    /// decoded is already in a byte vector of a limited size, calling this
185    /// function directly might be marginally faster (due to avoiding extra
186    /// checks).
187    ///
188    /// ### Rules for trait implementations
189    ///
190    /// * Simple types that that have a fixed size (own and member fields),
191    ///   don't have to overwrite this method, or be concern with it, should
192    ///   only impl `consensus_decode_partial`.
193    /// * Types that deserialize based on decoded untrusted length should
194    ///   implement `consensus_decode_partial_from_finite_reader` only:
195    ///   * Default implementation of `consensus_decode_partial` will forward to
196    ///     `consensus_decode_partial_from_finite_reader` with the reader
197    ///     wrapped by `Take`, protecting from readers that keep returning data.
198    ///   * Implementation must make sure to put a cap on things like
199    ///     `Vec::with_capacity` and other allocations to avoid oversized
200    ///     allocations, and rely on the reader being finite and running out of
201    ///     data, and collections reallocating on a legitimately oversized input
202    ///     data, instead of trying to enforce arbitrary length limits.
203    /// * Types that contain other types that might be require limited reader
204    ///   (thus implementing `consensus_decode_partial_from_finite_reader`),
205    ///   should also implement it applying same rules, and in addition make
206    ///   sure to call `consensus_decode_partial_from_finite_reader` on all
207    ///   members, to avoid creating redundant `Take` wrappers
208    ///   (`Take<Take<...>>`). Failure to do so might result only in a tiny
209    ///   performance hit.
210    #[inline]
211    fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
212        r: &mut R,
213        modules: &ModuleDecoderRegistry,
214    ) -> Result<Self, DecodeError> {
215        // This method is always strictly less general than, `consensus_decode_partial`,
216        // so it's safe and make sense to default to just calling it. This way
217        // most types, that don't care about protecting against resource
218        // exhaustion due to malicious input, can just ignore it.
219        Self::consensus_decode_partial(r, modules)
220    }
221
222    #[inline]
223    fn consensus_decode_whole(
224        slice: &[u8],
225        modules: &ModuleDecoderRegistry,
226    ) -> Result<Self, DecodeError> {
227        let total_len = slice.len() as u64;
228
229        let r = &mut &slice[..];
230        let mut r = Read::take(r, total_len);
231
232        // This method is always strictly less general than, `consensus_decode_partial`,
233        // so it's safe and make sense to default to just calling it. This way
234        // most types, that don't care about protecting against resource
235        // exhaustion due to malicious input, can just ignore it.
236        let res = Self::consensus_decode_partial_from_finite_reader(&mut r, modules)?;
237        let left = r.limit();
238
239        if left != 0 {
240            return Err(fedimint_core::encoding::DecodeError::custom(format!(
241                "Type did not consume all bytes during decoding; expected={total_len}; \
242                 left={left}; type={}",
243                std::any::type_name::<Self>(),
244            )));
245        }
246        Ok(res)
247    }
248    /// Decode an object with a well-defined format.
249    ///
250    /// This is the method that should be implemented for a typical, fixed sized
251    /// type implementing this trait. Default implementation is wrapping the
252    /// reader in [`std::io::Take`] to limit the input size to
253    /// [`MAX_DECODE_SIZE`], and forwards the call to
254    /// [`Self::consensus_decode_partial_from_finite_reader`], which is
255    /// convenient for types that override
256    /// [`Self::consensus_decode_partial_from_finite_reader`] instead.
257    #[inline]
258    fn consensus_decode_partial<R: std::io::Read>(
259        r: &mut R,
260        modules: &ModuleDecoderRegistry,
261    ) -> Result<Self, DecodeError> {
262        Self::consensus_decode_partial_from_finite_reader(
263            &mut r.take(MAX_DECODE_SIZE as u64),
264            modules,
265        )
266    }
267
268    /// Decode an object from hex
269    fn consensus_decode_hex(
270        hex: &str,
271        modules: &ModuleDecoderRegistry,
272    ) -> Result<Self, DecodeError> {
273        let bytes = Vec::<u8>::from_hex(hex).map_err(DecodeError::from_err)?;
274        Decodable::consensus_decode_whole(&bytes, modules)
275    }
276}
277
278/// Encodes a timestamp as the legacy `(seconds, nanoseconds)` `SystemTime`
279/// representation.
280///
281/// Existing encoded types use this only to preserve their stored
282/// representation. It panics when `time` precedes the Unix epoch.
283pub fn encode_legacy_system_time<W: std::io::Write>(
284    time: &SystemTime,
285    writer: &mut W,
286) -> Result<(), std::io::Error> {
287    let duration = time
288        .duration_since(UNIX_EPOCH)
289        .expect("timestamps before the Unix epoch are unsupported");
290    duration.consensus_encode_dyn(writer)
291}
292
293/// Decodes a timestamp from the legacy `(seconds, nanoseconds)` representation.
294///
295/// Existing encoded types use this only to preserve their stored
296/// representation.
297pub fn decode_legacy_system_time_from_finite_reader<D: std::io::Read>(
298    decoder: &mut D,
299    modules: &ModuleDecoderRegistry,
300) -> Result<SystemTime, DecodeError> {
301    let duration = Duration::consensus_decode_partial_from_finite_reader(decoder, modules)?;
302    // `UNIX_EPOCH + duration` panics ("overflow when adding duration to instant")
303    // instead of erroring when `duration` is past what `SystemTime` can hold. A
304    // decoder must not abort the process on arbitrary bytes, so use the checked
305    // form. Anything that round-trips today still round-trips.
306    UNIX_EPOCH
307        .checked_add(duration)
308        .ok_or_else(|| DecodeError::from_str("SystemTime overflow: duration too large"))
309}
310
311/// Encodes an optional timestamp in the legacy `SystemTime` representation.
312///
313/// Existing encoded types use this only to preserve their stored
314/// representation.
315pub fn encode_legacy_option_system_time<W: std::io::Write>(
316    time: &Option<SystemTime>,
317    writer: &mut W,
318) -> Result<(), std::io::Error> {
319    match time {
320        Some(time) => {
321            1u8.consensus_encode(writer)?;
322            encode_legacy_system_time(time, writer)
323        }
324        None => 0u8.consensus_encode(writer),
325    }
326}
327
328/// Decodes an optional timestamp in the legacy `SystemTime` representation.
329///
330/// Existing encoded types use this only to preserve their stored
331/// representation.
332pub fn decode_legacy_option_system_time_from_finite_reader<D: std::io::Read>(
333    decoder: &mut D,
334    modules: &ModuleDecoderRegistry,
335) -> Result<Option<SystemTime>, DecodeError> {
336    match u8::consensus_decode_partial_from_finite_reader(decoder, modules)? {
337        0 => Ok(None),
338        1 => Ok(Some(decode_legacy_system_time_from_finite_reader(
339            decoder, modules,
340        )?)),
341        _ => Err(DecodeError::from_str(
342            "Invalid flag for option enum, expected 0 or 1",
343        )),
344    }
345}
346
347/// Decodes a field from a finite reader and annotates errors with its schema
348/// context.
349pub fn decode_field_from_finite_reader<T: Decodable, D: std::io::Read>(
350    decoder: &mut D,
351    modules: &ModuleDecoderRegistry,
352    context: &'static str,
353) -> Result<T, DecodeError> {
354    with_decoding_context(
355        T::consensus_decode_partial_from_finite_reader(decoder, modules),
356        context,
357    )
358}
359
360/// Adds schema context to an error returned while decoding a field.
361pub fn with_decoding_context<T>(
362    result: Result<T, DecodeError>,
363    context: &'static str,
364) -> Result<T, DecodeError> {
365    result.context(context)
366}
367
368/// Failure to consensus-decode a value.
369///
370/// `Display` prints only the outermost layer: the context of the decoding step
371/// that failed, or the leaf cause when there is no context. The full chain is
372/// reachable through [`std::error::Error::source`] and printed flat by
373/// [`FmtCompact::fmt_compact`](crate::util::FmtCompact).
374#[derive(Debug, Error)]
375#[non_exhaustive]
376pub enum DecodeError {
377    /// The reader failed or ran out of input.
378    #[error(transparent)]
379    Io(#[from] std::io::Error),
380    /// The input names an enum variant the type does not have.
381    #[error("Invalid enum variant {variant} while decoding {type_name}")]
382    InvalidVariant {
383        /// The variant index found in the input.
384        variant: u64,
385        /// The name of the type being decoded.
386        type_name: &'static str,
387    },
388    /// A decoding step failed; `context` names the step and `source` says why.
389    #[error("{context}")]
390    Context {
391        /// The decoding step that failed.
392        context: String,
393        /// Why it failed.
394        #[source]
395        source: Box<Self>,
396    },
397    /// The input decoded but is not a valid value of the type; the source
398    /// says why.
399    #[error(transparent)]
400    Invalid(Box<dyn std::error::Error + Send + Sync>),
401    /// The input is malformed in a way only a message describes.
402    #[error("{message}")]
403    Custom {
404        /// What is wrong with the input.
405        message: String,
406    },
407}
408
409impl DecodeError {
410    /// A decode failure described by a message.
411    pub fn custom(message: impl Into<String>) -> Self {
412        Self::Custom {
413            message: message.into(),
414        }
415    }
416
417    /// A decode failure described by a static message.
418    // TODO: think about better name
419    #[allow(clippy::should_implement_trait)]
420    pub fn from_str(s: &'static str) -> Self {
421        Self::custom(s)
422    }
423
424    /// A decode failure caused by a typed error, shown as that error.
425    pub fn from_err<E>(e: E) -> Self
426    where
427        E: std::error::Error + Send + Sync + 'static,
428    {
429        Self::Invalid(Box::new(e))
430    }
431
432    /// Wraps this error in the context of the decoding step that failed.
433    pub fn context(self, context: impl Display) -> Self {
434        Self::Context {
435            context: context.to_string(),
436            source: Box::new(self),
437        }
438    }
439}
440
441/// Adds the context of a decoding step to a failed result.
442///
443/// Implemented for every `Result` whose error converts into [`DecodeError`],
444/// so it applies to results of `Decodable` calls and to `std::io::Result`.
445/// Its method names match `anyhow::Context`'s; where both traits are in scope,
446/// call it as `DecodeContext::context(result, ..)` to avoid the clash.
447pub trait DecodeContext<T> {
448    /// Wraps the error in `context`.
449    fn context<C>(self, context: C) -> Result<T, DecodeError>
450    where
451        C: Display;
452
453    /// Wraps the error in the context produced by `context`, which only runs
454    /// on failure.
455    fn with_context<C, F>(self, context: F) -> Result<T, DecodeError>
456    where
457        C: Display,
458        F: FnOnce() -> C;
459}
460
461impl<T, E> DecodeContext<T> for Result<T, E>
462where
463    E: Into<DecodeError>,
464{
465    fn context<C>(self, context: C) -> Result<T, DecodeError>
466    where
467        C: Display,
468    {
469        self.map_err(|error| error.into().context(context))
470    }
471
472    fn with_context<C, F>(self, context: F) -> Result<T, DecodeError>
473    where
474        C: Display,
475        F: FnOnce() -> C,
476    {
477        self.map_err(|error| error.into().context(context()))
478    }
479}
480
481impl Encodable for SafeUrl {
482    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
483        self.to_string().consensus_encode(writer)
484    }
485}
486
487impl Decodable for SafeUrl {
488    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
489        d: &mut D,
490        modules: &ModuleDecoderRegistry,
491    ) -> Result<Self, DecodeError> {
492        String::consensus_decode_partial_from_finite_reader(d, modules)?
493            .parse::<Self>()
494            .map_err(DecodeError::from_err)
495    }
496}
497
498macro_rules! impl_encode_decode_num_as_plain {
499    ($num_type:ty) => {
500        impl Encodable for $num_type {
501            fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
502                let bytes = self.to_be_bytes();
503                writer.write_all(&bytes[..])?;
504                Ok(())
505            }
506        }
507
508        impl Decodable for $num_type {
509            fn consensus_decode_partial<D: std::io::Read>(
510                d: &mut D,
511                _modules: &ModuleDecoderRegistry,
512            ) -> Result<Self, crate::encoding::DecodeError> {
513                let mut bytes = [0u8; (<$num_type>::BITS / 8) as usize];
514                d.read_exact(&mut bytes)?;
515                Ok(<$num_type>::from_be_bytes(bytes))
516            }
517        }
518    };
519}
520
521macro_rules! impl_encode_decode_num_as_bigsize {
522    ($num_type:ty) => {
523        impl Encodable for $num_type {
524            fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
525                BigSize(u64::from(*self)).consensus_encode(writer)
526            }
527        }
528
529        impl Decodable for $num_type {
530            fn consensus_decode_partial<D: std::io::Read>(
531                d: &mut D,
532                _modules: &ModuleDecoderRegistry,
533            ) -> Result<Self, crate::encoding::DecodeError> {
534                let varint = BigSize::consensus_decode_partial(d, &Default::default())?;
535                <$num_type>::try_from(varint.0).map_err(crate::encoding::DecodeError::from_err)
536            }
537        }
538    };
539}
540
541impl_encode_decode_num_as_bigsize!(u64);
542impl_encode_decode_num_as_bigsize!(u32);
543impl_encode_decode_num_as_bigsize!(u16);
544impl_encode_decode_num_as_plain!(u8);
545
546macro_rules! impl_encode_decode_tuple {
547    ($($x:ident),*) => (
548        #[allow(non_snake_case)]
549        impl <$($x: Encodable),*> Encodable for ($($x),*) {
550            fn consensus_encode<W: std::io::Write>(&self, s: &mut W) -> Result<(), std::io::Error> {
551                let &($(ref $x),*) = self;
552                $($x.consensus_encode(s)?;)*
553                Ok(())
554            }
555        }
556
557        #[allow(non_snake_case)]
558        impl<$($x: Decodable),*> Decodable for ($($x),*) {
559            fn consensus_decode_partial<D: std::io::Read>(d: &mut D, modules: &ModuleDecoderRegistry) -> Result<Self, DecodeError> {
560                Ok(($({let $x = Decodable::consensus_decode_partial(d, modules)?; $x }),*))
561            }
562        }
563    );
564}
565
566impl_encode_decode_tuple!(T1, T2);
567impl_encode_decode_tuple!(T1, T2, T3);
568impl_encode_decode_tuple!(T1, T2, T3, T4);
569impl_encode_decode_tuple!(T1, T2, T3, T4, T5);
570impl_encode_decode_tuple!(T1, T2, T3, T4, T5, T6);
571
572impl<T> Encodable for Option<T>
573where
574    T: Encodable,
575{
576    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
577        if let Some(inner) = self {
578            1u8.consensus_encode(writer)?;
579            inner.consensus_encode(writer)?;
580        } else {
581            0u8.consensus_encode(writer)?;
582        }
583        Ok(())
584    }
585}
586
587impl<T> Decodable for Option<T>
588where
589    T: Decodable,
590{
591    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
592        d: &mut D,
593        modules: &ModuleDecoderRegistry,
594    ) -> Result<Self, DecodeError> {
595        let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
596        match flag {
597            0 => Ok(None),
598            1 => Ok(Some(T::consensus_decode_partial_from_finite_reader(
599                d, modules,
600            )?)),
601            _ => Err(DecodeError::from_str(
602                "Invalid flag for option enum, expected 0 or 1",
603            )),
604        }
605    }
606}
607
608impl<T, E> Encodable for Result<T, E>
609where
610    T: Encodable,
611    E: Encodable,
612{
613    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
614        match self {
615            Ok(value) => {
616                1u8.consensus_encode(writer)?;
617                value.consensus_encode(writer)?;
618            }
619            Err(error) => {
620                0u8.consensus_encode(writer)?;
621                error.consensus_encode(writer)?;
622            }
623        }
624
625        Ok(())
626    }
627}
628
629impl<T, E> Decodable for Result<T, E>
630where
631    T: Decodable,
632    E: Decodable,
633{
634    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
635        d: &mut D,
636        modules: &ModuleDecoderRegistry,
637    ) -> Result<Self, DecodeError> {
638        let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
639        match flag {
640            0 => Ok(Err(E::consensus_decode_partial_from_finite_reader(
641                d, modules,
642            )?)),
643            1 => Ok(Ok(T::consensus_decode_partial_from_finite_reader(
644                d, modules,
645            )?)),
646            _ => Err(DecodeError::from_str(
647                "Invalid flag for option enum, expected 0 or 1",
648            )),
649        }
650    }
651}
652
653impl<T> Encodable for Box<T>
654where
655    T: Encodable,
656{
657    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
658        self.as_ref().consensus_encode(writer)
659    }
660}
661
662impl<T> Decodable for Box<T>
663where
664    T: Decodable,
665{
666    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
667        d: &mut D,
668        modules: &ModuleDecoderRegistry,
669    ) -> Result<Self, DecodeError> {
670        Ok(Self::new(T::consensus_decode_partial_from_finite_reader(
671            d, modules,
672        )?))
673    }
674}
675
676impl Encodable for () {
677    fn consensus_encode<W: std::io::Write>(&self, _writer: &mut W) -> Result<(), std::io::Error> {
678        Ok(())
679    }
680}
681
682impl Decodable for () {
683    fn consensus_decode_partial<D: std::io::Read>(
684        _d: &mut D,
685        _modules: &ModuleDecoderRegistry,
686    ) -> Result<Self, DecodeError> {
687        Ok(())
688    }
689}
690
691impl Encodable for &str {
692    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
693        self.as_bytes().consensus_encode(writer)
694    }
695}
696
697impl Encodable for String {
698    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
699        self.as_bytes().consensus_encode(writer)
700    }
701}
702
703impl Decodable for String {
704    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
705        d: &mut D,
706        modules: &ModuleDecoderRegistry,
707    ) -> Result<Self, DecodeError> {
708        Self::from_utf8(Decodable::consensus_decode_partial_from_finite_reader(
709            d, modules,
710        )?)
711        .map_err(DecodeError::from_err)
712    }
713}
714
715impl Encodable for Duration {
716    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
717        self.as_secs().consensus_encode(writer)?;
718        self.subsec_nanos().consensus_encode(writer)?;
719
720        Ok(())
721    }
722}
723
724impl Decodable for Duration {
725    fn consensus_decode_partial<D: std::io::Read>(
726        d: &mut D,
727        modules: &ModuleDecoderRegistry,
728    ) -> Result<Self, DecodeError> {
729        let secs = Decodable::consensus_decode_partial(d, modules)?;
730        let nsecs = Decodable::consensus_decode_partial(d, modules)?;
731        // The encoder writes `subsec_nanos()`, which is always below one billion,
732        // so a larger `nsecs` is never something we wrote. Accepting it makes the
733        // encoding non-canonical and lets `Duration::new` panic when the carried
734        // second overflows `secs`.
735        if 1_000_000_000 <= nsecs {
736            return Err(DecodeError::from_str("Duration nanoseconds out of range"));
737        }
738        Ok(Self::new(secs, nsecs))
739    }
740}
741
742impl Encodable for bool {
743    fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
744        let bool_as_u8 = u8::from(*self);
745        writer.write_all(&[bool_as_u8])?;
746        Ok(())
747    }
748}
749
750impl Decodable for bool {
751    fn consensus_decode_partial<D: Read>(
752        d: &mut D,
753        _modules: &ModuleDecoderRegistry,
754    ) -> Result<Self, DecodeError> {
755        let mut bool_as_u8 = [0u8];
756        d.read_exact(&mut bool_as_u8)?;
757        match bool_as_u8[0] {
758            0 => Ok(false),
759            1 => Ok(true),
760            _ => Err(DecodeError::from_str("Out of range, expected 0 or 1")),
761        }
762    }
763}
764
765impl Encodable for Cow<'static, str> {
766    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
767        self.as_ref().consensus_encode(writer)
768    }
769}
770
771impl Decodable for Cow<'static, str> {
772    fn consensus_decode_partial<D: std::io::Read>(
773        d: &mut D,
774        modules: &ModuleDecoderRegistry,
775    ) -> Result<Self, DecodeError> {
776        Ok(Cow::Owned(String::consensus_decode_partial(d, modules)?))
777    }
778}
779
780/// A type that decodes `module_instance_id`-prefixed `T`s even
781/// when corresponding `Decoder` is not available.
782///
783/// All dyn-module types are encoded as:
784///
785/// ```norust
786/// module_instance_id | len_u64 | data
787/// ```
788///
789/// So clients that don't have a corresponding module, can read
790/// the `len_u64` and skip the amount of data specified in it.
791///
792/// This type makes it more convenient. It's possible to attempt
793/// to retry decoding after more modules become available by using
794/// [`DynRawFallback::redecode_raw`].
795///
796/// Notably this struct does not ignore any errors. It only skips
797/// decoding when the module decoder is not available.
798#[derive(Debug, Clone, Serialize, Deserialize)]
799pub enum DynRawFallback<T> {
800    Raw {
801        module_instance_id: ModuleInstanceId,
802        #[serde(with = "::fedimint_core::encoding::as_hex")]
803        raw: Vec<u8>,
804    },
805    Decoded(T),
806}
807
808impl<T> cmp::PartialEq for DynRawFallback<T>
809where
810    T: cmp::PartialEq + Encodable,
811{
812    fn eq(&self, other: &Self) -> bool {
813        match (self, other) {
814            (
815                Self::Raw {
816                    module_instance_id: mid_self,
817                    raw: raw_self,
818                },
819                Self::Raw {
820                    module_instance_id: mid_other,
821                    raw: raw_other,
822                },
823            ) => mid_self.eq(mid_other) && raw_self.eq(raw_other),
824            (r @ Self::Raw { .. }, d @ Self::Decoded(_))
825            | (d @ Self::Decoded(_), r @ Self::Raw { .. }) => {
826                r.consensus_encode_to_vec() == d.consensus_encode_to_vec()
827            }
828            (Self::Decoded(s), Self::Decoded(o)) => s == o,
829        }
830    }
831}
832
833impl<T> cmp::Eq for DynRawFallback<T> where T: cmp::Eq + Encodable {}
834
835impl<T> DynRawFallback<T>
836where
837    T: Decodable + 'static,
838{
839    /// Get the decoded `T` or `None` if not decoded yet
840    pub fn decoded(self) -> Option<T> {
841        match self {
842            Self::Raw { .. } => None,
843            Self::Decoded(v) => Some(v),
844        }
845    }
846
847    /// Convert into the decoded `T` and panic if not decoded yet
848    pub fn expect_decoded(self) -> T {
849        match self {
850            Self::Raw { .. } => {
851                panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
852            }
853            Self::Decoded(v) => v,
854        }
855    }
856
857    /// Get the decoded `T` and panic if not decoded yet
858    pub fn expect_decoded_ref(&self) -> &T {
859        match self {
860            Self::Raw { .. } => {
861                panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
862            }
863            Self::Decoded(v) => v,
864        }
865    }
866
867    /// Attempt to re-decode raw values with new set of of `modules`
868    ///
869    /// In certain contexts it might be necessary to try again with
870    /// a new set of modules.
871    pub fn redecode_raw(
872        self,
873        decoders: &ModuleDecoderRegistry,
874    ) -> Result<Self, crate::encoding::DecodeError> {
875        Ok(match self {
876            Self::Raw {
877                module_instance_id,
878                raw,
879            } => match decoders.get(module_instance_id) {
880                Some(decoder) => Self::Decoded(decoder.decode_complete(
881                    &mut &raw[..],
882                    raw.len() as u64,
883                    module_instance_id,
884                    decoders,
885                )?),
886                None => Self::Raw {
887                    module_instance_id,
888                    raw,
889                },
890            },
891            Self::Decoded(v) => Self::Decoded(v),
892        })
893    }
894}
895
896impl<T> From<T> for DynRawFallback<T> {
897    fn from(value: T) -> Self {
898        Self::Decoded(value)
899    }
900}
901
902impl<T> Decodable for DynRawFallback<T>
903where
904    T: Decodable + 'static,
905{
906    fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
907        reader: &mut R,
908        decoders: &ModuleDecoderRegistry,
909    ) -> Result<Self, crate::encoding::DecodeError> {
910        let module_instance_id =
911            fedimint_core::core::ModuleInstanceId::consensus_decode_partial_from_finite_reader(
912                reader, decoders,
913            )?;
914        Ok(match decoders.get(module_instance_id) {
915            Some(decoder) => {
916                let total_len_u64 =
917                    u64::consensus_decode_partial_from_finite_reader(reader, decoders)?;
918                Self::Decoded(decoder.decode_complete(
919                    reader,
920                    total_len_u64,
921                    module_instance_id,
922                    decoders,
923                )?)
924            }
925            None => {
926                // since the decoder is not available, just read the raw data
927                Self::Raw {
928                    module_instance_id,
929                    raw: Vec::consensus_decode_partial_from_finite_reader(reader, decoders)?,
930                }
931            }
932        })
933    }
934}
935
936impl<T> Encodable for DynRawFallback<T>
937where
938    T: Encodable,
939{
940    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
941        match self {
942            Self::Raw {
943                module_instance_id,
944                raw,
945            } => {
946                module_instance_id.consensus_encode(writer)?;
947                raw.consensus_encode(writer)?;
948                Ok(())
949            }
950            Self::Decoded(v) => v.consensus_encode(writer),
951        }
952    }
953}
954
955#[cfg(test)]
956mod tests {
957    use std::fmt::Debug;
958    use std::io::Cursor;
959
960    use super::*;
961    use crate::encoding::{Decodable, Encodable};
962    use crate::module::registry::ModuleRegistry;
963    use crate::util::FmtCompact as _;
964
965    pub(crate) fn test_roundtrip<T>(value: &T)
966    where
967        T: Encodable + Decodable + Eq + Debug,
968    {
969        let mut bytes = Vec::new();
970        value.consensus_encode(&mut bytes).unwrap();
971
972        let mut cursor = Cursor::new(bytes);
973        let decoded =
974            T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
975        assert_eq!(value, &decoded);
976    }
977
978    pub(crate) fn test_roundtrip_expected<T>(value: &T, expected: &[u8])
979    where
980        T: Encodable + Decodable + Eq + Debug,
981    {
982        let mut bytes = Vec::new();
983        value.consensus_encode(&mut bytes).unwrap();
984        assert_eq!(&expected, &bytes);
985
986        let mut cursor = Cursor::new(bytes);
987        let decoded =
988            T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
989        assert_eq!(value, &decoded);
990    }
991
992    #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
993    enum NoDefaultEnum {
994        Foo,
995        Bar(u32, String),
996        Baz { baz: u8 },
997    }
998
999    #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
1000    enum DefaultEnum {
1001        Foo,
1002        Bar(u32, String),
1003        #[encodable_default]
1004        Default {
1005            variant: u64,
1006            bytes: Vec<u8>,
1007        },
1008    }
1009
1010    #[test_log::test]
1011    fn test_derive_enum_no_default_roundtrip_success() {
1012        let enums = [
1013            NoDefaultEnum::Foo,
1014            NoDefaultEnum::Bar(
1015                42,
1016                "The answer to life, the universe, and everything".to_string(),
1017            ),
1018            NoDefaultEnum::Baz { baz: 0 },
1019        ];
1020
1021        for e in enums {
1022            test_roundtrip(&e);
1023        }
1024    }
1025
1026    #[test_log::test]
1027    fn test_derive_enum_no_default_decode_fail() {
1028        let unknown_variant = DefaultEnum::Default {
1029            variant: 42,
1030            bytes: vec![0, 1, 2, 3],
1031        };
1032        let mut unknown_variant_encoding = vec![];
1033        unknown_variant
1034            .consensus_encode(&mut unknown_variant_encoding)
1035            .unwrap();
1036
1037        let mut cursor = Cursor::new(&unknown_variant_encoding);
1038        let decode_res =
1039            NoDefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
1040
1041        match decode_res {
1042            Ok(_) => panic!("Should return error"),
1043            Err(e) => assert!(e.to_string().contains("Invalid enum variant")),
1044        }
1045    }
1046
1047    #[test_log::test]
1048    fn test_derive_enum_default_decode_success() {
1049        let unknown_variant = NoDefaultEnum::Baz { baz: 123 };
1050        let mut unknown_variant_encoding = vec![];
1051        unknown_variant
1052            .consensus_encode(&mut unknown_variant_encoding)
1053            .unwrap();
1054
1055        let mut cursor = Cursor::new(&unknown_variant_encoding);
1056        let decode_res =
1057            DefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
1058
1059        assert_eq!(
1060            decode_res.unwrap(),
1061            DefaultEnum::Default {
1062                variant: 2,
1063                bytes: vec![123],
1064            }
1065        );
1066    }
1067
1068    #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
1069    struct TestStruct {
1070        vec: Vec<u8>,
1071        num: u32,
1072    }
1073
1074    #[test_log::test]
1075    fn test_derive_struct() {
1076        let reference = TestStruct {
1077            vec: vec![1, 2, 3],
1078            num: 42,
1079        };
1080        let bytes = [3, 1, 2, 3, 42];
1081
1082        test_roundtrip_expected(&reference, &bytes);
1083    }
1084
1085    #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
1086    struct TestTupleStruct(Vec<u8>, u32);
1087
1088    #[derive(Debug)]
1089    struct TestFiniteReader;
1090
1091    impl Decodable for TestFiniteReader {
1092        fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
1093            reader: &mut R,
1094            modules: &ModuleDecoderRegistry,
1095        ) -> Result<Self, DecodeError> {
1096            let _: Vec<u8> = decode_field_from_finite_reader(
1097                reader,
1098                modules,
1099                "Decoding tuple block TestFiniteReader field field_0",
1100            )?;
1101            let _: Vec<u8> = decode_field_from_finite_reader(
1102                reader,
1103                modules,
1104                "Decoding tuple block TestFiniteReader field field_1",
1105            )?;
1106            Ok(Self)
1107        }
1108    }
1109
1110    #[test_log::test]
1111    fn test_derive_tuple_struct() {
1112        let reference = TestTupleStruct(vec![1, 2, 3], 42);
1113        let bytes = [3, 1, 2, 3, 42];
1114
1115        test_roundtrip_expected(&reference, &bytes);
1116    }
1117
1118    #[test_log::test]
1119    fn test_legacy_system_time_encoding() {
1120        let time = UNIX_EPOCH + Duration::new(42, 100);
1121        let expected = [42, 100];
1122        let mut bytes = vec![];
1123        encode_legacy_system_time(&time, &mut bytes).expect("encoding to a vector cannot fail");
1124        assert_eq!(bytes, expected);
1125
1126        let mut cursor = Cursor::new(expected);
1127        let decoded = decode_legacy_system_time_from_finite_reader(
1128            &mut cursor,
1129            &ModuleDecoderRegistry::default(),
1130        )
1131        .expect("valid encoding");
1132        assert_eq!(decoded, time);
1133    }
1134
1135    #[test_log::test]
1136    fn test_client_backup_snapshot_uses_legacy_system_time_encoding() {
1137        let reference = crate::backup::ClientBackupSnapshot {
1138            timestamp: UNIX_EPOCH + Duration::new(42, 100),
1139            data: vec![1, 2, 3],
1140        };
1141        let expected = [42, 100, 3, 1, 2, 3];
1142
1143        test_roundtrip_expected(&reference, &expected);
1144    }
1145
1146    #[test_log::test]
1147    fn test_legacy_optional_system_time_encoding() {
1148        let time = Some(UNIX_EPOCH + Duration::new(42, 100));
1149        let expected = [1, 42, 100];
1150        let mut bytes = vec![];
1151        encode_legacy_option_system_time(&time, &mut bytes)
1152            .expect("encoding to a vector cannot fail");
1153        assert_eq!(bytes, expected);
1154
1155        let mut cursor = Cursor::new(expected);
1156        let decoded = decode_legacy_option_system_time_from_finite_reader(
1157            &mut cursor,
1158            &ModuleDecoderRegistry::default(),
1159        )
1160        .expect("valid encoding");
1161        assert_eq!(decoded, time);
1162    }
1163
1164    #[test_log::test]
1165    fn test_legacy_optional_system_time_encoding_none() {
1166        let mut bytes = vec![];
1167        encode_legacy_option_system_time(&None, &mut bytes)
1168            .expect("encoding to a vector cannot fail");
1169        assert_eq!(bytes, [0]);
1170
1171        let mut cursor = Cursor::new(bytes);
1172        let decoded = decode_legacy_option_system_time_from_finite_reader(
1173            &mut cursor,
1174            &ModuleDecoderRegistry::default(),
1175        )
1176        .expect("valid encoding");
1177        assert_eq!(decoded, None);
1178    }
1179
1180    #[test_log::test]
1181    fn test_legacy_optional_system_time_encoding_rejects_invalid_flag() {
1182        let error = decode_legacy_option_system_time_from_finite_reader(
1183            &mut Cursor::new([2]),
1184            &ModuleDecoderRegistry::default(),
1185        )
1186        .expect_err("invalid option flag must fail");
1187        assert_eq!(
1188            error.to_string(),
1189            "Invalid flag for option enum, expected 0 or 1"
1190        );
1191    }
1192
1193    #[test_log::test]
1194    fn test_legacy_system_time_field_decode_adds_context() {
1195        let error = with_decoding_context(
1196            decode_legacy_system_time_from_finite_reader(
1197                &mut Cursor::new([42]),
1198                &ModuleDecoderRegistry::default(),
1199            ),
1200            "Decoding named block field: Test{ ... timestamp ... }",
1201        )
1202        .expect_err("truncated timestamp must fail");
1203        assert!(
1204            error
1205                .to_string()
1206                .contains("Decoding named block field: Test{ ... timestamp ... }")
1207        );
1208    }
1209
1210    #[test_log::test]
1211    fn test_legacy_system_time_decode_overflow_is_an_error() {
1212        // secs = u64::MAX is past what `SystemTime` can represent. No encoder we
1213        // ship produces it, but a peer can put any u64 on the wire.
1214        let mut bytes = Vec::new();
1215        u64::MAX.consensus_encode(&mut bytes).unwrap();
1216        0u32.consensus_encode(&mut bytes).unwrap();
1217        decode_legacy_system_time_from_finite_reader(
1218            &mut Cursor::new(bytes),
1219            &ModuleDecoderRegistry::default(),
1220        )
1221        .expect_err("an unrepresentable timestamp must be a decode error, not a panic");
1222    }
1223
1224    #[test_log::test]
1225    fn test_duration_decode_rejects_out_of_range_nsecs() {
1226        // secs = u64::MAX with nsecs = 1e9 carries a second and overflows `secs`
1227        // inside `Duration::new`; it must be rejected instead.
1228        let reg = ModuleDecoderRegistry::default();
1229        let mut bad = Vec::new();
1230        u64::MAX.consensus_encode(&mut bad).unwrap();
1231        1_000_000_000u32.consensus_encode(&mut bad).unwrap();
1232        Duration::consensus_decode_partial(&mut Cursor::new(bad), &reg)
1233            .expect_err("nsecs of one billion must be rejected");
1234
1235        // The largest canonical nsecs must still decode.
1236        let mut ok = Vec::new();
1237        u64::MAX.consensus_encode(&mut ok).unwrap();
1238        999_999_999u32.consensus_encode(&mut ok).unwrap();
1239        Duration::consensus_decode_partial(&mut Cursor::new(ok), &reg)
1240            .expect("the largest canonical nsecs must decode");
1241    }
1242
1243    #[test_log::test]
1244    fn test_finite_reader_shares_decode_limit_between_fields() {
1245        let field = vec![0u8; MAX_DECODE_SIZE / 2];
1246        let mut bytes = vec![];
1247        field
1248            .consensus_encode(&mut bytes)
1249            .expect("encoding to a vector cannot fail");
1250        field
1251            .consensus_encode(&mut bytes)
1252            .expect("encoding to a vector cannot fail");
1253
1254        let error = TestFiniteReader::consensus_decode_partial(
1255            &mut Cursor::new(bytes),
1256            &ModuleDecoderRegistry::default(),
1257        )
1258        .expect_err("both fields cannot exceed one decode limit");
1259        assert!(
1260            error
1261                .to_string()
1262                .contains("Decoding tuple block TestFiniteReader field field_1")
1263        );
1264    }
1265
1266    #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
1267    enum TestEnum {
1268        Foo(Option<u64>),
1269        Bar { bazz: Vec<u8> },
1270    }
1271
1272    #[test_log::test]
1273    fn test_derive_enum() {
1274        let test_cases = [
1275            (TestEnum::Foo(Some(42)), vec![0, 2, 1, 42]),
1276            (TestEnum::Foo(None), vec![0, 1, 0]),
1277            (
1278                TestEnum::Bar {
1279                    bazz: vec![1, 2, 3],
1280                },
1281                vec![1, 4, 3, 1, 2, 3],
1282            ),
1283        ];
1284
1285        for (reference, bytes) in test_cases {
1286            test_roundtrip_expected(&reference, &bytes);
1287        }
1288    }
1289
1290    #[test]
1291    fn test_derive_empty_enum_decode() {
1292        #[derive(Debug, Encodable, Decodable)]
1293        enum NotConstructable {}
1294
1295        let vec = vec![42u8];
1296        let mut cursor = Cursor::new(vec);
1297
1298        assert!(
1299            NotConstructable::consensus_decode_partial(
1300                &mut cursor,
1301                &ModuleDecoderRegistry::default()
1302            )
1303            .is_err()
1304        );
1305    }
1306
1307    #[test]
1308    fn test_custom_index_enum() {
1309        #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
1310        enum Old {
1311            Foo,
1312            Bar,
1313            Baz,
1314        }
1315
1316        #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
1317        enum New {
1318            #[encodable(index = 0)]
1319            Foo,
1320            #[encodable(index = 2)]
1321            Baz,
1322            #[encodable_default]
1323            Default { variant: u64, bytes: Vec<u8> },
1324        }
1325
1326        let test_vector = vec![
1327            (Old::Foo, New::Foo),
1328            (
1329                Old::Bar,
1330                New::Default {
1331                    variant: 1,
1332                    bytes: vec![],
1333                },
1334            ),
1335            (Old::Baz, New::Baz),
1336        ];
1337
1338        for (old, new) in test_vector {
1339            let old_bytes = old.consensus_encode_to_vec();
1340            let decoded_new = New::consensus_decode_whole(&old_bytes, &ModuleRegistry::default())
1341                .expect("Decoding failed");
1342            assert_eq!(decoded_new, new);
1343        }
1344    }
1345
1346    fn encode_value<T: Encodable>(value: &T) -> Vec<u8> {
1347        let mut writer = Vec::new();
1348        value.consensus_encode(&mut writer).unwrap();
1349        writer
1350    }
1351
1352    fn decode_value<T: Decodable>(bytes: &[u8]) -> T {
1353        T::consensus_decode_whole(bytes, &ModuleDecoderRegistry::default()).unwrap()
1354    }
1355
1356    fn keeps_ordering_after_serialization<T: Ord + Encodable + Decodable + Debug>(mut vec: Vec<T>) {
1357        vec.sort();
1358        let mut encoded = vec.iter().map(encode_value).collect::<Vec<_>>();
1359        encoded.sort();
1360        let decoded = encoded.iter().map(|v| decode_value(v)).collect::<Vec<_>>();
1361        for (i, (a, b)) in vec.iter().zip(decoded.iter()).enumerate() {
1362            assert_eq!(a, b, "difference at index {i}");
1363        }
1364    }
1365
1366    #[test]
1367    fn test_lexicographical_sorting() {
1368        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1369        struct TestAmount(u64);
1370
1371        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1372        struct TestComplexAmount(u16, u32, u64);
1373
1374        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1375        struct Text(String);
1376
1377        let amounts = (0..20000).map(TestAmount).collect::<Vec<_>>();
1378        keeps_ordering_after_serialization(amounts);
1379
1380        let complex_amounts = (10..20000)
1381            .flat_map(|i| {
1382                (i - 1..=i + 1).flat_map(move |j| {
1383                    (i - 1..=i + 1).map(move |k| TestComplexAmount(i as u16, j as u32, k as u64))
1384                })
1385            })
1386            .collect::<Vec<_>>();
1387        keeps_ordering_after_serialization(complex_amounts);
1388
1389        let texts = (' '..'~')
1390            .flat_map(|i| {
1391                (' '..'~')
1392                    .map(|j| Text(format!("{i}{j}")))
1393                    .collect::<Vec<_>>()
1394            })
1395            .collect::<Vec<_>>();
1396        keeps_ordering_after_serialization(texts);
1397
1398        // bitcoin structures are not lexicographically sortable so we cannot
1399        // test them here. in future we may crate a wrapper type that is
1400        // lexicographically sortable to use when needed
1401    }
1402
1403    #[test]
1404    fn whole_decode_rejects_trailing_bytes_with_a_message() {
1405        let err = u8::consensus_decode_whole(&[1, 2], &ModuleDecoderRegistry::default())
1406            .expect_err("one byte too many");
1407        assert!(matches!(err, DecodeError::Custom { .. }), "{err:?}");
1408        assert!(
1409            err.to_string()
1410                .starts_with("Type did not consume all bytes during decoding"),
1411            "{err}"
1412        );
1413    }
1414
1415    #[test]
1416    fn decode_error_display_is_the_outer_layer_and_the_chain_is_reachable() {
1417        let io = std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "short read");
1418        let err = Err::<(), _>(io)
1419            .context("Decoding field a")
1420            .context("Decoding Foo")
1421            .expect_err("built from an error");
1422
1423        assert_eq!(err.to_string(), "Decoding Foo");
1424        assert_eq!(
1425            err.fmt_compact().to_string(),
1426            "Decoding Foo: Decoding field a: short read"
1427        );
1428
1429        let DecodeError::Context { source: inner, .. } = &err else {
1430            panic!("outer layer is the last context added: {err:?}");
1431        };
1432        let DecodeError::Context { source: leaf, .. } = &**inner else {
1433            panic!("inner layer is the first context added: {inner:?}");
1434        };
1435        assert!(matches!(**leaf, DecodeError::Io(_)), "{leaf:?}");
1436    }
1437
1438    #[test]
1439    fn from_err_shows_the_typed_error_as_is() {
1440        let parse_error = "x".parse::<u8>().expect_err("not a number");
1441        let message = parse_error.to_string();
1442
1443        let err = DecodeError::from_err(parse_error);
1444
1445        assert!(matches!(err, DecodeError::Invalid(_)), "{err:?}");
1446        assert_eq!(err.to_string(), message);
1447        assert_eq!(err.fmt_compact().to_string(), message);
1448    }
1449
1450    #[test]
1451    fn custom_and_from_str_carry_only_a_message() {
1452        let err = DecodeError::custom(format!("Unknown network magic: {:x}", 0xd9b4_bef9_u32));
1453        assert!(matches!(err, DecodeError::Custom { .. }), "{err:?}");
1454        assert_eq!(err.to_string(), "Unknown network magic: d9b4bef9");
1455        assert!(std::error::Error::source(&err).is_none());
1456
1457        let err = DecodeError::from_str("Out of range, expected 0 or 1");
1458        assert_eq!(err.to_string(), "Out of range, expected 0 or 1");
1459    }
1460
1461    #[test]
1462    fn with_context_only_formats_on_error() {
1463        let ok: Result<u8, DecodeError> = Ok(1);
1464        let formatted = ok
1465            .with_context(|| panic!("must not be called on Ok"))
1466            .expect("still Ok");
1467        assert_eq!(formatted, 1);
1468
1469        let err = Err::<u8, _>(DecodeError::from_str("bad"))
1470            .with_context(|| format!("Decoding item {}", 3))
1471            .expect_err("still Err");
1472        assert_eq!(err.fmt_compact().to_string(), "Decoding item 3: bad");
1473    }
1474
1475    #[test]
1476    fn truncated_input_is_an_io_error() {
1477        let err = u8::consensus_decode_whole(&[], &ModuleDecoderRegistry::default())
1478            .expect_err("empty input is not a u8");
1479        assert!(matches!(err, DecodeError::Io(_)), "{err:?}");
1480    }
1481
1482    #[test]
1483    fn truncated_integer_is_an_io_error() {
1484        let err = u32::consensus_decode_whole(&[], &ModuleDecoderRegistry::default())
1485            .expect_err("zero bytes are not a u32");
1486        assert!(matches!(err, DecodeError::Io(_)), "{err:?}");
1487    }
1488
1489    #[test]
1490    fn truncated_transaction_id_is_an_io_error() {
1491        let err = crate::TransactionId::consensus_decode_whole(
1492            &[0u8; 31],
1493            &ModuleDecoderRegistry::default(),
1494        )
1495        .expect_err("31 bytes are not a transaction id");
1496        assert!(matches!(err, DecodeError::Io(_)), "{err:?}");
1497    }
1498
1499    #[test]
1500    fn derived_decode_names_the_failing_field_and_keeps_the_io_root() {
1501        // `vec` decodes as one element (7); `num` then has no bytes left.
1502        let err = TestStruct::consensus_decode_whole(&[1, 7], &ModuleDecoderRegistry::default())
1503            .expect_err("payload is truncated");
1504
1505        let DecodeError::Context { context, source } = &err else {
1506            panic!("the failing field's context is the outer layer: {err:?}");
1507        };
1508        assert_eq!(
1509            context,
1510            "Decoding named block field: TestStruct{ ... num ... }"
1511        );
1512        assert!(matches!(**source, DecodeError::Io(_)), "{source:?}");
1513    }
1514
1515    #[test]
1516    fn derived_decode_reports_unknown_variants_structurally() {
1517        let unknown_variant = DefaultEnum::Default {
1518            variant: 42,
1519            bytes: vec![0, 1, 2, 3],
1520        };
1521        let mut encoding = vec![];
1522        unknown_variant
1523            .consensus_encode(&mut encoding)
1524            .expect("encodes");
1525
1526        let err = NoDefaultEnum::consensus_decode_whole(&encoding, &ModuleRegistry::default())
1527            .expect_err("variant 42 does not exist");
1528        assert!(
1529            matches!(
1530                err,
1531                DecodeError::InvalidVariant {
1532                    variant: 42,
1533                    type_name: "NoDefaultEnum"
1534                }
1535            ),
1536            "{err:?}"
1537        );
1538    }
1539}