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