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 secp256k1;
17mod threshold_crypto;
18
19#[cfg(all(feature = "iroh", not(target_family = "wasm")))]
20mod iroh;
21#[cfg(not(target_family = "wasm"))]
22mod tls;
23
24use std::borrow::Cow;
25use std::fmt::{Debug, Formatter};
26use std::io::{self, Error, Read, Write};
27use std::time::{Duration, SystemTime, UNIX_EPOCH};
28
29use anyhow::Context;
30use bitcoin::hashes::sha256;
31pub use fedimint_derive::{Decodable, Encodable};
32use hex::{FromHex, ToHex};
33use lightning::util::ser::BigSize;
34use serde::{Deserialize, Serialize};
35use thiserror::Error;
36
37use crate::core::ModuleInstanceId;
38use crate::module::registry::ModuleDecoderRegistry;
39use crate::util::SafeUrl;
40
41/// Object-safe trait for things that can encode themselves
42///
43/// Like `rust-bitcoin`'s `consensus_encode`, but without generics,
44/// so can be used in `dyn` objects.
45pub trait DynEncodable {
46    fn consensus_encode_dyn(
47        &self,
48        writer: &mut dyn std::io::Write,
49    ) -> Result<usize, std::io::Error>;
50}
51
52impl Encodable for dyn DynEncodable {
53    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
54        self.consensus_encode_dyn(writer)
55    }
56}
57
58impl<T> DynEncodable for T
59where
60    T: Encodable,
61{
62    fn consensus_encode_dyn(
63        &self,
64        mut writer: &mut dyn std::io::Write,
65    ) -> Result<usize, std::io::Error> {
66        <Self as Encodable>::consensus_encode(self, &mut writer)
67    }
68}
69
70impl Encodable for Box<dyn DynEncodable> {
71    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
72        (**self).consensus_encode_dyn(writer)
73    }
74}
75
76impl<T> Encodable for &T
77where
78    T: Encodable,
79{
80    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
81        (**self).consensus_encode(writer)
82    }
83}
84
85/// Data which can be encoded in a consensus-consistent way
86pub trait Encodable {
87    /// Encode an object with a well-defined format.
88    /// Returns the number of bytes written on success.
89    ///
90    /// The only errors returned are errors propagated from the writer.
91    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error>;
92
93    /// [`Self::consensus_encode`] to newly allocated `Vec<u8>`
94    fn consensus_encode_to_vec(&self) -> Vec<u8> {
95        let mut bytes = vec![];
96        self.consensus_encode(&mut bytes)
97            .expect("encoding to bytes can't fail for io reasons");
98        bytes
99    }
100
101    /// Encode and convert to hex string representation
102    fn consensus_encode_to_hex(&self) -> String {
103        // TODO: This double allocation offends real Rustaceans. We should
104        // be able to go straight to String, but this use case seems under-served
105        // by hex encoding crates.
106        self.consensus_encode_to_vec().encode_hex()
107    }
108
109    /// Encode without storing the encoding, return the size
110    fn consensus_encode_to_len(&self) -> usize {
111        self.consensus_encode(&mut io::sink())
112            .expect("encoding to bytes can't fail for io reasons")
113    }
114
115    /// Generate a SHA256 hash of the consensus encoding using the default hash
116    /// engine for `H`.
117    ///
118    /// Can be used to validate all federation members agree on state without
119    /// revealing the object
120    fn consensus_hash<H>(&self) -> H
121    where
122        H: bitcoin::hashes::Hash,
123        H::Engine: std::io::Write,
124    {
125        let mut engine = H::engine();
126        self.consensus_encode(&mut engine)
127            .expect("writing to HashEngine cannot fail");
128        H::from_engine(engine)
129    }
130
131    /// [`Self::consensus_hash`] for [`bitcoin::hashes::sha256::Hash`]
132    fn consensus_hash_sha256(&self) -> sha256::Hash {
133        self.consensus_hash()
134    }
135}
136
137/// Maximum size, in bytes, of data we are allowed to ever decode
138/// for a single value.
139pub const MAX_DECODE_SIZE: usize = 16_000_000;
140
141/// Data which can be encoded in a consensus-consistent way
142pub trait Decodable: Sized {
143    /// Decode `Self` from a size-limited reader.
144    ///
145    /// Like `consensus_decode_partial` but relies on the reader being limited
146    /// in the amount of data it returns, e.g. by being wrapped in
147    /// [`std::io::Take`].
148    ///
149    /// Failing to abide to this requirement might lead to memory exhaustion
150    /// caused by malicious inputs.
151    ///
152    /// Users should default to `consensus_decode_partial`, but when data to be
153    /// decoded is already in a byte vector of a limited size, calling this
154    /// function directly might be marginally faster (due to avoiding extra
155    /// checks).
156    ///
157    /// ### Rules for trait implementations
158    ///
159    /// * Simple types that that have a fixed size (own and member fields),
160    ///   don't have to overwrite this method, or be concern with it, should
161    ///   only impl `consensus_decode_partial`.
162    /// * Types that deserialize based on decoded untrusted length should
163    ///   implement `consensus_decode_partial_from_finite_reader` only:
164    ///   * Default implementation of `consensus_decode_partial` will forward to
165    ///     `consensus_decode_partial_from_finite_reader` with the reader
166    ///     wrapped by `Take`, protecting from readers that keep returning data.
167    ///   * Implementation must make sure to put a cap on things like
168    ///     `Vec::with_capacity` and other allocations to avoid oversized
169    ///     allocations, and rely on the reader being finite and running out of
170    ///     data, and collections reallocating on a legitimately oversized input
171    ///     data, instead of trying to enforce arbitrary length limits.
172    /// * Types that contain other types that might be require limited reader
173    ///   (thus implementing `consensus_decode_partial_from_finite_reader`),
174    ///   should also implement it applying same rules, and in addition make
175    ///   sure to call `consensus_decode_partial_from_finite_reader` on all
176    ///   members, to avoid creating redundant `Take` wrappers
177    ///   (`Take<Take<...>>`). Failure to do so might result only in a tiny
178    ///   performance hit.
179    #[inline]
180    fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
181        r: &mut R,
182        modules: &ModuleDecoderRegistry,
183    ) -> Result<Self, DecodeError> {
184        // This method is always strictly less general than, `consensus_decode_partial`,
185        // so it's safe and make sense to default to just calling it. This way
186        // most types, that don't care about protecting against resource
187        // exhaustion due to malicious input, can just ignore it.
188        Self::consensus_decode_partial(r, modules)
189    }
190
191    #[inline]
192    fn consensus_decode_whole(
193        slice: &[u8],
194        modules: &ModuleDecoderRegistry,
195    ) -> Result<Self, DecodeError> {
196        let total_len = slice.len() as u64;
197
198        let r = &mut &slice[..];
199        let mut r = Read::take(r, total_len);
200
201        // This method is always strictly less general than, `consensus_decode_partial`,
202        // so it's safe and make sense to default to just calling it. This way
203        // most types, that don't care about protecting against resource
204        // exhaustion due to malicious input, can just ignore it.
205        let res = Self::consensus_decode_partial_from_finite_reader(&mut r, modules)?;
206        let left = r.limit();
207
208        if left != 0 {
209            return Err(fedimint_core::encoding::DecodeError::new_custom(
210                anyhow::anyhow!(
211                    "Type did not consume all bytes during decoding; expected={}; left={}; type={}",
212                    total_len,
213                    left,
214                    std::any::type_name::<Self>(),
215                ),
216            ));
217        }
218        Ok(res)
219    }
220    /// Decode an object with a well-defined format.
221    ///
222    /// This is the method that should be implemented for a typical, fixed sized
223    /// type implementing this trait. Default implementation is wrapping the
224    /// reader in [`std::io::Take`] to limit the input size to
225    /// [`MAX_DECODE_SIZE`], and forwards the call to
226    /// [`Self::consensus_decode_partial_from_finite_reader`], which is
227    /// convenient for types that override
228    /// [`Self::consensus_decode_partial_from_finite_reader`] instead.
229    #[inline]
230    fn consensus_decode_partial<R: std::io::Read>(
231        r: &mut R,
232        modules: &ModuleDecoderRegistry,
233    ) -> Result<Self, DecodeError> {
234        Self::consensus_decode_partial_from_finite_reader(
235            &mut r.take(MAX_DECODE_SIZE as u64),
236            modules,
237        )
238    }
239
240    /// Decode an object from hex
241    fn consensus_decode_hex(
242        hex: &str,
243        modules: &ModuleDecoderRegistry,
244    ) -> Result<Self, DecodeError> {
245        let bytes = Vec::<u8>::from_hex(hex)
246            .map_err(anyhow::Error::from)
247            .map_err(DecodeError::new_custom)?;
248        Decodable::consensus_decode_whole(&bytes, modules)
249    }
250}
251
252impl Encodable for SafeUrl {
253    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
254        self.to_string().consensus_encode(writer)
255    }
256}
257
258impl Decodable for SafeUrl {
259    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
260        d: &mut D,
261        modules: &ModuleDecoderRegistry,
262    ) -> Result<Self, DecodeError> {
263        String::consensus_decode_partial_from_finite_reader(d, modules)?
264            .parse::<Self>()
265            .map_err(DecodeError::from_err)
266    }
267}
268
269#[derive(Debug, Error)]
270pub struct DecodeError(pub(crate) anyhow::Error);
271
272impl DecodeError {
273    pub fn new_custom(e: anyhow::Error) -> Self {
274        Self(e)
275    }
276}
277
278impl From<anyhow::Error> for DecodeError {
279    fn from(e: anyhow::Error) -> Self {
280        Self(e)
281    }
282}
283
284macro_rules! impl_encode_decode_num_as_plain {
285    ($num_type:ty) => {
286        impl Encodable for $num_type {
287            fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
288                let bytes = self.to_be_bytes();
289                writer.write_all(&bytes[..])?;
290                Ok(bytes.len())
291            }
292        }
293
294        impl Decodable for $num_type {
295            fn consensus_decode_partial<D: std::io::Read>(
296                d: &mut D,
297                _modules: &ModuleDecoderRegistry,
298            ) -> Result<Self, crate::encoding::DecodeError> {
299                let mut bytes = [0u8; (<$num_type>::BITS / 8) as usize];
300                d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
301                Ok(<$num_type>::from_be_bytes(bytes))
302            }
303        }
304    };
305}
306
307macro_rules! impl_encode_decode_num_as_bigsize {
308    ($num_type:ty) => {
309        impl Encodable for $num_type {
310            fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
311                BigSize(u64::from(*self)).consensus_encode(writer)
312            }
313        }
314
315        impl Decodable for $num_type {
316            fn consensus_decode_partial<D: std::io::Read>(
317                d: &mut D,
318                _modules: &ModuleDecoderRegistry,
319            ) -> Result<Self, crate::encoding::DecodeError> {
320                let varint = BigSize::consensus_decode_partial(d, &Default::default())
321                    .context(concat!("VarInt inside ", stringify!($num_type)))?;
322                <$num_type>::try_from(varint.0).map_err(crate::encoding::DecodeError::from_err)
323            }
324        }
325    };
326}
327
328impl_encode_decode_num_as_bigsize!(u64);
329impl_encode_decode_num_as_bigsize!(u32);
330impl_encode_decode_num_as_bigsize!(u16);
331impl_encode_decode_num_as_plain!(u8);
332
333macro_rules! impl_encode_decode_tuple {
334    ($($x:ident),*) => (
335        #[allow(non_snake_case)]
336        impl <$($x: Encodable),*> Encodable for ($($x),*) {
337            fn consensus_encode<W: std::io::Write>(&self, s: &mut W) -> Result<usize, std::io::Error> {
338                let &($(ref $x),*) = self;
339                let mut len = 0;
340                $(len += $x.consensus_encode(s)?;)*
341                Ok(len)
342            }
343        }
344
345        #[allow(non_snake_case)]
346        impl<$($x: Decodable),*> Decodable for ($($x),*) {
347            fn consensus_decode_partial<D: std::io::Read>(d: &mut D, modules: &ModuleDecoderRegistry) -> Result<Self, DecodeError> {
348                Ok(($({let $x = Decodable::consensus_decode_partial(d, modules)?; $x }),*))
349            }
350        }
351    );
352}
353
354impl_encode_decode_tuple!(T1, T2);
355impl_encode_decode_tuple!(T1, T2, T3);
356impl_encode_decode_tuple!(T1, T2, T3, T4);
357
358impl<T> Encodable for Option<T>
359where
360    T: Encodable,
361{
362    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
363        let mut len = 0;
364        if let Some(inner) = self {
365            len += 1u8.consensus_encode(writer)?;
366            len += inner.consensus_encode(writer)?;
367        } else {
368            len += 0u8.consensus_encode(writer)?;
369        }
370        Ok(len)
371    }
372}
373
374impl<T> Decodable for Option<T>
375where
376    T: Decodable,
377{
378    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
379        d: &mut D,
380        modules: &ModuleDecoderRegistry,
381    ) -> Result<Self, DecodeError> {
382        let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
383        match flag {
384            0 => Ok(None),
385            1 => Ok(Some(T::consensus_decode_partial_from_finite_reader(
386                d, modules,
387            )?)),
388            _ => Err(DecodeError::from_str(
389                "Invalid flag for option enum, expected 0 or 1",
390            )),
391        }
392    }
393}
394
395impl<T, E> Encodable for Result<T, E>
396where
397    T: Encodable,
398    E: Encodable,
399{
400    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
401        let mut len = 0;
402
403        match self {
404            Ok(value) => {
405                len += 1u8.consensus_encode(writer)?;
406                len += value.consensus_encode(writer)?;
407            }
408            Err(error) => {
409                len += 0u8.consensus_encode(writer)?;
410                len += error.consensus_encode(writer)?;
411            }
412        }
413
414        Ok(len)
415    }
416}
417
418impl<T, E> Decodable for Result<T, E>
419where
420    T: Decodable,
421    E: Decodable,
422{
423    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
424        d: &mut D,
425        modules: &ModuleDecoderRegistry,
426    ) -> Result<Self, DecodeError> {
427        let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
428        match flag {
429            0 => Ok(Err(E::consensus_decode_partial_from_finite_reader(
430                d, modules,
431            )?)),
432            1 => Ok(Ok(T::consensus_decode_partial_from_finite_reader(
433                d, modules,
434            )?)),
435            _ => Err(DecodeError::from_str(
436                "Invalid flag for option enum, expected 0 or 1",
437            )),
438        }
439    }
440}
441
442impl<T> Encodable for Box<T>
443where
444    T: Encodable,
445{
446    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
447        self.as_ref().consensus_encode(writer)
448    }
449}
450
451impl<T> Decodable for Box<T>
452where
453    T: Decodable,
454{
455    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
456        d: &mut D,
457        modules: &ModuleDecoderRegistry,
458    ) -> Result<Self, DecodeError> {
459        Ok(Self::new(T::consensus_decode_partial_from_finite_reader(
460            d, modules,
461        )?))
462    }
463}
464
465impl Encodable for () {
466    fn consensus_encode<W: std::io::Write>(
467        &self,
468        _writer: &mut W,
469    ) -> Result<usize, std::io::Error> {
470        Ok(0)
471    }
472}
473
474impl Decodable for () {
475    fn consensus_decode_partial<D: std::io::Read>(
476        _d: &mut D,
477        _modules: &ModuleDecoderRegistry,
478    ) -> Result<Self, DecodeError> {
479        Ok(())
480    }
481}
482
483impl Encodable for &str {
484    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
485        self.as_bytes().consensus_encode(writer)
486    }
487}
488
489impl Encodable for String {
490    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
491        self.as_bytes().consensus_encode(writer)
492    }
493}
494
495impl Decodable for String {
496    fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
497        d: &mut D,
498        modules: &ModuleDecoderRegistry,
499    ) -> Result<Self, DecodeError> {
500        Self::from_utf8(Decodable::consensus_decode_partial_from_finite_reader(
501            d, modules,
502        )?)
503        .map_err(DecodeError::from_err)
504    }
505}
506
507impl Encodable for SystemTime {
508    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
509        let duration = self.duration_since(UNIX_EPOCH).expect("valid duration");
510        duration.consensus_encode_dyn(writer)
511    }
512}
513
514impl Decodable for SystemTime {
515    fn consensus_decode_partial<D: std::io::Read>(
516        d: &mut D,
517        modules: &ModuleDecoderRegistry,
518    ) -> Result<Self, DecodeError> {
519        let duration = Duration::consensus_decode_partial(d, modules)?;
520        Ok(UNIX_EPOCH + duration)
521    }
522}
523
524impl Encodable for Duration {
525    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
526        let mut count = 0;
527        count += self.as_secs().consensus_encode(writer)?;
528        count += self.subsec_nanos().consensus_encode(writer)?;
529
530        Ok(count)
531    }
532}
533
534impl Decodable for Duration {
535    fn consensus_decode_partial<D: std::io::Read>(
536        d: &mut D,
537        modules: &ModuleDecoderRegistry,
538    ) -> Result<Self, DecodeError> {
539        let secs = Decodable::consensus_decode_partial(d, modules)?;
540        let nsecs = Decodable::consensus_decode_partial(d, modules)?;
541        Ok(Self::new(secs, nsecs))
542    }
543}
544
545impl Encodable for bool {
546    fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<usize, Error> {
547        let bool_as_u8 = u8::from(*self);
548        writer.write_all(&[bool_as_u8])?;
549        Ok(1)
550    }
551}
552
553impl Decodable for bool {
554    fn consensus_decode_partial<D: Read>(
555        d: &mut D,
556        _modules: &ModuleDecoderRegistry,
557    ) -> Result<Self, DecodeError> {
558        let mut bool_as_u8 = [0u8];
559        d.read_exact(&mut bool_as_u8)
560            .map_err(DecodeError::from_err)?;
561        match bool_as_u8[0] {
562            0 => Ok(false),
563            1 => Ok(true),
564            _ => Err(DecodeError::from_str("Out of range, expected 0 or 1")),
565        }
566    }
567}
568
569impl DecodeError {
570    // TODO: think about better name
571    #[allow(clippy::should_implement_trait)]
572    pub fn from_str(s: &'static str) -> Self {
573        #[derive(Debug)]
574        struct StrError(&'static str);
575
576        impl std::fmt::Display for StrError {
577            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
578                std::fmt::Display::fmt(&self.0, f)
579            }
580        }
581
582        impl std::error::Error for StrError {}
583
584        Self(anyhow::Error::from(StrError(s)))
585    }
586
587    pub fn from_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
588        Self(anyhow::Error::from(e))
589    }
590}
591
592impl std::fmt::Display for DecodeError {
593    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
594        f.write_fmt(format_args!("{:#}", self.0))
595    }
596}
597
598impl Encodable for Cow<'static, str> {
599    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
600        self.as_ref().consensus_encode(writer)
601    }
602}
603
604impl Decodable for Cow<'static, str> {
605    fn consensus_decode_partial<D: std::io::Read>(
606        d: &mut D,
607        modules: &ModuleDecoderRegistry,
608    ) -> Result<Self, DecodeError> {
609        Ok(Cow::Owned(String::consensus_decode_partial(d, modules)?))
610    }
611}
612
613/// A type that decodes `module_instance_id`-prefixed `T`s even
614/// when corresponding `Decoder` is not available.
615///
616/// All dyn-module types are encoded as:
617///
618/// ```norust
619/// module_instance_id | len_u64 | data
620/// ```
621///
622/// So clients that don't have a corresponding module, can read
623/// the `len_u64` and skip the amount of data specified in it.
624///
625/// This type makes it more convenient. It's possible to attempt
626/// to retry decoding after more modules become available by using
627/// [`DynRawFallback::redecode_raw`].
628///
629/// Notably this struct does not ignore any errors. It only skips
630/// decoding when the module decoder is not available.
631#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
632pub enum DynRawFallback<T> {
633    Raw {
634        module_instance_id: ModuleInstanceId,
635        #[serde(with = "::fedimint_core::encoding::as_hex")]
636        raw: Vec<u8>,
637    },
638    Decoded(T),
639}
640
641impl<T> DynRawFallback<T>
642where
643    T: Decodable + 'static,
644{
645    /// Get the decoded `T` or `None` if not decoded yet
646    pub fn decoded(self) -> Option<T> {
647        match self {
648            Self::Raw { .. } => None,
649            Self::Decoded(v) => Some(v),
650        }
651    }
652
653    /// Convert into the decoded `T` and panic if not decoded yet
654    pub fn expect_decoded(self) -> T {
655        match self {
656            Self::Raw { .. } => {
657                panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
658            }
659            Self::Decoded(v) => v,
660        }
661    }
662
663    /// Get the decoded `T` and panic if not decoded yet
664    pub fn expect_decoded_ref(&self) -> &T {
665        match self {
666            Self::Raw { .. } => {
667                panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
668            }
669            Self::Decoded(v) => v,
670        }
671    }
672
673    /// Attempt to re-decode raw values with new set of of `modules`
674    ///
675    /// In certain contexts it might be necessary to try again with
676    /// a new set of modules.
677    pub fn redecode_raw(
678        self,
679        decoders: &ModuleDecoderRegistry,
680    ) -> Result<Self, crate::encoding::DecodeError> {
681        Ok(match self {
682            Self::Raw {
683                module_instance_id,
684                raw,
685            } => match decoders.get(module_instance_id) {
686                Some(decoder) => Self::Decoded(decoder.decode_complete(
687                    &mut &raw[..],
688                    raw.len() as u64,
689                    module_instance_id,
690                    decoders,
691                )?),
692                None => Self::Raw {
693                    module_instance_id,
694                    raw,
695                },
696            },
697            Self::Decoded(v) => Self::Decoded(v),
698        })
699    }
700}
701
702impl<T> From<T> for DynRawFallback<T> {
703    fn from(value: T) -> Self {
704        Self::Decoded(value)
705    }
706}
707
708impl<T> Decodable for DynRawFallback<T>
709where
710    T: Decodable + 'static,
711{
712    fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
713        reader: &mut R,
714        decoders: &ModuleDecoderRegistry,
715    ) -> Result<Self, crate::encoding::DecodeError> {
716        let module_instance_id =
717            fedimint_core::core::ModuleInstanceId::consensus_decode_partial_from_finite_reader(
718                reader, decoders,
719            )?;
720        Ok(match decoders.get(module_instance_id) {
721            Some(decoder) => {
722                let total_len_u64 =
723                    u64::consensus_decode_partial_from_finite_reader(reader, decoders)?;
724                Self::Decoded(decoder.decode_complete(
725                    reader,
726                    total_len_u64,
727                    module_instance_id,
728                    decoders,
729                )?)
730            }
731            None => {
732                // since the decoder is not available, just read the raw data
733                Self::Raw {
734                    module_instance_id,
735                    raw: Vec::consensus_decode_partial_from_finite_reader(reader, decoders)?,
736                }
737            }
738        })
739    }
740}
741
742impl<T> Encodable for DynRawFallback<T>
743where
744    T: Encodable,
745{
746    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error> {
747        match self {
748            Self::Raw {
749                module_instance_id,
750                raw,
751            } => {
752                let mut written = module_instance_id.consensus_encode(writer)?;
753                written += raw.consensus_encode(writer)?;
754                Ok(written)
755            }
756            Self::Decoded(v) => v.consensus_encode(writer),
757        }
758    }
759}
760
761#[cfg(test)]
762mod tests {
763    use std::fmt::Debug;
764    use std::io::Cursor;
765
766    use super::*;
767    use crate::encoding::{Decodable, Encodable};
768    use crate::module::registry::ModuleRegistry;
769
770    pub(crate) fn test_roundtrip<T>(value: &T)
771    where
772        T: Encodable + Decodable + Eq + Debug,
773    {
774        let mut bytes = Vec::new();
775        let len = value.consensus_encode(&mut bytes).unwrap();
776        assert_eq!(len, bytes.len());
777
778        let mut cursor = Cursor::new(bytes);
779        let decoded =
780            T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
781        assert_eq!(value, &decoded);
782        assert_eq!(cursor.position(), len as u64);
783    }
784
785    pub(crate) fn test_roundtrip_expected<T>(value: &T, expected: &[u8])
786    where
787        T: Encodable + Decodable + Eq + Debug,
788    {
789        let mut bytes = Vec::new();
790        let len = value.consensus_encode(&mut bytes).unwrap();
791        assert_eq!(len, bytes.len());
792        assert_eq!(&expected, &bytes);
793
794        let mut cursor = Cursor::new(bytes);
795        let decoded =
796            T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
797        assert_eq!(value, &decoded);
798        assert_eq!(cursor.position(), len as u64);
799    }
800
801    #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
802    enum NoDefaultEnum {
803        Foo,
804        Bar(u32, String),
805        Baz { baz: u8 },
806    }
807
808    #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
809    enum DefaultEnum {
810        Foo,
811        Bar(u32, String),
812        #[encodable_default]
813        Default {
814            variant: u64,
815            bytes: Vec<u8>,
816        },
817    }
818
819    #[test_log::test]
820    fn test_derive_enum_no_default_roundtrip_success() {
821        let enums = [
822            NoDefaultEnum::Foo,
823            NoDefaultEnum::Bar(
824                42,
825                "The answer to life, the universe, and everything".to_string(),
826            ),
827            NoDefaultEnum::Baz { baz: 0 },
828        ];
829
830        for e in enums {
831            test_roundtrip(&e);
832        }
833    }
834
835    #[test_log::test]
836    fn test_derive_enum_no_default_decode_fail() {
837        let unknown_variant = DefaultEnum::Default {
838            variant: 42,
839            bytes: vec![0, 1, 2, 3],
840        };
841        let mut unknown_variant_encoding = vec![];
842        unknown_variant
843            .consensus_encode(&mut unknown_variant_encoding)
844            .unwrap();
845
846        let mut cursor = Cursor::new(&unknown_variant_encoding);
847        let decode_res =
848            NoDefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
849
850        match decode_res {
851            Ok(_) => panic!("Should return error"),
852            Err(e) => assert!(e.to_string().contains("Invalid enum variant")),
853        }
854    }
855
856    #[test_log::test]
857    fn test_derive_enum_default_decode_success() {
858        let unknown_variant = NoDefaultEnum::Baz { baz: 123 };
859        let mut unknown_variant_encoding = vec![];
860        unknown_variant
861            .consensus_encode(&mut unknown_variant_encoding)
862            .unwrap();
863
864        let mut cursor = Cursor::new(&unknown_variant_encoding);
865        let decode_res =
866            DefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
867
868        assert_eq!(
869            decode_res.unwrap(),
870            DefaultEnum::Default {
871                variant: 2,
872                bytes: vec![123],
873            }
874        );
875    }
876
877    #[test_log::test]
878    fn test_derive_struct() {
879        #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
880        struct TestStruct {
881            vec: Vec<u8>,
882            num: u32,
883        }
884
885        let reference = TestStruct {
886            vec: vec![1, 2, 3],
887            num: 42,
888        };
889        let bytes = [3, 1, 2, 3, 42];
890
891        test_roundtrip_expected(&reference, &bytes);
892    }
893
894    #[test_log::test]
895    fn test_derive_tuple_struct() {
896        #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
897        struct TestStruct(Vec<u8>, u32);
898
899        let reference = TestStruct(vec![1, 2, 3], 42);
900        let bytes = [3, 1, 2, 3, 42];
901
902        test_roundtrip_expected(&reference, &bytes);
903    }
904
905    #[test_log::test]
906    fn test_derive_enum() {
907        #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
908        enum TestEnum {
909            Foo(Option<u64>),
910            Bar { bazz: Vec<u8> },
911        }
912
913        let test_cases = [
914            (TestEnum::Foo(Some(42)), vec![0, 2, 1, 42]),
915            (TestEnum::Foo(None), vec![0, 1, 0]),
916            (
917                TestEnum::Bar {
918                    bazz: vec![1, 2, 3],
919                },
920                vec![1, 4, 3, 1, 2, 3],
921            ),
922        ];
923
924        for (reference, bytes) in test_cases {
925            test_roundtrip_expected(&reference, &bytes);
926        }
927    }
928
929    #[test_log::test]
930    fn test_systemtime() {
931        test_roundtrip(&fedimint_core::time::now());
932    }
933
934    #[test]
935    fn test_derive_empty_enum_decode() {
936        #[derive(Debug, Encodable, Decodable)]
937        enum NotConstructable {}
938
939        let vec = vec![42u8];
940        let mut cursor = Cursor::new(vec);
941
942        assert!(NotConstructable::consensus_decode_partial(
943            &mut cursor,
944            &ModuleDecoderRegistry::default()
945        )
946        .is_err());
947    }
948
949    #[test]
950    fn test_custom_index_enum() {
951        #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
952        enum Old {
953            Foo,
954            Bar,
955            Baz,
956        }
957
958        #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
959        enum New {
960            #[encodable(index = 0)]
961            Foo,
962            #[encodable(index = 2)]
963            Baz,
964            #[encodable_default]
965            Default { variant: u64, bytes: Vec<u8> },
966        }
967
968        let test_vector = vec![
969            (Old::Foo, New::Foo),
970            (
971                Old::Bar,
972                New::Default {
973                    variant: 1,
974                    bytes: vec![],
975                },
976            ),
977            (Old::Baz, New::Baz),
978        ];
979
980        for (old, new) in test_vector {
981            let old_bytes = old.consensus_encode_to_vec();
982            let decoded_new = New::consensus_decode_whole(&old_bytes, &ModuleRegistry::default())
983                .expect("Decoding failed");
984            assert_eq!(decoded_new, new);
985        }
986    }
987
988    fn encode_value<T: Encodable>(value: &T) -> Vec<u8> {
989        let mut writer = Vec::new();
990        value.consensus_encode(&mut writer).unwrap();
991        writer
992    }
993
994    fn decode_value<T: Decodable>(bytes: &[u8]) -> T {
995        T::consensus_decode_whole(bytes, &ModuleDecoderRegistry::default()).unwrap()
996    }
997
998    fn keeps_ordering_after_serialization<T: Ord + Encodable + Decodable + Debug>(mut vec: Vec<T>) {
999        vec.sort();
1000        let mut encoded = vec.iter().map(encode_value).collect::<Vec<_>>();
1001        encoded.sort();
1002        let decoded = encoded.iter().map(|v| decode_value(v)).collect::<Vec<_>>();
1003        for (i, (a, b)) in vec.iter().zip(decoded.iter()).enumerate() {
1004            assert_eq!(a, b, "difference at index {i}");
1005        }
1006    }
1007
1008    #[test]
1009    fn test_lexicographical_sorting() {
1010        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1011        struct TestAmount(u64);
1012
1013        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1014        struct TestComplexAmount(u16, u32, u64);
1015
1016        #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1017        struct Text(String);
1018
1019        let amounts = (0..20000).map(TestAmount).collect::<Vec<_>>();
1020        keeps_ordering_after_serialization(amounts);
1021
1022        let complex_amounts = (10..20000)
1023            .flat_map(|i| {
1024                (i - 1..=i + 1).flat_map(move |j| {
1025                    (i - 1..=i + 1).map(move |k| TestComplexAmount(i as u16, j as u32, k as u64))
1026                })
1027            })
1028            .collect::<Vec<_>>();
1029        keeps_ordering_after_serialization(complex_amounts);
1030
1031        let texts = (' '..'~')
1032            .flat_map(|i| {
1033                (' '..'~')
1034                    .map(|j| Text(format!("{i}{j}")))
1035                    .collect::<Vec<_>>()
1036            })
1037            .collect::<Vec<_>>();
1038        keeps_ordering_after_serialization(texts);
1039
1040        // bitcoin structures are not lexicographically sortable so we cannot
1041        // test them here. in future we may crate a wrapper type that is
1042        // lexicographically sortable to use when needed
1043    }
1044}