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