fedimint_core/
macros.rs

1/// Define "dyn newtype" (a newtype over `dyn Trait`)
2///
3/// This is a simple pattern that make working with `dyn Trait`s
4/// easier, by hiding their details.
5///
6/// A "dyn newtype" `Deref`s to the underlying `&dyn Trait`, making
7/// it easy to access the encapsulated operations, while hiding
8/// the boxing details.
9#[macro_export]
10macro_rules! dyn_newtype_define {
11    (   $(#[$outer:meta])*
12        $vis:vis $name:ident<$lifetime:lifetime>(Box<$trait:ident>)
13    ) => {
14        $crate::_dyn_newtype_define_inner!{
15            $(#[$outer])*
16            $vis $name<$lifetime>(Box<$trait>)
17        }
18        $crate::_dyn_newtype_impl_deref_mut!($name<$lifetime>);
19    };
20    (   $(#[$outer:meta])*
21        $vis:vis $name:ident(Box<$trait:ident>)
22    ) => {
23        $crate::_dyn_newtype_define_inner!{
24            $(#[$outer])*
25            $vis $name(Box<$trait>)
26        }
27        $crate::_dyn_newtype_impl_deref_mut!($name);
28    };
29    (   $(#[$outer:meta])*
30        $vis:vis $name:ident<$lifetime:lifetime>(Arc<$trait:ident>)
31    ) => {
32        $crate::_dyn_newtype_define_inner!{
33            $(#[$outer])*
34            $vis $name<$lifetime>(Arc<$trait>)
35        }
36    };
37    (   $(#[$outer:meta])*
38        $vis:vis $name:ident(Arc<$trait:ident>)
39    ) => {
40        $crate::_dyn_newtype_define_inner!{
41            $(#[$outer])*
42            $vis $name(Arc<$trait>)
43        }
44    };
45}
46
47#[macro_export]
48macro_rules! _dyn_newtype_define_inner {
49    (   $(#[$outer:meta])*
50        $vis:vis $name:ident($container:ident<$trait:ident>)
51    ) => {
52        $(#[$outer])*
53        $vis struct $name { inner: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)> }
54
55        impl std::ops::Deref for $name {
56            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
57
58            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
59                &*self.inner
60            }
61
62        }
63
64        impl $name {
65            pub fn get_mut(&mut self) -> Option<&mut <Self as std::ops::Deref>::Target> {
66                Arc::get_mut(&mut self.inner)
67            }
68        }
69
70        impl<I> From<I> for $name
71        where
72            I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static,
73        {
74            fn from(i: I) -> Self {
75                Self { inner: $container::new(i) }
76            }
77        }
78
79        impl std::fmt::Debug for $name {
80            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81                std::fmt::Debug::fmt(&self.inner, f)
82            }
83        }
84    };
85    (   $(#[$outer:meta])*
86        $vis:vis $name:ident<$lifetime:lifetime>($container:ident<$trait:ident>)
87    ) => {
88        $(#[$outer])*
89        $vis struct $name<$lifetime> { inner: $container<dyn $trait<$lifetime> + Send + $lifetime> }
90
91        impl<$lifetime> std::ops::Deref for $name<$lifetime> {
92            type Target = $crate::maybe_add_send!(dyn $trait<$lifetime> + $lifetime);
93
94            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
95                &*self.inner
96            }
97        }
98
99        impl<$lifetime, I> From<I> for $name<$lifetime>
100        where
101            I: $trait<$lifetime> + $crate::task::MaybeSend + $lifetime,
102        {
103            fn from(i: I) -> Self {
104                Self($container::new(i))
105            }
106        }
107    };
108}
109
110/// Implements the `Display` trait for dyn newtypes whose traits implement
111/// `Display`
112#[macro_export]
113macro_rules! dyn_newtype_display_passthrough {
114    ($newtype:ty) => {
115        impl std::fmt::Display for $newtype {
116            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117                std::fmt::Display::fmt(&self.inner, f)
118            }
119        }
120    };
121}
122
123/// Define a "module plugin dyn-newtype" which is like a standard "dyn newtype",
124/// but with associated "module_instance_id".
125#[macro_export]
126macro_rules! module_plugin_dyn_newtype_define{
127    (   $(#[$outer:meta])*
128        $vis:vis $name:ident<$lifetime:lifetime>(Box<$trait:ident>)
129    ) => {
130        $crate::_dyn_newtype_define_with_instance_id_inner!{
131            $(#[$outer])*
132            $vis $name<$lifetime>(Box<$trait>)
133        }
134        $crate::_dyn_newtype_impl_deref_mut!($name<$lifetime>);
135    };
136    (   $(#[$outer:meta])*
137        $vis:vis $name:ident(Box<$trait:ident>)
138    ) => {
139        $crate::_dyn_newtype_define_with_instance_id_inner!{
140            $(#[$outer])*
141            $vis $name(Box<$trait>)
142        }
143        $crate::_dyn_newtype_impl_deref_mut!($name);
144    };
145    (   $(#[$outer:meta])*
146        $vis:vis $name:ident<$lifetime:lifetime>(Arc<$trait:ident>)
147    ) => {
148        $crate::_dyn_newtype_define_with_instance_id_inner!{
149            $(#[$outer])*
150            $vis $name<$lifetime>(Arc<$trait>)
151        }
152    };
153    (   $(#[$outer:meta])*
154        $vis:vis $name:ident(Arc<$trait:ident>)
155    ) => {
156        $crate::_dyn_newtype_define_with_instance_id_inner!{
157            $(#[$outer])*
158            $vis $name(Arc<$trait>)
159        }
160    };
161}
162
163#[macro_export]
164macro_rules! _dyn_newtype_define_with_instance_id_inner {
165    (   $(#[$outer:meta])*
166        $vis:vis $name:ident($container:ident<$trait:ident>)
167    ) => {
168        $(#[$outer])*
169        $vis struct $name {
170            module_instance_id: $crate::core::ModuleInstanceId,
171            inner: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)>,
172        }
173
174        impl std::ops::Deref for $name {
175            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
176
177            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
178                &*self.inner
179            }
180
181        }
182
183        impl $name {
184            pub fn module_instance_id(&self) -> ::fedimint_core::core::ModuleInstanceId {
185                self.module_instance_id
186            }
187
188            pub fn from_typed<I>(
189                module_instance_id: ::fedimint_core::core::ModuleInstanceId,
190                typed: I
191            ) -> Self
192            where
193                I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
194
195                Self { inner: $container::new(typed), module_instance_id }
196            }
197
198            pub fn from_parts(
199                module_instance_id: $crate::core::ModuleInstanceId,
200                dynbox: $container<$crate::maybe_add_send_sync!(dyn $trait + 'static)>
201            ) -> Self {
202                Self { inner: dynbox, module_instance_id }
203            }
204        }
205
206        impl std::fmt::Debug for $name {
207            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208                f.debug_struct(stringify!($name))
209                    .field("id", &self.module_instance_id)
210                    .field("kind", &self.module_kind())
211                    .field("inner", &self.inner)
212                    .finish()
213            }
214        }
215    };
216    (   $(#[$outer:meta])*
217        $vis:vis $name:ident<$lifetime:lifetime>($container:ident<$trait:ident>)
218    ) => {
219        $(#[$outer])*
220        $vis struct $name<$lifetime>{ inner: $container<dyn $trait<$lifetime> + Send + $lifetime>, module_instance_id: ModuleInstanceId }
221
222        impl $name {
223            pub fn module_instance_id(&self) -> ::fedimint_core::core::ModuleInstanceId {
224                self.1
225            }
226
227            pub fn from_typed<I>(module_instance_id: ::fedimint_core::core::ModuleInstanceId, typed: I) -> Self
228            where
229                I: $trait + $crate::task::MaybeSend + $crate::task::MaybeSync + 'static {
230
231                Self { inner: $container::new(typed), module_instance_id }
232            }
233        }
234
235        impl<$lifetime> std::ops::Deref for $name<$lifetime> {
236            type Target = $crate::maybe_add_send_sync!(dyn $trait + 'static);
237
238            fn deref(&self) -> &<Self as std::ops::Deref>::Target {
239                &*self.inner
240            }
241        }
242    };
243}
244
245#[macro_export]
246macro_rules! _dyn_newtype_impl_deref_mut {
247    ($name:ident<$lifetime:lifetime>) => {
248        impl<$lifetime> std::ops::DerefMut for $name<$lifetime> {
249            fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
250                &mut *self.inner
251            }
252        }
253    };
254    ($name:ident) => {
255        impl std::ops::DerefMut for $name {
256            fn deref_mut(&mut self) -> &mut <Self as std::ops::Deref>::Target {
257                &mut *self.inner
258            }
259        }
260    };
261}
262
263/// Implement `Clone` on a "dyn newtype"
264///
265/// ... by calling `clone` method on the underlying
266/// `dyn Trait`.
267///
268/// Cloning `dyn Trait`s is non trivial due to object-safety.
269///
270/// Note: the underlying `dyn Trait` needs to implement
271/// a `fn clone(&self) -> Newtype` for this to work,
272/// and this macro does not check or do anything about it.
273///
274/// If the newtype is using `Arc` you probably want
275/// to just use standard `#[derive(Clone)]` to clone
276/// the `Arc` itself.
277#[macro_export]
278macro_rules! dyn_newtype_impl_dyn_clone_passthrough {
279    ($name:ident) => {
280        impl Clone for $name {
281            fn clone(&self) -> Self {
282                self.0.clone()
283            }
284        }
285    };
286}
287
288#[macro_export]
289macro_rules! module_plugin_dyn_newtype_clone_passthrough {
290    ($name:ident) => {
291        impl Clone for $name {
292            fn clone(&self) -> Self {
293                self.inner.clone(self.module_instance_id)
294            }
295        }
296    };
297}
298
299/// Implement `Encodable` and `Decodable` for a "module dyn newtype"
300///
301/// "Module dyn newtype" is just a "dyn newtype" used by general purpose
302/// Fedimint code to abstract away details of mint modules.
303#[macro_export]
304macro_rules! module_plugin_dyn_newtype_encode_decode {
305    ($name:ident) => {
306        impl Encodable for $name {
307            fn consensus_encode<W: std::io::Write>(
308                &self,
309                writer: &mut W,
310            ) -> Result<usize, std::io::Error> {
311                let mut written = self.module_instance_id.consensus_encode(writer)?;
312
313                let mut buf = Vec::with_capacity(512);
314                let buf_written = self.inner.consensus_encode_dyn(&mut buf)?;
315                assert_eq!(buf.len(), buf_written);
316
317                written += buf.consensus_encode(writer)?;
318
319                Ok(written)
320            }
321        }
322
323        impl Decodable for $name {
324            fn consensus_decode_from_finite_reader<R: std::io::Read>(
325                reader: &mut R,
326                decoders: &$crate::module::registry::ModuleDecoderRegistry,
327            ) -> Result<Self, fedimint_core::encoding::DecodeError> {
328                let module_instance_id =
329                    fedimint_core::core::ModuleInstanceId::consensus_decode_from_finite_reader(
330                        reader, decoders,
331                    )?;
332                let val = match decoders.get(module_instance_id) {
333                    Some(decoder) => {
334                        let total_len_u64 =
335                            u64::consensus_decode_from_finite_reader(reader, decoders)?;
336                        decoder.decode_complete(
337                            reader,
338                            total_len_u64,
339                            module_instance_id,
340                            decoders,
341                        )?
342                    }
343                    None => match decoders.decoding_mode() {
344                        $crate::module::registry::DecodingMode::Reject => {
345                            return Err(fedimint_core::encoding::DecodeError::new_custom(
346                                anyhow::anyhow!(
347                                    "Module decoder not available: {module_instance_id} when decoding {}", std::any::type_name::<Self>()
348                                ),
349                            ));
350                        }
351                        $crate::module::registry::DecodingMode::Fallback => $name::from_typed(
352                            module_instance_id,
353                            $crate::core::DynUnknown(
354                                Vec::<u8>::consensus_decode_from_finite_reader(
355                                    reader,
356                                    &Default::default(),
357                                )?,
358                            ),
359                        ),
360                    },
361                };
362
363                Ok(val)
364            }
365        }
366    };
367}
368
369/// Define a "plugin" trait
370///
371/// "Plugin trait" is a trait that a developer of a mint module
372/// needs to implement when implementing mint module. It uses associated
373/// types with trait bounds to guide the developer.
374///
375/// Blanket implementations are used to convert the "plugin trait",
376/// incompatible with `dyn Trait` into "module types" and corresponding
377/// "module dyn newtypes", erasing the exact type and used in a common
378/// Fedimint code.
379#[macro_export]
380macro_rules! module_plugin_static_trait_define{
381    (   $(#[$outer:meta])*
382        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }
383    ) => {
384        pub trait $static_trait:
385            std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + 'static
386        {
387            const KIND : ModuleKind;
388
389            $($extra_methods)*
390        }
391
392        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
393            fn as_any(&self) -> &(dyn Any + Send + Sync) {
394                self
395            }
396
397            fn module_kind(&self) -> Option<ModuleKind> {
398                None
399            }
400
401            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
402                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
403            }
404
405            fn dyn_hash(&self) -> u64 {
406                use std::hash::Hash;
407                let mut s = std::collections::hash_map::DefaultHasher::new();
408                self.hash(&mut s);
409                std::hash::Hasher::finish(&s)
410            }
411
412            $($extra_impls)*
413        }
414
415        impl<T> $dyn_trait for T
416        where
417            T: $static_trait + DynEncodable + 'static + Send + Sync,
418        {
419            fn as_any(&self) -> &(dyn Any + Send + Sync) {
420                self
421            }
422
423            fn module_kind(&self) -> Option<ModuleKind> {
424                Some(<Self as $static_trait>::KIND)
425            }
426
427            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
428                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
429            }
430
431            fn dyn_hash(&self) -> u64 {
432                let mut s = std::collections::hash_map::DefaultHasher::new();
433                self.hash(&mut s);
434                std::hash::Hasher::finish(&s)
435            }
436
437            $($extra_impls)*
438        }
439
440        impl std::hash::Hash for $dyn_newtype {
441            fn hash<H>(&self, state: &mut H)
442            where
443                H: std::hash::Hasher
444            {
445                self.module_instance_id.hash(state);
446                self.inner.dyn_hash().hash(state);
447            }
448        }
449    };
450}
451
452/// A copy of `module_lugin_static_trait_define` but for `ClientConfig`.
453///
454/// `ClientConfig` is a snowflake that requires `: Serialize` and conditional
455/// implementation for `DynUnknown`. The macro is getting gnarly, so seems
456/// easier to copy-paste-modify, than pile up conditional argument.
457#[macro_export]
458macro_rules! module_plugin_static_trait_define_config{
459    (   $(#[$outer:meta])*
460        $dyn_newtype:ident, $static_trait:ident, $dyn_trait:ident, { $($extra_methods:tt)* }, { $($extra_impls:tt)* }, { $($extra_impls_unknown:tt)* }
461    ) => {
462        pub trait $static_trait:
463            std::fmt::Debug + std::fmt::Display + std::cmp::PartialEq + std::hash::Hash + DynEncodable + Decodable + Encodable + Clone + IntoDynInstance<DynType = $dyn_newtype> + Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static
464        {
465            const KIND : ::fedimint_core::core::ModuleKind;
466            $($extra_methods)*
467        }
468
469        impl $dyn_trait for ::fedimint_core::core::DynUnknown {
470            fn as_any(&self) -> &(dyn Any + Send + Sync) {
471                self
472            }
473
474            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
475                None
476            }
477
478            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
479                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
480            }
481
482            fn dyn_hash(&self) -> u64 {
483                use std::hash::Hash;
484                let mut s = std::collections::hash_map::DefaultHasher::new();
485                self.hash(&mut s);
486                std::hash::Hasher::finish(&s)
487            }
488
489            $($extra_impls_unknown)*
490        }
491
492        impl<T> $dyn_trait for T
493        where
494            T: $static_trait + DynEncodable + 'static + Send + Sync,
495        {
496            fn as_any(&self) -> &(dyn Any + Send + Sync) {
497                self
498            }
499
500            fn module_kind(&self) -> Option<::fedimint_core::core::ModuleKind> {
501                Some(<T as $static_trait>::KIND)
502            }
503
504            fn clone(&self, instance_id: ::fedimint_core::core::ModuleInstanceId) -> $dyn_newtype {
505                $dyn_newtype::from_typed(instance_id, <Self as Clone>::clone(self))
506            }
507
508            fn dyn_hash(&self) -> u64 {
509                let mut s = std::collections::hash_map::DefaultHasher::new();
510                self.hash(&mut s);
511                std::hash::Hasher::finish(&s)
512            }
513
514            $($extra_impls)*
515        }
516
517        impl std::hash::Hash for $dyn_newtype {
518            fn hash<H>(&self, state: &mut H)
519            where
520                H: std::hash::Hasher
521            {
522                self.module_instance_id.hash(state);
523                self.inner.dyn_hash().hash(state);
524            }
525        }
526    };
527}
528
529/// Implements the necessary traits for all configuration related types of a
530/// `FederationServer` module.
531#[macro_export]
532macro_rules! plugin_types_trait_impl_config {
533    ($common_gen:ty, $gen:ty, $gen_local:ty, $gen_consensus:ty, $cfg:ty, $cfg_local:ty, $cfg_private:ty, $cfg_consensus:ty, $cfg_client:ty) => {
534        impl fedimint_core::config::ModuleInitParams for $gen {
535            type Local = $gen_local;
536            type Consensus = $gen_consensus;
537
538            fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self {
539                Self { local, consensus }
540            }
541
542            fn to_parts(self) -> (Self::Local, Self::Consensus) {
543                (self.local, self.consensus)
544            }
545        }
546
547        impl fedimint_core::config::TypedServerModuleConsensusConfig for $cfg_consensus {
548            fn kind(&self) -> fedimint_core::core::ModuleKind {
549                <$common_gen as fedimint_core::module::CommonModuleInit>::KIND
550            }
551
552            fn version(&self) -> fedimint_core::module::ModuleConsensusVersion {
553                <$common_gen as fedimint_core::module::CommonModuleInit>::CONSENSUS_VERSION
554            }
555        }
556
557        impl fedimint_core::config::TypedServerModuleConfig for $cfg {
558            type Local = $cfg_local;
559            type Private = $cfg_private;
560            type Consensus = $cfg_consensus;
561
562            fn from_parts(
563                local: Self::Local,
564                private: Self::Private,
565                consensus: Self::Consensus,
566            ) -> Self {
567                Self {
568                    local,
569                    private,
570                    consensus,
571                }
572            }
573
574            fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus) {
575                (
576                    <$common_gen as fedimint_core::module::CommonModuleInit>::KIND,
577                    self.local,
578                    self.private,
579                    self.consensus,
580                )
581            }
582        }
583    };
584}
585
586/// Implements the necessary traits for all associated types of a
587/// `FederationServer` module.
588#[macro_export]
589macro_rules! plugin_types_trait_impl_common {
590    ($kind:expr, $types:ty, $client_config:ty, $input:ty, $output:ty, $outcome:ty, $ci:ty, $input_error:ty, $output_error:ty) => {
591        impl fedimint_core::module::ModuleCommon for $types {
592            type ClientConfig = $client_config;
593            type Input = $input;
594            type Output = $output;
595            type OutputOutcome = $outcome;
596            type ConsensusItem = $ci;
597            type InputError = $input_error;
598            type OutputError = $output_error;
599        }
600
601        impl fedimint_core::core::ClientConfig for $client_config {
602            const KIND: ModuleKind = $kind;
603        }
604
605        impl fedimint_core::core::IntoDynInstance for $client_config {
606            type DynType = fedimint_core::core::DynClientConfig;
607
608            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
609                fedimint_core::core::DynClientConfig::from_typed(instance_id, self)
610            }
611        }
612
613        impl fedimint_core::core::Input for $input {
614            const KIND: ModuleKind = $kind;
615        }
616
617        impl fedimint_core::core::IntoDynInstance for $input {
618            type DynType = fedimint_core::core::DynInput;
619
620            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
621                fedimint_core::core::DynInput::from_typed(instance_id, self)
622            }
623        }
624
625        impl fedimint_core::core::Output for $output {
626            const KIND: ModuleKind = $kind;
627        }
628
629        impl fedimint_core::core::IntoDynInstance for $output {
630            type DynType = fedimint_core::core::DynOutput;
631
632            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
633                fedimint_core::core::DynOutput::from_typed(instance_id, self)
634            }
635        }
636
637        impl fedimint_core::core::OutputOutcome for $outcome {
638            const KIND: ModuleKind = $kind;
639        }
640
641        impl fedimint_core::core::IntoDynInstance for $outcome {
642            type DynType = fedimint_core::core::DynOutputOutcome;
643
644            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
645                fedimint_core::core::DynOutputOutcome::from_typed(instance_id, self)
646            }
647        }
648
649        impl fedimint_core::core::ModuleConsensusItem for $ci {
650            const KIND: ModuleKind = $kind;
651        }
652
653        impl fedimint_core::core::IntoDynInstance for $ci {
654            type DynType = fedimint_core::core::DynModuleConsensusItem;
655
656            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
657                fedimint_core::core::DynModuleConsensusItem::from_typed(instance_id, self)
658            }
659        }
660
661        impl fedimint_core::core::InputError for $input_error {
662            const KIND: ModuleKind = $kind;
663        }
664
665        impl fedimint_core::core::IntoDynInstance for $input_error {
666            type DynType = fedimint_core::core::DynInputError;
667
668            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
669                fedimint_core::core::DynInputError::from_typed(instance_id, self)
670            }
671        }
672
673        impl fedimint_core::core::OutputError for $output_error {
674            const KIND: ModuleKind = $kind;
675        }
676
677        impl fedimint_core::core::IntoDynInstance for $output_error {
678            type DynType = fedimint_core::core::DynOutputError;
679
680            fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
681                fedimint_core::core::DynOutputError::from_typed(instance_id, self)
682            }
683        }
684    };
685}
686
687#[macro_export]
688macro_rules! erased_eq_no_instance_id {
689    ($newtype:ty) => {
690        fn erased_eq_no_instance_id(&self, other: &$newtype) -> bool {
691            let other: &Self = other
692                .as_any()
693                .downcast_ref()
694                .expect("Type is ensured in previous step");
695
696            self == other
697        }
698    };
699}
700
701#[macro_export]
702macro_rules! module_plugin_dyn_newtype_eq_passthrough {
703    ($newtype:ty) => {
704        impl PartialEq for $newtype {
705            fn eq(&self, other: &Self) -> bool {
706                if self.module_instance_id != other.module_instance_id {
707                    return false;
708                }
709                self.erased_eq_no_instance_id(other)
710            }
711        }
712
713        impl Eq for $newtype {}
714    };
715}
716
717#[macro_export]
718macro_rules! module_plugin_dyn_newtype_display_passthrough {
719    ($newtype:ty) => {
720        impl std::fmt::Display for $newtype {
721            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
722                f.write_fmt(format_args!("{}-{}", self.module_instance_id, self.inner))
723            }
724        }
725    };
726}
727
728// TODO: use concat_ident for error name once it lands in stable, see https://github.com/rust-lang/rust/issues/29599
729/// Macro for defining module associated types.
730///
731/// Wraps a type into an enum with a default variant, this allows to add new
732/// versions of the type in the future. Depending on context unknown versions
733/// may be ignored or lead to errors. E.g. the client might just ignore an
734/// unknown input version since it cannot originate from itself while the server
735/// would reject it for not being able to validate its correctness.
736///
737/// Adding extensibility this way is a last line of defense against breaking
738/// changes, most often other ways of introducing new functionality should be
739/// preferred (e.g. new module versions, pure client-side changes, …).
740#[macro_export]
741macro_rules! extensible_associated_module_type {
742    ($name:ident, $name_v0:ident, $error:ident) => {
743        #[derive(
744            Debug,
745            Clone,
746            Eq,
747            PartialEq,
748            Hash,
749            serde::Deserialize,
750            serde::Serialize,
751            fedimint_core::encoding::Encodable,
752            fedimint_core::encoding::Decodable,
753        )]
754        pub enum $name {
755            V0($name_v0),
756            #[encodable_default]
757            Default {
758                variant: u64,
759                bytes: Vec<u8>,
760            },
761        }
762
763        impl $name {
764            pub fn maybe_v0_ref(&self) -> Option<&$name_v0> {
765                match self {
766                    $name::V0(v0) => Some(v0),
767                    $name::Default { .. } => None,
768                }
769            }
770
771            pub fn ensure_v0_ref(&self) -> Result<&$name_v0, $error> {
772                match self {
773                    $name::V0(v0) => Ok(v0),
774                    $name::Default { variant, .. } => Err($error { variant: *variant }),
775                }
776            }
777        }
778
779        #[derive(
780            Debug,
781            thiserror::Error,
782            Clone,
783            Eq,
784            PartialEq,
785            Hash,
786            serde::Deserialize,
787            serde::Serialize,
788            fedimint_core::encoding::Encodable,
789            fedimint_core::encoding::Decodable,
790        )]
791        #[error("Unknown {} variant {variant}", stringify!($name))]
792        pub struct $error {
793            pub variant: u64,
794        }
795
796        impl std::fmt::Display for $name {
797            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798                match self {
799                    $name::V0(inner) => std::fmt::Display::fmt(inner, f),
800                    $name::Default { variant, .. } => {
801                        write!(f, "Unknown {} (variant={variant})", stringify!($name))
802                    }
803                }
804            }
805        }
806    };
807}