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;