Skip to main content

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