1pub 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
41pub 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
85pub trait Encodable {
87 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<usize, std::io::Error>;
92
93 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 fn consensus_encode_to_hex(&self) -> String {
103 self.consensus_encode_to_vec().encode_hex()
107 }
108
109 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 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 fn consensus_hash_sha256(&self) -> sha256::Hash {
133 self.consensus_hash()
134 }
135}
136
137pub const MAX_DECODE_SIZE: usize = 16_000_000;
140
141pub trait Decodable: Sized {
143 #[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 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 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 #[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 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 #[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#[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 pub fn decoded(self) -> Option<T> {
647 match self {
648 Self::Raw { .. } => None,
649 Self::Decoded(v) => Some(v),
650 }
651 }
652
653 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 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 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 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 }
1044}