fedimint_core/
core.rs

1//! Fedimint Core API (common) module interface
2//!
3//! Fedimint supports externally implemented modules.
4//!
5//! This (Rust) module defines common interoperability types
6//! and functionality that is used on both client and sever side.
7use core::fmt;
8use std::any::{Any, TypeId};
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11use std::fmt::{Debug, Display, Formatter};
12use std::io::Read;
13use std::str::FromStr;
14use std::sync::Arc;
15
16use anyhow::anyhow;
17use bitcoin::hashes::{Hash, sha256};
18use fedimint_core::encoding::{Decodable, DecodeError, DynEncodable, Encodable};
19use fedimint_core::module::registry::ModuleDecoderRegistry;
20use rand::RngCore;
21use serde::{Deserialize, Deserializer, Serialize};
22
23use crate::module::registry::ModuleRegistry;
24use crate::{
25    erased_eq_no_instance_id, module_plugin_dyn_newtype_clone_passthrough,
26    module_plugin_dyn_newtype_define, module_plugin_dyn_newtype_display_passthrough,
27    module_plugin_dyn_newtype_encode_decode, module_plugin_dyn_newtype_eq_passthrough,
28    module_plugin_static_trait_define, module_plugin_static_trait_define_config,
29};
30
31pub mod backup;
32
33/// Unique identifier for one semantic, correlatable operation.
34///
35/// The concept of *operations* is used to avoid losing privacy while being as
36/// efficient as possible with regards to network requests.
37///
38/// For Fedimint transactions to be private users need to communicate with the
39/// federation using an anonymous communication network. If each API request was
40/// done in a way that it cannot be correlated to any other API request we would
41/// achieve privacy, but would reduce efficiency. E.g. on Tor we would need to
42/// open a new circuit for every request and open a new web socket connection.
43///
44/// Fortunately we do not need to do that to maintain privacy. Many API requests
45/// and transactions can be correlated by the federation anyway, in these cases
46/// it does not make any difference to re-use the same network connection. All
47/// requests, transactions, state machines that are connected from the
48/// federation's point of view anyway are grouped together as one *operation*.
49///
50/// # Choice of Operation ID
51///
52/// In cases where an operation is created by a new transaction that's being
53/// submitted the transaction's ID can be used as operation ID. If there is no
54/// transaction related to it, it should be generated randomly. Since it is a
55/// 256bit value collisions are impossible for all intents and purposes.
56#[derive(Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, PartialOrd, Ord)]
57pub struct OperationId(pub [u8; 32]);
58
59pub struct OperationIdFullFmt<'a>(&'a OperationId);
60pub struct OperationIdShortFmt<'a>(&'a OperationId);
61
62impl OperationId {
63    /// Generate random [`OperationId`]
64    pub fn new_random() -> Self {
65        let mut rng = rand::thread_rng();
66        let mut bytes = [0u8; 32];
67        rng.fill_bytes(&mut bytes);
68        Self(bytes)
69    }
70
71    pub fn from_encodable<E: Encodable>(encodable: &E) -> Self {
72        Self(encodable.consensus_hash::<sha256::Hash>().to_byte_array())
73    }
74
75    pub fn fmt_short(&'_ self) -> OperationIdShortFmt<'_> {
76        OperationIdShortFmt(self)
77    }
78    pub fn fmt_full(&'_ self) -> OperationIdFullFmt<'_> {
79        OperationIdFullFmt(self)
80    }
81}
82
83impl Display for OperationIdShortFmt<'_> {
84    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
85        fedimint_core::format_hex(&self.0.0[0..4], f)?;
86        f.write_str("_")?;
87        fedimint_core::format_hex(&self.0.0[28..], f)?;
88        Ok(())
89    }
90}
91
92impl Display for OperationIdFullFmt<'_> {
93    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
94        fedimint_core::format_hex(&self.0.0, f)
95    }
96}
97
98impl Debug for OperationId {
99    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100        write!(f, "OperationId({})", self.fmt_short())
101    }
102}
103
104impl FromStr for OperationId {
105    type Err = anyhow::Error;
106
107    fn from_str(s: &str) -> Result<Self, Self::Err> {
108        let bytes: [u8; 32] = hex::FromHex::from_hex(s)?;
109        Ok(Self(bytes))
110    }
111}
112
113impl Serialize for OperationId {
114    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
115        if serializer.is_human_readable() {
116            serializer.serialize_str(&self.fmt_full().to_string())
117        } else {
118            serializer.serialize_bytes(&self.0)
119        }
120    }
121}
122
123impl<'de> Deserialize<'de> for OperationId {
124    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125    where
126        D: Deserializer<'de>,
127    {
128        if deserializer.is_human_readable() {
129            let s = String::deserialize(deserializer)?;
130            let operation_id = Self::from_str(&s)
131                .map_err(|e| serde::de::Error::custom(format!("invalid operation id: {e}")))?;
132            Ok(operation_id)
133        } else {
134            let bytes: [u8; 32] = <[u8; 32]>::deserialize(deserializer)?;
135            Ok(Self(bytes))
136        }
137    }
138}
139
140/// Module instance ID
141///
142/// This value uniquely identifies a single instance of a module in a
143/// federation.
144///
145/// In case a single [`ModuleKind`] is instantiated twice (rare, but possible),
146/// each instance will have a different id.
147///
148/// Note: We have used this type differently before, assuming each `u16`
149/// uniquly identifies a type of module in question. This function will move
150/// to a `ModuleKind` type which only identifies type of a module (mint vs
151/// wallet vs ln, etc)
152// TODO: turn in a newtype
153pub type ModuleInstanceId = u16;
154
155/// Special IDs we use for global dkg
156pub const MODULE_INSTANCE_ID_GLOBAL: u16 = u16::MAX;
157
158/// A type of a module
159///
160/// This is a short string that identifies type of a module.
161/// Authors of 3rd party modules are free to come up with a string,
162/// long enough to avoid conflicts with similar modules.
163#[derive(PartialEq, Eq, Clone, PartialOrd, Ord, Serialize, Deserialize, Encodable, Decodable)]
164pub struct ModuleKind(Cow<'static, str>);
165
166impl ModuleKind {
167    pub fn clone_from_str(s: &str) -> Self {
168        Self(Cow::from(s.to_owned()))
169    }
170
171    pub const fn from_static_str(s: &'static str) -> Self {
172        Self(Cow::Borrowed(s))
173    }
174
175    pub fn as_str(&self) -> &str {
176        &self.0
177    }
178}
179
180impl fmt::Display for ModuleKind {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        std::fmt::Display::fmt(&self.0, f)
183    }
184}
185
186impl fmt::Debug for ModuleKind {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        std::fmt::Display::fmt(&self.0, f)
189    }
190}
191
192impl From<&'static str> for ModuleKind {
193    fn from(val: &'static str) -> Self {
194        Self::from_static_str(val)
195    }
196}
197
198/// A type used by when decoding dyn-types, when the module is missing
199///
200/// This allows parsing and handling of dyn-types of modules which
201/// are not available.
202#[derive(Debug, Hash, PartialEq, Eq, Clone)]
203pub struct DynUnknown(pub Vec<u8>);
204
205impl fmt::Display for DynUnknown {
206    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
207        f.write_str(&self.0.consensus_encode_to_hex())
208    }
209}
210
211// Note: confusingly, while `DynUnknown` carries a `Vec`
212// it is actually not responsible for writing out the length of the data,
213// as the higher level (`module_plugin_dyn_newtype_encode_decode`) is doing
214// it, based on how many bytes are written here. That's why `DynUnknown` does
215// not implement `Decodable` directly, and `Vec` here has len only
216// for the purpose of knowing how many bytes to carry.
217impl Encodable for DynUnknown {
218    fn consensus_encode<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
219        w.write_all(&self.0[..])?;
220        Ok(())
221    }
222}
223
224/// A type that has a `Dyn*`, type erased version of itself
225pub trait IntoDynInstance {
226    /// The type erased version of the type implementing this trait
227    type DynType: 'static;
228
229    /// Convert `self` into its type-erased equivalent
230    fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType;
231}
232
233type DecodeFn = Box<
234    dyn for<'a> Fn(
235            Box<dyn Read + 'a>,
236            ModuleInstanceId,
237            &ModuleDecoderRegistry,
238        ) -> Result<Box<dyn Any>, DecodeError>
239        + Send
240        + Sync,
241>;
242
243#[derive(Default)]
244pub struct DecoderBuilder {
245    decode_fns: BTreeMap<TypeId, DecodeFn>,
246    transparent: bool,
247}
248
249impl DecoderBuilder {
250    pub fn build(self) -> Decoder {
251        Decoder {
252            decode_fns: Arc::new(self.decode_fns),
253        }
254    }
255
256    /// Attach decoder for a specific `Type`/`DynType` pair where `DynType =
257    /// <Type as IntoDynInstance>::DynType`.
258    ///
259    /// This allows calling `decode::<DynType>` on this decoder, returning a
260    /// `DynType` object which contains a `Type` object internally.
261    ///
262    /// **Caution**: One `Decoder` object should only contain decoders that
263    /// belong to the same [*module kind*](fedimint_core::core::ModuleKind).
264    ///
265    /// # Panics
266    /// * If multiple `Types` with the same `DynType` are added
267    pub fn with_decodable_type<Type>(&mut self)
268    where
269        Type: IntoDynInstance + Decodable,
270    {
271        let is_transparent_decoder = self.transparent;
272        // TODO: enforce that all decoders are for the same module kind (+fix docs
273        // after)
274        let decode_fn: DecodeFn = Box::new(
275            move |mut reader, instance, decoders: &ModuleDecoderRegistry| {
276                // TODO: Ideally `DynTypes` decoding couldn't ever be nested, so we could just
277                // pass empty `decoders`. But the client context uses nested `DynTypes` in
278                // `DynState`, so we special-case it with a flag.
279                let decoders = if is_transparent_decoder {
280                    decoders
281                } else {
282                    &ModuleRegistry::default()
283                };
284                let typed_val =
285                    Type::consensus_decode_partial(&mut reader, decoders).map_err(|err| {
286                        let err: anyhow::Error = err.into();
287                        DecodeError::new_custom(
288                            err.context(format!("while decoding Dyn type module_id={instance}")),
289                        )
290                    })?;
291                let dyn_val = typed_val.into_dyn(instance);
292                let any_val: Box<dyn Any> = Box::new(dyn_val);
293                Ok(any_val)
294            },
295        );
296        if self
297            .decode_fns
298            .insert(TypeId::of::<Type::DynType>(), decode_fn)
299            .is_some()
300        {
301            panic!("Tried to add multiple decoders for the same DynType");
302        }
303    }
304}
305
306/// Consensus encoding decoder for module-specific types
307#[derive(Clone, Default)]
308pub struct Decoder {
309    decode_fns: Arc<BTreeMap<TypeId, DecodeFn>>,
310}
311
312impl Decoder {
313    /// Creates a `DecoderBuilder` to which decoders for single types can be
314    /// attached to build a `Decoder`.
315    pub fn builder() -> DecoderBuilder {
316        DecoderBuilder::default()
317    }
318
319    /// System Dyn-type, don't use.
320    #[doc(hidden)]
321    pub fn builder_system() -> DecoderBuilder {
322        DecoderBuilder {
323            transparent: true,
324            ..DecoderBuilder::default()
325        }
326    }
327
328    /// Decodes a specific `DynType` from the `reader` byte stream.
329    ///
330    /// # Panics
331    /// * If no decoder is registered for the `DynType`
332    pub fn decode_complete<DynType: Any>(
333        &self,
334        reader: &mut dyn Read,
335        total_len: u64,
336        module_id: ModuleInstanceId,
337        decoders: &ModuleDecoderRegistry,
338    ) -> Result<DynType, DecodeError> {
339        let mut reader = reader.take(total_len);
340
341        let val = self.decode_partial(&mut reader, module_id, decoders)?;
342        let left = reader.limit();
343
344        if left != 0 {
345            return Err(fedimint_core::encoding::DecodeError::new_custom(
346                anyhow::anyhow!(
347                    "Dyn type did not consume all bytes during decoding; module_id={}; expected={}; left={}; type={}",
348                    module_id,
349                    total_len,
350                    left,
351                    std::any::type_name::<DynType>(),
352                ),
353            ));
354        }
355
356        Ok(val)
357    }
358
359    /// Like [`Self::decode_complete`] but does not verify that all bytes were
360    /// consumed
361    pub fn decode_partial<DynType: Any>(
362        &self,
363        reader: &mut dyn Read,
364        module_id: ModuleInstanceId,
365        decoders: &ModuleDecoderRegistry,
366    ) -> Result<DynType, DecodeError> {
367        let decode_fn = self
368            .decode_fns
369            .get(&TypeId::of::<DynType>())
370            .ok_or_else(|| {
371                anyhow!(
372                    "Type unknown to decoder: {}, (registered decoders={})",
373                    std::any::type_name::<DynType>(),
374                    self.decode_fns.len()
375                )
376            })
377            .expect("Types being decoded must be registered");
378        Ok(*decode_fn(Box::new(reader), module_id, decoders)?
379            .downcast::<DynType>()
380            .expect("Decode fn returned wrong type, can't happen due to with_decodable_type"))
381    }
382}
383
384impl Debug for Decoder {
385    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
386        write!(f, "Decoder(registered_types = {})", self.decode_fns.len())
387    }
388}
389
390pub trait IClientConfig: Debug + Display + DynEncodable {
391    fn as_any(&self) -> &(dyn Any + Send + Sync);
392    fn module_kind(&self) -> Option<ModuleKind>;
393    fn clone(&self, instance_id: ModuleInstanceId) -> DynClientConfig;
394    fn dyn_hash(&self) -> u64;
395    fn erased_eq_no_instance_id(&self, other: &DynClientConfig) -> bool;
396    fn to_json(&self) -> Option<serde_json::Value>;
397}
398
399module_plugin_static_trait_define_config! {
400    DynClientConfig, ClientConfig, IClientConfig,
401    { },
402    {
403        erased_eq_no_instance_id!(DynClientConfig);
404
405        fn to_json(&self) -> Option<serde_json::Value> {
406            Some(serde_json::to_value(self.to_owned()).expect("serialization can't fail"))
407        }
408    },
409    {
410        erased_eq_no_instance_id!(DynClientConfig);
411
412        fn to_json(&self) -> Option<serde_json::Value> {
413            None
414        }
415    }
416}
417
418module_plugin_dyn_newtype_define! {
419    /// An owned, immutable input to a [`Transaction`](fedimint_core::transaction::Transaction)
420    pub DynClientConfig(Box<IClientConfig>)
421}
422module_plugin_dyn_newtype_encode_decode!(DynClientConfig);
423
424module_plugin_dyn_newtype_clone_passthrough!(DynClientConfig);
425
426module_plugin_dyn_newtype_eq_passthrough!(DynClientConfig);
427
428module_plugin_dyn_newtype_display_passthrough!(DynClientConfig);
429
430/// Something that can be an [`DynInput`] in a
431/// [`Transaction`](fedimint_core::transaction::Transaction)
432///
433/// General purpose code should use [`DynInput`] instead
434pub trait IInput: Debug + Display + DynEncodable {
435    fn as_any(&self) -> &(dyn Any + Send + Sync);
436    fn module_kind(&self) -> Option<ModuleKind>;
437    fn clone(&self, instance_id: ModuleInstanceId) -> DynInput;
438    fn dyn_hash(&self) -> u64;
439    fn erased_eq_no_instance_id(&self, other: &DynInput) -> bool;
440}
441
442module_plugin_static_trait_define! {
443    DynInput, Input, IInput,
444    { },
445    {
446        erased_eq_no_instance_id!(DynInput);
447    }
448}
449
450module_plugin_dyn_newtype_define! {
451    /// An owned, immutable input to a [`Transaction`](fedimint_core::transaction::Transaction)
452    pub DynInput(Box<IInput>)
453}
454module_plugin_dyn_newtype_encode_decode!(DynInput);
455
456module_plugin_dyn_newtype_clone_passthrough!(DynInput);
457
458module_plugin_dyn_newtype_eq_passthrough!(DynInput);
459
460module_plugin_dyn_newtype_display_passthrough!(DynInput);
461
462/// Something that can be an [`DynOutput`] in a
463/// [`Transaction`](fedimint_core::transaction::Transaction)
464///
465/// General purpose code should use [`DynOutput`] instead
466pub trait IOutput: Debug + Display + DynEncodable {
467    fn as_any(&self) -> &(dyn Any + Send + Sync);
468    fn module_kind(&self) -> Option<ModuleKind>;
469    fn clone(&self, instance_id: ModuleInstanceId) -> DynOutput;
470    fn dyn_hash(&self) -> u64;
471    fn erased_eq_no_instance_id(&self, other: &DynOutput) -> bool;
472}
473
474module_plugin_dyn_newtype_define! {
475    /// An owned, immutable output of a [`Transaction`](fedimint_core::transaction::Transaction)
476    pub DynOutput(Box<IOutput>)
477}
478module_plugin_static_trait_define! {
479    DynOutput, Output, IOutput,
480    { },
481    {
482        erased_eq_no_instance_id!(DynOutput);
483    }
484}
485module_plugin_dyn_newtype_encode_decode!(DynOutput);
486
487module_plugin_dyn_newtype_clone_passthrough!(DynOutput);
488
489module_plugin_dyn_newtype_eq_passthrough!(DynOutput);
490
491module_plugin_dyn_newtype_display_passthrough!(DynOutput);
492
493pub enum FinalizationError {
494    SomethingWentWrong,
495}
496
497pub trait IOutputOutcome: Debug + Display + DynEncodable {
498    fn as_any(&self) -> &(dyn Any + Send + Sync);
499    fn module_kind(&self) -> Option<ModuleKind>;
500    fn clone(&self, module_instance_id: ModuleInstanceId) -> DynOutputOutcome;
501    fn dyn_hash(&self) -> u64;
502    fn erased_eq_no_instance_id(&self, other: &DynOutputOutcome) -> bool;
503}
504
505module_plugin_dyn_newtype_define! {
506    /// An owned, immutable output of a [`Transaction`](fedimint_core::transaction::Transaction) before it was finalized
507    pub DynOutputOutcome(Box<IOutputOutcome>)
508}
509module_plugin_static_trait_define! {
510    DynOutputOutcome, OutputOutcome, IOutputOutcome,
511    { },
512    {
513        erased_eq_no_instance_id!(DynOutputOutcome);
514    }
515}
516module_plugin_dyn_newtype_encode_decode!(DynOutputOutcome);
517module_plugin_dyn_newtype_clone_passthrough!(DynOutputOutcome);
518module_plugin_dyn_newtype_eq_passthrough!(DynOutputOutcome);
519module_plugin_dyn_newtype_display_passthrough!(DynOutputOutcome);
520
521pub trait IModuleConsensusItem: Debug + Display + DynEncodable {
522    fn as_any(&self) -> &(dyn Any + Send + Sync);
523    fn module_kind(&self) -> Option<ModuleKind>;
524    fn clone(&self, module_instance_id: ModuleInstanceId) -> DynModuleConsensusItem;
525    fn dyn_hash(&self) -> u64;
526
527    fn erased_eq_no_instance_id(&self, other: &DynModuleConsensusItem) -> bool;
528}
529
530module_plugin_dyn_newtype_define! {
531    /// An owned, immutable output of a [`Transaction`](fedimint_core::transaction::Transaction) before it was finalized
532    pub DynModuleConsensusItem(Box<IModuleConsensusItem>)
533}
534module_plugin_static_trait_define! {
535    DynModuleConsensusItem, ModuleConsensusItem, IModuleConsensusItem,
536    { },
537    {
538        erased_eq_no_instance_id!(DynModuleConsensusItem);
539    }
540}
541module_plugin_dyn_newtype_encode_decode!(DynModuleConsensusItem);
542
543module_plugin_dyn_newtype_clone_passthrough!(DynModuleConsensusItem);
544
545module_plugin_dyn_newtype_eq_passthrough!(DynModuleConsensusItem);
546
547module_plugin_dyn_newtype_display_passthrough!(DynModuleConsensusItem);
548
549pub trait IOutputError: Debug + Display + DynEncodable {
550    fn as_any(&self) -> &(dyn Any + Send + Sync);
551    fn module_kind(&self) -> Option<ModuleKind>;
552    fn clone(&self, module_instance_id: ModuleInstanceId) -> DynOutputError;
553    fn dyn_hash(&self) -> u64;
554
555    fn erased_eq_no_instance_id(&self, other: &DynOutputError) -> bool;
556}
557
558module_plugin_dyn_newtype_define! {
559    pub DynOutputError(Box<IOutputError>)
560}
561module_plugin_static_trait_define! {
562    DynOutputError, OutputError, IOutputError,
563    { },
564    {
565        erased_eq_no_instance_id!(DynOutputError);
566    }
567}
568module_plugin_dyn_newtype_encode_decode!(DynOutputError);
569
570module_plugin_dyn_newtype_clone_passthrough!(DynOutputError);
571
572module_plugin_dyn_newtype_eq_passthrough!(DynOutputError);
573
574module_plugin_dyn_newtype_display_passthrough!(DynOutputError);
575
576pub trait IInputError: Debug + Display + DynEncodable {
577    fn as_any(&self) -> &(dyn Any + Send + Sync);
578    fn module_kind(&self) -> Option<ModuleKind>;
579    fn clone(&self, module_instance_id: ModuleInstanceId) -> DynInputError;
580    fn dyn_hash(&self) -> u64;
581
582    fn erased_eq_no_instance_id(&self, other: &DynInputError) -> bool;
583}
584
585module_plugin_dyn_newtype_define! {
586    pub DynInputError(Box<IInputError>)
587}
588module_plugin_static_trait_define! {
589    DynInputError, InputError, IInputError,
590    { },
591    {
592        erased_eq_no_instance_id!(DynInputError);
593    }
594}
595module_plugin_dyn_newtype_encode_decode!(DynInputError);
596
597module_plugin_dyn_newtype_clone_passthrough!(DynInputError);
598
599module_plugin_dyn_newtype_eq_passthrough!(DynInputError);
600
601module_plugin_dyn_newtype_display_passthrough!(DynInputError);