1pub 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::fmt::{Debug, Formatter};
22use std::io::{self, Error, Read, Write};
23use std::time::{Duration, SystemTime, UNIX_EPOCH};
24
25use anyhow::Context;
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
37pub struct CountWrite<W> {
43 inner: W,
44 count: u64,
45}
46
47impl<W> CountWrite<W> {
48 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
72pub 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
113pub trait Encodable {
115 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error>;
120
121 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 fn consensus_encode_to_hex(&self) -> String {
131 self.consensus_encode_to_vec().encode_hex()
135 }
136
137 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 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 fn consensus_hash_sha256(&self) -> sha256::Hash {
164 self.consensus_hash()
165 }
166}
167
168pub const MAX_DECODE_SIZE: usize = 16_000_000;
171
172pub trait Decodable: Sized {
174 #[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 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 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::new_custom(
241 anyhow::anyhow!(
242 "Type did not consume all bytes during decoding; expected={}; left={}; type={}",
243 total_len,
244 left,
245 std::any::type_name::<Self>(),
246 ),
247 ));
248 }
249 Ok(res)
250 }
251 #[inline]
261 fn consensus_decode_partial<R: std::io::Read>(
262 r: &mut R,
263 modules: &ModuleDecoderRegistry,
264 ) -> Result<Self, DecodeError> {
265 Self::consensus_decode_partial_from_finite_reader(
266 &mut r.take(MAX_DECODE_SIZE as u64),
267 modules,
268 )
269 }
270
271 fn consensus_decode_hex(
273 hex: &str,
274 modules: &ModuleDecoderRegistry,
275 ) -> Result<Self, DecodeError> {
276 let bytes = Vec::<u8>::from_hex(hex)
277 .map_err(anyhow::Error::from)
278 .map_err(DecodeError::new_custom)?;
279 Decodable::consensus_decode_whole(&bytes, modules)
280 }
281}
282
283impl Encodable for SafeUrl {
284 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
285 self.to_string().consensus_encode(writer)
286 }
287}
288
289impl Decodable for SafeUrl {
290 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
291 d: &mut D,
292 modules: &ModuleDecoderRegistry,
293 ) -> Result<Self, DecodeError> {
294 String::consensus_decode_partial_from_finite_reader(d, modules)?
295 .parse::<Self>()
296 .map_err(DecodeError::from_err)
297 }
298}
299
300#[derive(Debug, Error)]
301pub struct DecodeError(pub(crate) anyhow::Error);
302
303impl DecodeError {
304 pub fn new_custom(e: anyhow::Error) -> Self {
305 Self(e)
306 }
307}
308
309impl From<anyhow::Error> for DecodeError {
310 fn from(e: anyhow::Error) -> Self {
311 Self(e)
312 }
313}
314
315macro_rules! impl_encode_decode_num_as_plain {
316 ($num_type:ty) => {
317 impl Encodable for $num_type {
318 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
319 let bytes = self.to_be_bytes();
320 writer.write_all(&bytes[..])?;
321 Ok(())
322 }
323 }
324
325 impl Decodable for $num_type {
326 fn consensus_decode_partial<D: std::io::Read>(
327 d: &mut D,
328 _modules: &ModuleDecoderRegistry,
329 ) -> Result<Self, crate::encoding::DecodeError> {
330 let mut bytes = [0u8; (<$num_type>::BITS / 8) as usize];
331 d.read_exact(&mut bytes).map_err(DecodeError::from_err)?;
332 Ok(<$num_type>::from_be_bytes(bytes))
333 }
334 }
335 };
336}
337
338macro_rules! impl_encode_decode_num_as_bigsize {
339 ($num_type:ty) => {
340 impl Encodable for $num_type {
341 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
342 BigSize(u64::from(*self)).consensus_encode(writer)
343 }
344 }
345
346 impl Decodable for $num_type {
347 fn consensus_decode_partial<D: std::io::Read>(
348 d: &mut D,
349 _modules: &ModuleDecoderRegistry,
350 ) -> Result<Self, crate::encoding::DecodeError> {
351 let varint = BigSize::consensus_decode_partial(d, &Default::default())
352 .context(concat!("VarInt inside ", stringify!($num_type)))?;
353 <$num_type>::try_from(varint.0).map_err(crate::encoding::DecodeError::from_err)
354 }
355 }
356 };
357}
358
359impl_encode_decode_num_as_bigsize!(u64);
360impl_encode_decode_num_as_bigsize!(u32);
361impl_encode_decode_num_as_bigsize!(u16);
362impl_encode_decode_num_as_plain!(u8);
363
364macro_rules! impl_encode_decode_tuple {
365 ($($x:ident),*) => (
366 #[allow(non_snake_case)]
367 impl <$($x: Encodable),*> Encodable for ($($x),*) {
368 fn consensus_encode<W: std::io::Write>(&self, s: &mut W) -> Result<(), std::io::Error> {
369 let &($(ref $x),*) = self;
370 $($x.consensus_encode(s)?;)*
371 Ok(())
372 }
373 }
374
375 #[allow(non_snake_case)]
376 impl<$($x: Decodable),*> Decodable for ($($x),*) {
377 fn consensus_decode_partial<D: std::io::Read>(d: &mut D, modules: &ModuleDecoderRegistry) -> Result<Self, DecodeError> {
378 Ok(($({let $x = Decodable::consensus_decode_partial(d, modules)?; $x }),*))
379 }
380 }
381 );
382}
383
384impl_encode_decode_tuple!(T1, T2);
385impl_encode_decode_tuple!(T1, T2, T3);
386impl_encode_decode_tuple!(T1, T2, T3, T4);
387
388impl<T> Encodable for Option<T>
389where
390 T: Encodable,
391{
392 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
393 if let Some(inner) = self {
394 1u8.consensus_encode(writer)?;
395 inner.consensus_encode(writer)?;
396 } else {
397 0u8.consensus_encode(writer)?;
398 }
399 Ok(())
400 }
401}
402
403impl<T> Decodable for Option<T>
404where
405 T: Decodable,
406{
407 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
408 d: &mut D,
409 modules: &ModuleDecoderRegistry,
410 ) -> Result<Self, DecodeError> {
411 let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
412 match flag {
413 0 => Ok(None),
414 1 => Ok(Some(T::consensus_decode_partial_from_finite_reader(
415 d, modules,
416 )?)),
417 _ => Err(DecodeError::from_str(
418 "Invalid flag for option enum, expected 0 or 1",
419 )),
420 }
421 }
422}
423
424impl<T, E> Encodable for Result<T, E>
425where
426 T: Encodable,
427 E: Encodable,
428{
429 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
430 match self {
431 Ok(value) => {
432 1u8.consensus_encode(writer)?;
433 value.consensus_encode(writer)?;
434 }
435 Err(error) => {
436 0u8.consensus_encode(writer)?;
437 error.consensus_encode(writer)?;
438 }
439 }
440
441 Ok(())
442 }
443}
444
445impl<T, E> Decodable for Result<T, E>
446where
447 T: Decodable,
448 E: Decodable,
449{
450 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
451 d: &mut D,
452 modules: &ModuleDecoderRegistry,
453 ) -> Result<Self, DecodeError> {
454 let flag = u8::consensus_decode_partial_from_finite_reader(d, modules)?;
455 match flag {
456 0 => Ok(Err(E::consensus_decode_partial_from_finite_reader(
457 d, modules,
458 )?)),
459 1 => Ok(Ok(T::consensus_decode_partial_from_finite_reader(
460 d, modules,
461 )?)),
462 _ => Err(DecodeError::from_str(
463 "Invalid flag for option enum, expected 0 or 1",
464 )),
465 }
466 }
467}
468
469impl<T> Encodable for Box<T>
470where
471 T: Encodable,
472{
473 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
474 self.as_ref().consensus_encode(writer)
475 }
476}
477
478impl<T> Decodable for Box<T>
479where
480 T: Decodable,
481{
482 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
483 d: &mut D,
484 modules: &ModuleDecoderRegistry,
485 ) -> Result<Self, DecodeError> {
486 Ok(Self::new(T::consensus_decode_partial_from_finite_reader(
487 d, modules,
488 )?))
489 }
490}
491
492impl Encodable for () {
493 fn consensus_encode<W: std::io::Write>(&self, _writer: &mut W) -> Result<(), std::io::Error> {
494 Ok(())
495 }
496}
497
498impl Decodable for () {
499 fn consensus_decode_partial<D: std::io::Read>(
500 _d: &mut D,
501 _modules: &ModuleDecoderRegistry,
502 ) -> Result<Self, DecodeError> {
503 Ok(())
504 }
505}
506
507impl Encodable for &str {
508 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
509 self.as_bytes().consensus_encode(writer)
510 }
511}
512
513impl Encodable for String {
514 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Error> {
515 self.as_bytes().consensus_encode(writer)
516 }
517}
518
519impl Decodable for String {
520 fn consensus_decode_partial_from_finite_reader<D: std::io::Read>(
521 d: &mut D,
522 modules: &ModuleDecoderRegistry,
523 ) -> Result<Self, DecodeError> {
524 Self::from_utf8(Decodable::consensus_decode_partial_from_finite_reader(
525 d, modules,
526 )?)
527 .map_err(DecodeError::from_err)
528 }
529}
530
531impl Encodable for SystemTime {
532 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
533 let duration = self.duration_since(UNIX_EPOCH).expect("valid duration");
534 duration.consensus_encode_dyn(writer)
535 }
536}
537
538impl Decodable for SystemTime {
539 fn consensus_decode_partial<D: std::io::Read>(
540 d: &mut D,
541 modules: &ModuleDecoderRegistry,
542 ) -> Result<Self, DecodeError> {
543 let duration = Duration::consensus_decode_partial(d, modules)?;
544 Ok(UNIX_EPOCH + duration)
545 }
546}
547
548impl Encodable for Duration {
549 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
550 self.as_secs().consensus_encode(writer)?;
551 self.subsec_nanos().consensus_encode(writer)?;
552
553 Ok(())
554 }
555}
556
557impl Decodable for Duration {
558 fn consensus_decode_partial<D: std::io::Read>(
559 d: &mut D,
560 modules: &ModuleDecoderRegistry,
561 ) -> Result<Self, DecodeError> {
562 let secs = Decodable::consensus_decode_partial(d, modules)?;
563 let nsecs = Decodable::consensus_decode_partial(d, modules)?;
564 Ok(Self::new(secs, nsecs))
565 }
566}
567
568impl Encodable for bool {
569 fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
570 let bool_as_u8 = u8::from(*self);
571 writer.write_all(&[bool_as_u8])?;
572 Ok(())
573 }
574}
575
576impl Decodable for bool {
577 fn consensus_decode_partial<D: Read>(
578 d: &mut D,
579 _modules: &ModuleDecoderRegistry,
580 ) -> Result<Self, DecodeError> {
581 let mut bool_as_u8 = [0u8];
582 d.read_exact(&mut bool_as_u8)
583 .map_err(DecodeError::from_err)?;
584 match bool_as_u8[0] {
585 0 => Ok(false),
586 1 => Ok(true),
587 _ => Err(DecodeError::from_str("Out of range, expected 0 or 1")),
588 }
589 }
590}
591
592impl DecodeError {
593 #[allow(clippy::should_implement_trait)]
595 pub fn from_str(s: &'static str) -> Self {
596 #[derive(Debug)]
597 struct StrError(&'static str);
598
599 impl std::fmt::Display for StrError {
600 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
601 std::fmt::Display::fmt(&self.0, f)
602 }
603 }
604
605 impl std::error::Error for StrError {}
606
607 Self(anyhow::Error::from(StrError(s)))
608 }
609
610 pub fn from_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Self {
611 Self(anyhow::Error::from(e))
612 }
613}
614
615impl std::fmt::Display for DecodeError {
616 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
617 f.write_fmt(format_args!("{:#}", self.0))
618 }
619}
620
621impl Encodable for Cow<'static, str> {
622 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
623 self.as_ref().consensus_encode(writer)
624 }
625}
626
627impl Decodable for Cow<'static, str> {
628 fn consensus_decode_partial<D: std::io::Read>(
629 d: &mut D,
630 modules: &ModuleDecoderRegistry,
631 ) -> Result<Self, DecodeError> {
632 Ok(Cow::Owned(String::consensus_decode_partial(d, modules)?))
633 }
634}
635
636#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
655pub enum DynRawFallback<T> {
656 Raw {
657 module_instance_id: ModuleInstanceId,
658 #[serde(with = "::fedimint_core::encoding::as_hex")]
659 raw: Vec<u8>,
660 },
661 Decoded(T),
662}
663
664impl<T> DynRawFallback<T>
665where
666 T: Decodable + 'static,
667{
668 pub fn decoded(self) -> Option<T> {
670 match self {
671 Self::Raw { .. } => None,
672 Self::Decoded(v) => Some(v),
673 }
674 }
675
676 pub fn expect_decoded(self) -> T {
678 match self {
679 Self::Raw { .. } => {
680 panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
681 }
682 Self::Decoded(v) => v,
683 }
684 }
685
686 pub fn expect_decoded_ref(&self) -> &T {
688 match self {
689 Self::Raw { .. } => {
690 panic!("Expected decoded value. Possibly `redecode_raw` call is missing.")
691 }
692 Self::Decoded(v) => v,
693 }
694 }
695
696 pub fn redecode_raw(
701 self,
702 decoders: &ModuleDecoderRegistry,
703 ) -> Result<Self, crate::encoding::DecodeError> {
704 Ok(match self {
705 Self::Raw {
706 module_instance_id,
707 raw,
708 } => match decoders.get(module_instance_id) {
709 Some(decoder) => Self::Decoded(decoder.decode_complete(
710 &mut &raw[..],
711 raw.len() as u64,
712 module_instance_id,
713 decoders,
714 )?),
715 None => Self::Raw {
716 module_instance_id,
717 raw,
718 },
719 },
720 Self::Decoded(v) => Self::Decoded(v),
721 })
722 }
723}
724
725impl<T> From<T> for DynRawFallback<T> {
726 fn from(value: T) -> Self {
727 Self::Decoded(value)
728 }
729}
730
731impl<T> Decodable for DynRawFallback<T>
732where
733 T: Decodable + 'static,
734{
735 fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
736 reader: &mut R,
737 decoders: &ModuleDecoderRegistry,
738 ) -> Result<Self, crate::encoding::DecodeError> {
739 let module_instance_id =
740 fedimint_core::core::ModuleInstanceId::consensus_decode_partial_from_finite_reader(
741 reader, decoders,
742 )?;
743 Ok(match decoders.get(module_instance_id) {
744 Some(decoder) => {
745 let total_len_u64 =
746 u64::consensus_decode_partial_from_finite_reader(reader, decoders)?;
747 Self::Decoded(decoder.decode_complete(
748 reader,
749 total_len_u64,
750 module_instance_id,
751 decoders,
752 )?)
753 }
754 None => {
755 Self::Raw {
757 module_instance_id,
758 raw: Vec::consensus_decode_partial_from_finite_reader(reader, decoders)?,
759 }
760 }
761 })
762 }
763}
764
765impl<T> Encodable for DynRawFallback<T>
766where
767 T: Encodable,
768{
769 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
770 match self {
771 Self::Raw {
772 module_instance_id,
773 raw,
774 } => {
775 module_instance_id.consensus_encode(writer)?;
776 raw.consensus_encode(writer)?;
777 Ok(())
778 }
779 Self::Decoded(v) => v.consensus_encode(writer),
780 }
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use std::fmt::Debug;
787 use std::io::Cursor;
788
789 use super::*;
790 use crate::encoding::{Decodable, Encodable};
791 use crate::module::registry::ModuleRegistry;
792
793 pub(crate) fn test_roundtrip<T>(value: &T)
794 where
795 T: Encodable + Decodable + Eq + Debug,
796 {
797 let mut bytes = Vec::new();
798 value.consensus_encode(&mut bytes).unwrap();
799
800 let mut cursor = Cursor::new(bytes);
801 let decoded =
802 T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
803 assert_eq!(value, &decoded);
804 }
805
806 pub(crate) fn test_roundtrip_expected<T>(value: &T, expected: &[u8])
807 where
808 T: Encodable + Decodable + Eq + Debug,
809 {
810 let mut bytes = Vec::new();
811 value.consensus_encode(&mut bytes).unwrap();
812 assert_eq!(&expected, &bytes);
813
814 let mut cursor = Cursor::new(bytes);
815 let decoded =
816 T::consensus_decode_partial(&mut cursor, &ModuleDecoderRegistry::default()).unwrap();
817 assert_eq!(value, &decoded);
818 }
819
820 #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
821 enum NoDefaultEnum {
822 Foo,
823 Bar(u32, String),
824 Baz { baz: u8 },
825 }
826
827 #[derive(Debug, Eq, PartialEq, Encodable, Decodable)]
828 enum DefaultEnum {
829 Foo,
830 Bar(u32, String),
831 #[encodable_default]
832 Default {
833 variant: u64,
834 bytes: Vec<u8>,
835 },
836 }
837
838 #[test_log::test]
839 fn test_derive_enum_no_default_roundtrip_success() {
840 let enums = [
841 NoDefaultEnum::Foo,
842 NoDefaultEnum::Bar(
843 42,
844 "The answer to life, the universe, and everything".to_string(),
845 ),
846 NoDefaultEnum::Baz { baz: 0 },
847 ];
848
849 for e in enums {
850 test_roundtrip(&e);
851 }
852 }
853
854 #[test_log::test]
855 fn test_derive_enum_no_default_decode_fail() {
856 let unknown_variant = DefaultEnum::Default {
857 variant: 42,
858 bytes: vec![0, 1, 2, 3],
859 };
860 let mut unknown_variant_encoding = vec![];
861 unknown_variant
862 .consensus_encode(&mut unknown_variant_encoding)
863 .unwrap();
864
865 let mut cursor = Cursor::new(&unknown_variant_encoding);
866 let decode_res =
867 NoDefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
868
869 match decode_res {
870 Ok(_) => panic!("Should return error"),
871 Err(e) => assert!(e.to_string().contains("Invalid enum variant")),
872 }
873 }
874
875 #[test_log::test]
876 fn test_derive_enum_default_decode_success() {
877 let unknown_variant = NoDefaultEnum::Baz { baz: 123 };
878 let mut unknown_variant_encoding = vec![];
879 unknown_variant
880 .consensus_encode(&mut unknown_variant_encoding)
881 .unwrap();
882
883 let mut cursor = Cursor::new(&unknown_variant_encoding);
884 let decode_res =
885 DefaultEnum::consensus_decode_partial(&mut cursor, &ModuleRegistry::default());
886
887 assert_eq!(
888 decode_res.unwrap(),
889 DefaultEnum::Default {
890 variant: 2,
891 bytes: vec![123],
892 }
893 );
894 }
895
896 #[test_log::test]
897 fn test_derive_struct() {
898 #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
899 struct TestStruct {
900 vec: Vec<u8>,
901 num: u32,
902 }
903
904 let reference = TestStruct {
905 vec: vec![1, 2, 3],
906 num: 42,
907 };
908 let bytes = [3, 1, 2, 3, 42];
909
910 test_roundtrip_expected(&reference, &bytes);
911 }
912
913 #[test_log::test]
914 fn test_derive_tuple_struct() {
915 #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
916 struct TestStruct(Vec<u8>, u32);
917
918 let reference = TestStruct(vec![1, 2, 3], 42);
919 let bytes = [3, 1, 2, 3, 42];
920
921 test_roundtrip_expected(&reference, &bytes);
922 }
923
924 #[test_log::test]
925 fn test_derive_enum() {
926 #[derive(Debug, Encodable, Decodable, Eq, PartialEq)]
927 enum TestEnum {
928 Foo(Option<u64>),
929 Bar { bazz: Vec<u8> },
930 }
931
932 let test_cases = [
933 (TestEnum::Foo(Some(42)), vec![0, 2, 1, 42]),
934 (TestEnum::Foo(None), vec![0, 1, 0]),
935 (
936 TestEnum::Bar {
937 bazz: vec![1, 2, 3],
938 },
939 vec![1, 4, 3, 1, 2, 3],
940 ),
941 ];
942
943 for (reference, bytes) in test_cases {
944 test_roundtrip_expected(&reference, &bytes);
945 }
946 }
947
948 #[test_log::test]
949 fn test_systemtime() {
950 test_roundtrip(&fedimint_core::time::now());
951 }
952
953 #[test]
954 fn test_derive_empty_enum_decode() {
955 #[derive(Debug, Encodable, Decodable)]
956 enum NotConstructable {}
957
958 let vec = vec![42u8];
959 let mut cursor = Cursor::new(vec);
960
961 assert!(
962 NotConstructable::consensus_decode_partial(
963 &mut cursor,
964 &ModuleDecoderRegistry::default()
965 )
966 .is_err()
967 );
968 }
969
970 #[test]
971 fn test_custom_index_enum() {
972 #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
973 enum Old {
974 Foo,
975 Bar,
976 Baz,
977 }
978
979 #[derive(Debug, PartialEq, Eq, Encodable, Decodable)]
980 enum New {
981 #[encodable(index = 0)]
982 Foo,
983 #[encodable(index = 2)]
984 Baz,
985 #[encodable_default]
986 Default { variant: u64, bytes: Vec<u8> },
987 }
988
989 let test_vector = vec![
990 (Old::Foo, New::Foo),
991 (
992 Old::Bar,
993 New::Default {
994 variant: 1,
995 bytes: vec![],
996 },
997 ),
998 (Old::Baz, New::Baz),
999 ];
1000
1001 for (old, new) in test_vector {
1002 let old_bytes = old.consensus_encode_to_vec();
1003 let decoded_new = New::consensus_decode_whole(&old_bytes, &ModuleRegistry::default())
1004 .expect("Decoding failed");
1005 assert_eq!(decoded_new, new);
1006 }
1007 }
1008
1009 fn encode_value<T: Encodable>(value: &T) -> Vec<u8> {
1010 let mut writer = Vec::new();
1011 value.consensus_encode(&mut writer).unwrap();
1012 writer
1013 }
1014
1015 fn decode_value<T: Decodable>(bytes: &[u8]) -> T {
1016 T::consensus_decode_whole(bytes, &ModuleDecoderRegistry::default()).unwrap()
1017 }
1018
1019 fn keeps_ordering_after_serialization<T: Ord + Encodable + Decodable + Debug>(mut vec: Vec<T>) {
1020 vec.sort();
1021 let mut encoded = vec.iter().map(encode_value).collect::<Vec<_>>();
1022 encoded.sort();
1023 let decoded = encoded.iter().map(|v| decode_value(v)).collect::<Vec<_>>();
1024 for (i, (a, b)) in vec.iter().zip(decoded.iter()).enumerate() {
1025 assert_eq!(a, b, "difference at index {i}");
1026 }
1027 }
1028
1029 #[test]
1030 fn test_lexicographical_sorting() {
1031 #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1032 struct TestAmount(u64);
1033
1034 #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1035 struct TestComplexAmount(u16, u32, u64);
1036
1037 #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Encodable, Decodable)]
1038 struct Text(String);
1039
1040 let amounts = (0..20000).map(TestAmount).collect::<Vec<_>>();
1041 keeps_ordering_after_serialization(amounts);
1042
1043 let complex_amounts = (10..20000)
1044 .flat_map(|i| {
1045 (i - 1..=i + 1).flat_map(move |j| {
1046 (i - 1..=i + 1).map(move |k| TestComplexAmount(i as u16, j as u32, k as u64))
1047 })
1048 })
1049 .collect::<Vec<_>>();
1050 keeps_ordering_after_serialization(complex_amounts);
1051
1052 let texts = (' '..'~')
1053 .flat_map(|i| {
1054 (' '..'~')
1055 .map(|j| Text(format!("{i}{j}")))
1056 .collect::<Vec<_>>()
1057 })
1058 .collect::<Vec<_>>();
1059 keeps_ordering_after_serialization(texts);
1060
1061 }
1065}