/ fedimint-core / src / config.rs
config.rs
   1  use std::collections::{BTreeMap, BTreeSet};
   2  use std::fmt::{Debug, Display};
   3  use std::hash::Hash;
   4  use std::ops::Mul;
   5  use std::path::Path;
   6  use std::str::FromStr;
   7  
   8  use anyhow::{bail, format_err, Context};
   9  use bitcoin29::hashes::hex::format_hex;
  10  use bitcoin_hashes::sha256::{Hash as Sha256, HashEngine};
  11  use bitcoin_hashes::{hex, sha256};
  12  use bls12_381::Scalar;
  13  use fedimint_core::core::{ModuleInstanceId, ModuleKind};
  14  use fedimint_core::encoding::{DynRawFallback, Encodable};
  15  use fedimint_core::module::registry::ModuleRegistry;
  16  use fedimint_core::task::Cancelled;
  17  use fedimint_core::util::SafeUrl;
  18  use fedimint_core::{BitcoinHash, ModuleDecoderRegistry};
  19  use fedimint_logging::LOG_CORE;
  20  use hex::FromHex;
  21  use serde::de::DeserializeOwned;
  22  use serde::ser::SerializeMap;
  23  use serde::{Deserialize, Deserializer, Serialize, Serializer};
  24  use thiserror::Error;
  25  use threshold_crypto::group::{Curve, Group, GroupEncoding};
  26  use threshold_crypto::{G1Projective, G2Projective};
  27  use tracing::warn;
  28  
  29  use crate::core::DynClientConfig;
  30  use crate::encoding::Decodable;
  31  use crate::invite_code::InviteCode;
  32  use crate::module::{
  33      CoreConsensusVersion, DynCommonModuleInit, DynServerModuleInit, IDynCommonModuleInit,
  34      ModuleConsensusVersion,
  35  };
  36  use crate::{bls12_381_serde, maybe_add_send_sync, PeerId};
  37  
  38  // TODO: make configurable
  39  /// This limits the RAM consumption of a AlephBFT Unit to roughly 50kB
  40  pub const ALEPH_BFT_UNIT_BYTE_LIMIT: usize = 50_000;
  41  
  42  /// [`serde_json::Value`] that must contain `kind: String` field
  43  ///
  44  /// TODO: enforce at ser/deserialization
  45  /// TODO: make inside prive and enforce `kind` on construction, to
  46  /// other functions non-falliable
  47  #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
  48  pub struct JsonWithKind {
  49      kind: ModuleKind,
  50      #[serde(flatten)]
  51      value: serde_json::Value,
  52  }
  53  
  54  impl JsonWithKind {
  55      pub fn new(kind: ModuleKind, value: serde_json::Value) -> Self {
  56          Self { kind, value }
  57      }
  58  
  59      /// Workaround for a serde `flatten` quirk
  60      ///
  61      /// We serialize config with no fields as: eg. `{ kind: "ln" }`.
  62      ///
  63      /// When `kind` gets removed and `value` is parsed, it will
  64      /// parse as `Value::Object` that is empty.
  65      ///
  66      /// However empty module structs, like `struct FooConfigLocal;` (unit
  67      /// struct), will fail to deserialize with this value, as they expect
  68      /// `Value::Null`.
  69      ///
  70      /// We can turn manually empty object into null, and that's what
  71      /// we do in this function. This fixes the deserialization into
  72      /// unit type, but in turn breaks deserialization into `struct Foo{}`,
  73      /// which is arguably much less common, but valid.
  74      ///
  75      /// TODO: In the future, we should have a typed and erased versions of
  76      /// module construction traits, and then we can try with and
  77      /// without the workaround to have both cases working.
  78      /// See <https://github.com/fedimint/fedimint/issues/1303>
  79      pub fn with_fixed_empty_value(self) -> Self {
  80          if let serde_json::Value::Object(ref o) = self.value {
  81              if o.is_empty() {
  82                  return Self {
  83                      kind: self.kind,
  84                      value: serde_json::Value::Null,
  85                  };
  86              }
  87          }
  88  
  89          self
  90      }
  91  
  92      pub fn value(&self) -> &serde_json::Value {
  93          &self.value
  94      }
  95  
  96      pub fn kind(&self) -> &ModuleKind {
  97          &self.kind
  98      }
  99  
 100      pub fn is_kind(&self, kind: &ModuleKind) -> bool {
 101          &self.kind == kind
 102      }
 103  }
 104  
 105  #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
 106  pub struct PeerUrl {
 107      /// The peer's public URL (e.g. `wss://fedimint-server-1:5000`)
 108      pub url: SafeUrl,
 109      /// The peer's name
 110      pub name: String,
 111  }
 112  
 113  /// Total client config
 114  ///
 115  /// This includes global settings and client-side module configs.
 116  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
 117  pub struct ClientConfig {
 118      #[serde(flatten)]
 119      pub global: GlobalClientConfig,
 120      #[serde(deserialize_with = "de_int_key")]
 121      pub modules: BTreeMap<ModuleInstanceId, ClientModuleConfig>,
 122  }
 123  
 124  // FIXME: workaround for https://github.com/serde-rs/json/issues/989
 125  fn de_int_key<'de, D, K, V>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
 126  where
 127      D: Deserializer<'de>,
 128      K: Eq + Ord + FromStr,
 129      K::Err: Display,
 130      V: Deserialize<'de>,
 131  {
 132      let string_map = <BTreeMap<String, V>>::deserialize(deserializer)?;
 133      let map = string_map
 134          .into_iter()
 135          .map(|(key_str, value)| {
 136              let key = K::from_str(&key_str).map_err(serde::de::Error::custom)?;
 137              Ok((key, value))
 138          })
 139          .collect::<Result<BTreeMap<_, _>, _>>()?;
 140      Ok(map)
 141  }
 142  
 143  /// Client config that cannot be cryptographically verified but is easier to
 144  /// parse by external tools
 145  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
 146  pub struct JsonClientConfig {
 147      pub global: GlobalClientConfig,
 148      pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
 149  }
 150  
 151  /// Federation-wide client config
 152  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
 153  pub struct GlobalClientConfig {
 154      /// API endpoints for each federation member
 155      #[serde(deserialize_with = "de_int_key")]
 156      pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
 157      /// Core consensus version
 158      pub consensus_version: CoreConsensusVersion,
 159      // TODO: make it a String -> serde_json::Value map?
 160      /// Additional config the federation wants to transmit to the clients
 161      pub meta: BTreeMap<String, String>,
 162  }
 163  
 164  impl GlobalClientConfig {
 165      pub fn calculate_federation_id(&self) -> FederationId {
 166          FederationId(self.api_endpoints.consensus_hash())
 167      }
 168  
 169      /// Federation name from config metadata (if set)
 170      pub fn federation_name(&self) -> Option<&str> {
 171          self.meta.get(META_FEDERATION_NAME_KEY).map(|x| &**x)
 172      }
 173  }
 174  
 175  impl ClientConfig {
 176      /// See [`DynRawFallback::redecode_raw`].
 177      pub fn redecode_raw(
 178          self,
 179          modules: &ModuleDecoderRegistry,
 180      ) -> Result<Self, crate::encoding::DecodeError> {
 181          Ok(Self {
 182              modules: self
 183                  .modules
 184                  .into_iter()
 185                  .map(|(k, v)| {
 186                      // Assuming this isn't running in any hot path it's better to have the debug
 187                      // info than saving one allocation
 188                      let kind = v.kind.clone();
 189                      v.redecode_raw(modules)
 190                          .context(format!("redecode_raw: instance: {k}, kind: {kind}"))
 191                          .map(|v| (k, v))
 192                  })
 193                  .collect::<Result<_, _>>()?,
 194              ..self
 195          })
 196      }
 197  
 198      pub fn calculate_federation_id(&self) -> FederationId {
 199          self.global.calculate_federation_id()
 200      }
 201  
 202      /// Get the value of a given meta field
 203      pub fn meta<V: serde::de::DeserializeOwned + 'static>(
 204          &self,
 205          key: &str,
 206      ) -> Result<Option<V>, anyhow::Error> {
 207          let Some(str_value) = self.global.meta.get(key) else {
 208              return Ok(None);
 209          };
 210          let res = serde_json::from_str(str_value)
 211              .map(Some)
 212              .context(format!("Decoding meta field '{key}' failed"));
 213  
 214          // In the past we encoded some string fields as "just a string" without quotes,
 215          // this code ensures that old meta values still parse since config is hard to
 216          // change
 217          if res.is_err() && std::any::TypeId::of::<V>() == std::any::TypeId::of::<String>() {
 218              let string_ret = Box::new(str_value.clone());
 219              let ret = unsafe {
 220                  // We can transmute a String to V because we know that V==String
 221                  std::mem::transmute::<Box<String>, Box<V>>(string_ret)
 222              };
 223              Ok(Some(*ret))
 224          } else {
 225              res
 226          }
 227      }
 228  
 229      /// Create an invite code with the api endpoint of the given peer which can
 230      /// be used to download this client config
 231      pub fn invite_code(&self, peer: &PeerId) -> Option<InviteCode> {
 232          self.global.api_endpoints.get(peer).map(|peer_url| {
 233              InviteCode::new(peer_url.url.clone(), *peer, self.calculate_federation_id())
 234          })
 235      }
 236  }
 237  
 238  /// The federation id is a copy of the authentication threshold public key of
 239  /// the federation
 240  ///
 241  /// Stable id so long as guardians membership does not change
 242  /// Unique id so long as guardians do not all collude
 243  #[derive(
 244      Debug,
 245      Copy,
 246      Serialize,
 247      Deserialize,
 248      Clone,
 249      Eq,
 250      Hash,
 251      PartialEq,
 252      Encodable,
 253      Decodable,
 254      Ord,
 255      PartialOrd,
 256  )]
 257  pub struct FederationId(pub sha256::Hash);
 258  
 259  #[derive(
 260      Debug,
 261      Copy,
 262      Serialize,
 263      Deserialize,
 264      Clone,
 265      Eq,
 266      Hash,
 267      PartialEq,
 268      Encodable,
 269      Decodable,
 270      Ord,
 271      PartialOrd,
 272  )]
 273  /// Prefix of the [`FederationId`], useful for UX improvements
 274  ///
 275  /// Intentionally compact to save on the encoding. With 4 billion
 276  /// combinations real-life non-malicious collisions should never
 277  /// happen.
 278  pub struct FederationIdPrefix([u8; 4]);
 279  
 280  impl Display for FederationIdPrefix {
 281      fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 282          format_hex(&self.0, f)
 283      }
 284  }
 285  
 286  impl Display for FederationId {
 287      fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 288          format_hex(&self.0.to_byte_array(), f)
 289      }
 290  }
 291  
 292  impl FromStr for FederationIdPrefix {
 293      type Err = anyhow::Error;
 294  
 295      fn from_str(s: &str) -> Result<Self, Self::Err> {
 296          Ok(Self(<[u8; 4]>::from_hex(s)?))
 297      }
 298  }
 299  
 300  /// Display as a hex encoding
 301  impl FederationId {
 302      /// Random dummy id for testing
 303      pub fn dummy() -> Self {
 304          Self(sha256::Hash::from_byte_array([42; 32]))
 305      }
 306  
 307      pub(crate) fn from_byte_array(bytes: [u8; 32]) -> Self {
 308          Self(sha256::Hash::from_byte_array(bytes))
 309      }
 310  
 311      pub fn to_prefix(&self) -> FederationIdPrefix {
 312          FederationIdPrefix(self.0[..4].try_into().expect("can't fail"))
 313      }
 314  
 315      /// Converts a federation id to a public key to which we know but discard
 316      /// the private key.
 317      ///
 318      /// Clients MUST never use this private key for any signing operations!
 319      ///
 320      /// That is ok because we only use the public key for adding a route
 321      /// hint to LN invoices that tells fedimint clients that the invoice can
 322      /// only be paid internally. Since no LN node with that pub key can exist
 323      /// other LN senders will know that they cannot pay the invoice.
 324      pub fn to_fake_ln_pub_key(
 325          &self,
 326          secp: &secp256k1::Secp256k1<secp256k1::All>,
 327      ) -> anyhow::Result<secp256k1::PublicKey> {
 328          let sk = secp256k1::SecretKey::from_slice(&self.0.to_byte_array())?;
 329          Ok(secp256k1::PublicKey::from_secret_key(secp, &sk))
 330      }
 331  }
 332  
 333  impl FromStr for FederationId {
 334      type Err = anyhow::Error;
 335  
 336      fn from_str(s: &str) -> Result<Self, Self::Err> {
 337          Ok(Self::from_byte_array(<[u8; 32]>::from_hex(s)?))
 338      }
 339  }
 340  
 341  impl ClientConfig {
 342      /// Returns the consensus hash for a given client config
 343      pub fn consensus_hash(&self) -> sha256::Hash {
 344          let mut engine = HashEngine::default();
 345          self.consensus_encode(&mut engine)
 346              .expect("Consensus hashing should never fail");
 347          sha256::Hash::from_engine(engine)
 348      }
 349  
 350      pub fn get_module<T: Decodable + 'static>(&self, id: ModuleInstanceId) -> anyhow::Result<&T> {
 351          if let Some(client_cfg) = self.modules.get(&id) {
 352              client_cfg.cast()
 353          } else {
 354              Err(format_err!("Client config for module id {id} not found"))
 355          }
 356      }
 357  
 358      // TODO: rename this and one above
 359      pub fn get_module_cfg(&self, id: ModuleInstanceId) -> anyhow::Result<ClientModuleConfig> {
 360          if let Some(client_cfg) = self.modules.get(&id) {
 361              Ok(client_cfg.clone())
 362          } else {
 363              Err(format_err!("Client config for module id {id} not found"))
 364          }
 365      }
 366  
 367      /// (soft-deprecated): Get the first instance of a module of a given kind in
 368      /// defined in config
 369      ///
 370      /// Since module ids are numerical and for time being we only support 1:1
 371      /// mint, wallet, ln module code in the client, this is useful, but
 372      /// please write any new code that avoids assumptions about available
 373      /// modules.
 374      pub fn get_first_module_by_kind<T: Decodable + 'static>(
 375          &self,
 376          kind: impl Into<ModuleKind>,
 377      ) -> anyhow::Result<(ModuleInstanceId, &T)> {
 378          let kind: ModuleKind = kind.into();
 379          let Some((id, module_cfg)) = self.modules.iter().find(|(_, v)| v.is_kind(&kind)) else {
 380              anyhow::bail!("Module kind {kind} not found")
 381          };
 382          Ok((*id, module_cfg.cast()?))
 383      }
 384  
 385      // TODO: rename this and above
 386      pub fn get_first_module_by_kind_cfg(
 387          &self,
 388          kind: impl Into<ModuleKind>,
 389      ) -> anyhow::Result<(ModuleInstanceId, ClientModuleConfig)> {
 390          let kind: ModuleKind = kind.into();
 391          self.modules
 392              .iter()
 393              .find(|(_, v)| v.is_kind(&kind))
 394              .map(|(id, v)| (*id, v.clone()))
 395              .ok_or_else(|| anyhow::format_err!("Module kind {kind} not found"))
 396      }
 397  }
 398  
 399  #[derive(Clone, Debug)]
 400  pub struct ModuleInitRegistry<M>(BTreeMap<ModuleKind, M>);
 401  
 402  impl<M> Default for ModuleInitRegistry<M> {
 403      fn default() -> Self {
 404          Self(Default::default())
 405      }
 406  }
 407  
 408  /// Type erased `ModuleInitParams` used to generate the `ServerModuleConfig`
 409  /// during config gen
 410  #[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
 411  pub struct ConfigGenModuleParams {
 412      pub local: serde_json::Value,
 413      pub consensus: serde_json::Value,
 414  }
 415  
 416  pub type ServerModuleInitRegistry = ModuleInitRegistry<DynServerModuleInit>;
 417  
 418  impl ConfigGenModuleParams {
 419      pub fn new(local: serde_json::Value, consensus: serde_json::Value) -> Self {
 420          Self { local, consensus }
 421      }
 422  
 423      /// Converts the JSON into typed version, errors unless both `local` and
 424      /// `consensus` values are defined
 425      pub fn to_typed<P: ModuleInitParams>(&self) -> anyhow::Result<P> {
 426          Ok(P::from_parts(
 427              Self::parse("local", self.local.clone())?,
 428              Self::parse("consensus", self.consensus.clone())?,
 429          ))
 430      }
 431  
 432      fn parse<P: DeserializeOwned>(name: &str, json: serde_json::Value) -> anyhow::Result<P> {
 433          serde_json::from_value(json).with_context(|| format!("Schema mismatch for {name} argument"))
 434      }
 435  
 436      pub fn from_typed<P: ModuleInitParams>(p: P) -> anyhow::Result<Self> {
 437          let (local, consensus) = p.to_parts();
 438          Ok(Self {
 439              local: serde_json::to_value(local)?,
 440              consensus: serde_json::to_value(consensus)?,
 441          })
 442      }
 443  }
 444  
 445  pub type CommonModuleInitRegistry = ModuleInitRegistry<DynCommonModuleInit>;
 446  
 447  /// Registry that contains the config gen params for all modules
 448  pub type ServerModuleConfigGenParamsRegistry = ModuleRegistry<ConfigGenModuleParams>;
 449  
 450  impl Eq for ServerModuleConfigGenParamsRegistry {}
 451  
 452  impl PartialEq for ServerModuleConfigGenParamsRegistry {
 453      fn eq(&self, other: &Self) -> bool {
 454          self.iter_modules().eq(other.iter_modules())
 455      }
 456  }
 457  
 458  impl Serialize for ServerModuleConfigGenParamsRegistry {
 459      fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
 460          let modules: Vec<_> = self.iter_modules().collect();
 461          let mut serializer = serializer.serialize_map(Some(modules.len()))?;
 462          for (id, kind, params) in modules.into_iter() {
 463              serializer.serialize_key(&id)?;
 464              serializer.serialize_value(&(kind.clone(), params.clone()))?;
 465          }
 466          serializer.end()
 467      }
 468  }
 469  
 470  impl<'de> Deserialize<'de> for ServerModuleConfigGenParamsRegistry {
 471      fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 472      where
 473          D: Deserializer<'de>,
 474      {
 475          let json: BTreeMap<ModuleInstanceId, (ModuleKind, ConfigGenModuleParams)> =
 476              Deserialize::deserialize(deserializer)?;
 477          let mut params = BTreeMap::new();
 478  
 479          for (id, (kind, module)) in json {
 480              params.insert(id, (kind, module));
 481          }
 482          Ok(ModuleRegistry::from(params))
 483      }
 484  }
 485  
 486  impl<M> From<Vec<M>> for ModuleInitRegistry<M>
 487  where
 488      M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
 489  {
 490      fn from(value: Vec<M>) -> Self {
 491          Self(BTreeMap::from_iter(
 492              value.into_iter().map(|i| (i.as_ref().module_kind(), i)),
 493          ))
 494      }
 495  }
 496  
 497  impl<M> FromIterator<M> for ModuleInitRegistry<M>
 498  where
 499      M: AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
 500  {
 501      fn from_iter<T: IntoIterator<Item = M>>(iter: T) -> Self {
 502          Self(BTreeMap::from_iter(
 503              iter.into_iter().map(|i| (i.as_ref().module_kind(), i)),
 504          ))
 505      }
 506  }
 507  
 508  impl<M> ModuleInitRegistry<M> {
 509      pub fn new() -> Self {
 510          Default::default()
 511      }
 512  
 513      pub fn attach<T>(&mut self, gen: T)
 514      where
 515          T: Into<M> + 'static + Send + Sync,
 516          M: AsRef<dyn IDynCommonModuleInit + 'static + Send + Sync>,
 517      {
 518          let gen: M = gen.into();
 519          let kind = gen.as_ref().module_kind();
 520          if self.0.insert(kind.clone(), gen).is_some() {
 521              panic!("Can't insert module of same kind twice: {kind}");
 522          }
 523      }
 524  
 525      pub fn kinds(&self) -> BTreeSet<ModuleKind> {
 526          self.0.keys().cloned().collect()
 527      }
 528  
 529      pub fn get(&self, k: &ModuleKind) -> Option<&M> {
 530          self.0.get(k)
 531      }
 532  }
 533  
 534  impl ModuleRegistry<ConfigGenModuleParams> {
 535      pub fn attach_config_gen_params_by_id<T: ModuleInitParams>(
 536          &mut self,
 537          id: ModuleInstanceId,
 538          kind: ModuleKind,
 539          gen: T,
 540      ) -> &mut Self {
 541          let params = ConfigGenModuleParams::from_typed(gen)
 542              .unwrap_or_else(|err| panic!("Invalid config gen params for {kind}: {err}"));
 543          self.register_module(id, kind, params);
 544          self
 545      }
 546  
 547      pub fn attach_config_gen_params<T: ModuleInitParams>(
 548          &mut self,
 549          kind: ModuleKind,
 550          gen: T,
 551      ) -> &mut Self {
 552          let params = ConfigGenModuleParams::from_typed(gen)
 553              .unwrap_or_else(|err| panic!("Invalid config gen params for {kind}: {err}"));
 554          self.append_module(kind, params);
 555          self
 556      }
 557  }
 558  
 559  impl ServerModuleInitRegistry {
 560      pub fn to_common(&self) -> CommonModuleInitRegistry {
 561          ModuleInitRegistry(
 562              self.0
 563                  .iter()
 564                  .map(|(k, v)| (k.clone(), v.to_dyn_common()))
 565                  .collect(),
 566          )
 567      }
 568  }
 569  
 570  impl<M> ModuleInitRegistry<M>
 571  where
 572      M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
 573  {
 574      #[deprecated(
 575          note = "You probably want `available_decoders` to support missing module kinds. If you really want a strict behavior, use `decoders_strict`"
 576      )]
 577      pub fn decoders<'a>(
 578          &self,
 579          modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
 580      ) -> anyhow::Result<ModuleDecoderRegistry> {
 581          self.decoders_strict(modules)
 582      }
 583  
 584      /// Get decoders for `modules` and fail if any is unsupported
 585      pub fn decoders_strict<'a>(
 586          &self,
 587          modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
 588      ) -> anyhow::Result<ModuleDecoderRegistry> {
 589          let mut decoders = BTreeMap::new();
 590          for (id, kind) in modules {
 591              let Some(init) = self.0.get(kind) else {
 592                  anyhow::bail!(
 593                      "Detected configuration for unsupported module id: {id}, kind: {kind}"
 594                  )
 595              };
 596  
 597              decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
 598          }
 599          Ok(ModuleDecoderRegistry::from(decoders))
 600      }
 601  
 602      /// Get decoders for `modules` and skip unsupported ones
 603      pub fn available_decoders<'a>(
 604          &self,
 605          modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
 606      ) -> anyhow::Result<ModuleDecoderRegistry> {
 607          let mut decoders = BTreeMap::new();
 608          for (id, kind) in modules {
 609              let Some(init) = self.0.get(kind) else {
 610                  warn!(target: LOG_CORE, "Unsupported module id: {id}, kind: {kind}");
 611                  continue;
 612              };
 613  
 614              decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
 615          }
 616          Ok(ModuleDecoderRegistry::from(decoders))
 617      }
 618  }
 619  
 620  /// Empty struct for if there are no params
 621  #[derive(Debug, Default, Clone, Serialize, Deserialize)]
 622  pub struct EmptyGenParams {}
 623  
 624  pub trait ModuleInitParams: serde::Serialize + serde::de::DeserializeOwned {
 625      /// Locally configurable parameters for config generation
 626      type Local: DeserializeOwned + Serialize;
 627      /// Consensus parameters for config generation
 628      type Consensus: DeserializeOwned + Serialize;
 629  
 630      /// Assemble from the distinct parts
 631      fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self;
 632  
 633      /// Split the config into its distinct parts
 634      fn to_parts(self) -> (Self::Local, Self::Consensus);
 635  }
 636  
 637  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
 638  pub struct ServerModuleConsensusConfig {
 639      pub kind: ModuleKind,
 640      pub version: ModuleConsensusVersion,
 641      #[serde(with = "::hex::serde")]
 642      pub config: Vec<u8>,
 643  }
 644  
 645  /// Config for the client-side of a particular Federation module
 646  ///
 647  /// Since modules are (tbd.) pluggable into Federations,
 648  /// it needs to be some form of an abstract type-erased-like
 649  /// value.
 650  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
 651  pub struct ClientModuleConfig {
 652      pub kind: ModuleKind,
 653      pub version: ModuleConsensusVersion,
 654      #[serde(with = "::fedimint_core::encoding::as_hex")]
 655      pub config: DynRawFallback<DynClientConfig>,
 656  }
 657  
 658  impl ClientModuleConfig {
 659      pub fn from_typed<T: fedimint_core::core::ClientConfig>(
 660          module_instance_id: ModuleInstanceId,
 661          kind: ModuleKind,
 662          version: ModuleConsensusVersion,
 663          value: T,
 664      ) -> anyhow::Result<Self> {
 665          Ok(Self {
 666              kind,
 667              version,
 668              config: fedimint_core::core::DynClientConfig::from_typed(module_instance_id, value)
 669                  .into(),
 670          })
 671      }
 672  
 673      pub fn redecode_raw(
 674          self,
 675          modules: &ModuleDecoderRegistry,
 676      ) -> Result<Self, crate::encoding::DecodeError> {
 677          Ok(Self {
 678              config: self.config.redecode_raw(modules)?,
 679              ..self
 680          })
 681      }
 682  
 683      pub fn is_kind(&self, kind: &ModuleKind) -> bool {
 684          &self.kind == kind
 685      }
 686  
 687      pub fn kind(&self) -> &ModuleKind {
 688          &self.kind
 689      }
 690  }
 691  
 692  impl ClientModuleConfig {
 693      pub fn cast<T>(&self) -> anyhow::Result<&T>
 694      where
 695          T: 'static,
 696      {
 697          self.config
 698              .expect_decoded_ref()
 699              .as_any()
 700              .downcast_ref::<T>()
 701              .context("can't convert client module config to desired type")
 702      }
 703  }
 704  
 705  /// Config for the server-side of a particular Federation module
 706  ///
 707  /// See [`ClientModuleConfig`].
 708  #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
 709  pub struct ServerModuleConfig {
 710      pub local: JsonWithKind,
 711      pub private: JsonWithKind,
 712      pub consensus: ServerModuleConsensusConfig,
 713      pub consensus_json: JsonWithKind,
 714  }
 715  
 716  impl ServerModuleConfig {
 717      pub fn from(
 718          local: JsonWithKind,
 719          private: JsonWithKind,
 720          consensus: ServerModuleConsensusConfig,
 721          consensus_json: JsonWithKind,
 722      ) -> Self {
 723          Self {
 724              local,
 725              private,
 726              consensus,
 727              consensus_json,
 728          }
 729      }
 730  
 731      pub fn to_typed<T: TypedServerModuleConfig>(&self) -> anyhow::Result<T> {
 732          let local = serde_json::from_value(self.local.value().clone())?;
 733          let private = serde_json::from_value(self.private.value().clone())?;
 734          let consensus =
 735              <T::Consensus>::consensus_decode(&mut &self.consensus.config[..], &Default::default())?;
 736  
 737          Ok(TypedServerModuleConfig::from_parts(
 738              local, private, consensus,
 739          ))
 740      }
 741  }
 742  
 743  /// Consensus-critical part of a server side module config
 744  pub trait TypedServerModuleConsensusConfig:
 745      DeserializeOwned + Serialize + Encodable + Decodable
 746  {
 747      fn kind(&self) -> ModuleKind;
 748  
 749      fn version(&self) -> ModuleConsensusVersion;
 750  
 751      fn from_erased(erased: &ServerModuleConsensusConfig) -> anyhow::Result<Self> {
 752          Ok(Self::consensus_decode(
 753              &mut &erased.config[..],
 754              &Default::default(),
 755          )?)
 756      }
 757  }
 758  
 759  /// Module (server side) config, typed
 760  pub trait TypedServerModuleConfig: DeserializeOwned + Serialize {
 761      /// Local non-consensus, not security-sensitive settings
 762      type Local: DeserializeOwned + Serialize;
 763      /// Private for this federation member data that are security sensitive and
 764      /// will be encrypted at rest
 765      type Private: DeserializeOwned + Serialize;
 766      /// Shared consensus-critical config
 767      type Consensus: TypedServerModuleConsensusConfig;
 768  
 769      /// Assemble from the three functionally distinct parts
 770      fn from_parts(local: Self::Local, private: Self::Private, consensus: Self::Consensus) -> Self;
 771  
 772      /// Split the config into its three functionally distinct parts
 773      fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus);
 774  
 775      /// Turn the typed config into type-erased version
 776      fn to_erased(self) -> ServerModuleConfig {
 777          let (kind, local, private, consensus) = self.to_parts();
 778  
 779          ServerModuleConfig {
 780              local: JsonWithKind::new(
 781                  kind.clone(),
 782                  serde_json::to_value(local).expect("serialization can't fail"),
 783              ),
 784              private: JsonWithKind::new(
 785                  kind.clone(),
 786                  serde_json::to_value(private).expect("serialization can't fail"),
 787              ),
 788              consensus: ServerModuleConsensusConfig {
 789                  kind: consensus.kind(),
 790                  version: consensus.version(),
 791                  config: consensus.consensus_encode_to_vec(),
 792              },
 793              consensus_json: JsonWithKind::new(
 794                  kind,
 795                  serde_json::to_value(consensus).expect("serialization can't fail"),
 796              ),
 797          }
 798      }
 799  }
 800  
 801  /// Things that a `distributed_gen` config can send between peers
 802  #[derive(Serialize, Deserialize, Debug, Clone)]
 803  pub enum DkgPeerMsg {
 804      PublicKey(secp256k1::PublicKey),
 805      DistributedGen(SupportedDkgMessage),
 806      Module(Vec<u8>),
 807      // Dkg completed on our side
 808      Done,
 809  }
 810  
 811  /// Result of running DKG
 812  pub type DkgResult<T> = Result<T, DkgError>;
 813  
 814  #[derive(Error, Debug)]
 815  /// Captures an error occurring in DKG
 816  pub enum DkgError {
 817      /// User has cancelled the DKG task
 818      #[error("Operation cancelled")]
 819      Cancelled(#[from] Cancelled),
 820      /// Error running DKG
 821      #[error("Running DKG failed due to {0}")]
 822      Failed(#[from] anyhow::Error),
 823      #[error("The module was not found {0}")]
 824      ModuleNotFound(ModuleKind),
 825      #[error("Params for modules were not found {0:?}")]
 826      ParamsNotFound(BTreeSet<ModuleKind>),
 827      #[error("Failed to decode module message {0:?}")]
 828      ModuleDecodeError(ModuleKind),
 829  }
 830  
 831  /// Supported (by Fedimint's code) `DkgMessage<T>` types
 832  ///
 833  /// Since `DkgMessage` is an open-set, yet we only use a subset of it,
 834  /// we can make a subset-trait to convert it to an `enum` that we
 835  /// it's easier to handle.
 836  ///
 837  /// Candidate for refactoring after modularization effort is complete.
 838  pub trait ISupportedDkgMessage: Sized + Serialize + DeserializeOwned {
 839      fn to_msg(self) -> SupportedDkgMessage;
 840      fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self>;
 841  }
 842  
 843  /// `enum` version of [`SupportedDkgMessage`]
 844  #[derive(Serialize, Deserialize, Debug, Clone)]
 845  pub enum SupportedDkgMessage {
 846      G1(DkgMessage<G1Projective>),
 847      G2(DkgMessage<G2Projective>),
 848  }
 849  
 850  impl ISupportedDkgMessage for DkgMessage<G1Projective> {
 851      fn to_msg(self) -> SupportedDkgMessage {
 852          SupportedDkgMessage::G1(self)
 853      }
 854  
 855      fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self> {
 856          match msg {
 857              SupportedDkgMessage::G1(s) => Ok(s),
 858              SupportedDkgMessage::G2(_) => bail!("Incorrect DkgGroup: G2"),
 859          }
 860      }
 861  }
 862  
 863  impl ISupportedDkgMessage for DkgMessage<G2Projective> {
 864      fn to_msg(self) -> SupportedDkgMessage {
 865          SupportedDkgMessage::G2(self)
 866      }
 867  
 868      fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self> {
 869          match msg {
 870              SupportedDkgMessage::G1(_) => bail!("Incorrect DkgGroup: G1"),
 871              SupportedDkgMessage::G2(s) => Ok(s),
 872          }
 873      }
 874  }
 875  
 876  #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
 877  pub enum DkgMessage<G: DkgGroup> {
 878      HashedCommit(Sha256),
 879      Commit(#[serde(with = "serde_commit")] Vec<G>),
 880      Share(
 881          #[serde(with = "bls12_381_serde::scalar")] Scalar,
 882          #[serde(with = "bls12_381_serde::scalar")] Scalar,
 883      ),
 884      Extract(#[serde(with = "serde_commit")] Vec<G>),
 885  }
 886  
 887  /// Defines a group (e.g. G1 or G2) that we can generate keys for
 888  pub trait DkgGroup:
 889      Group + Mul<Scalar, Output = Self> + Curve + GroupEncoding + SGroup + Unpin
 890  {
 891  }
 892  
 893  impl<T: Group + Mul<Scalar, Output = T> + Curve + GroupEncoding + SGroup + Unpin> DkgGroup for T {}
 894  
 895  /// Handling the Group serialization with a wrapper
 896  mod serde_commit {
 897      use serde::{Deserialize, Deserializer, Serialize, Serializer};
 898  
 899      use crate::config::DkgGroup;
 900  
 901      pub fn serialize<S: Serializer, G: DkgGroup>(vec: &[G], s: S) -> Result<S::Ok, S::Error> {
 902          let wrap_vec: Vec<Wrap<G>> = vec.iter().cloned().map(Wrap).collect();
 903          wrap_vec.serialize(s)
 904      }
 905  
 906      pub fn deserialize<'d, D: Deserializer<'d>, G: DkgGroup>(d: D) -> Result<Vec<G>, D::Error> {
 907          let wrap_vec = <Vec<Wrap<G>>>::deserialize(d)?;
 908          Ok(wrap_vec.into_iter().map(|wrap| wrap.0).collect())
 909      }
 910  
 911      struct Wrap<G: DkgGroup>(G);
 912  
 913      impl<G: DkgGroup> Serialize for Wrap<G> {
 914          fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
 915              self.0.serialize2(s)
 916          }
 917      }
 918  
 919      impl<'d, G: DkgGroup> Deserialize<'d> for Wrap<G> {
 920          fn deserialize<D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
 921              G::deserialize2(d).map(Wrap)
 922          }
 923      }
 924  }
 925  
 926  pub trait SGroup: Sized {
 927      fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error>;
 928      fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error>;
 929  }
 930  
 931  impl SGroup for G2Projective {
 932      fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
 933          bls12_381_serde::g2::serialize(&self.to_affine(), s)
 934      }
 935  
 936      fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
 937          bls12_381_serde::g2::deserialize(d).map(G2Projective::from)
 938      }
 939  }
 940  
 941  impl SGroup for G1Projective {
 942      fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
 943          bls12_381_serde::g1::serialize(&self.to_affine(), s)
 944      }
 945  
 946      fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
 947          bls12_381_serde::g1::deserialize(d).map(G1Projective::from)
 948      }
 949  }
 950  
 951  /// Key under which the federation name can be sent to client in the `meta` part
 952  /// of the config
 953  pub const META_FEDERATION_NAME_KEY: &str = "federation_name";
 954  
 955  /// Key under which the vetted gateways can be sent to client in the `meta` part
 956  /// of the config
 957  pub const META_VETTED_GATEWAYS_KEY: &str = "vetted_gateways";
 958  
 959  /// Key under which the override URL can be sent to client in the `meta` part
 960  /// of the config
 961  pub const META_OVERRIDE_URL_KEY: &str = "meta_override_url";
 962  
 963  pub fn load_from_file<T: DeserializeOwned>(path: &Path) -> Result<T, anyhow::Error> {
 964      let file = std::fs::File::open(path)?;
 965      Ok(serde_json::from_reader(file)?)
 966  }
 967  
 968  pub mod serde_binary_human_readable {
 969      use std::borrow::Cow;
 970  
 971      use hex::{FromHex, ToHex};
 972      use serde::de::DeserializeOwned;
 973      use serde::{Deserialize, Deserializer, Serialize, Serializer};
 974  
 975      pub fn serialize<T: Serialize, S: Serializer>(x: &T, s: S) -> Result<S::Ok, S::Error> {
 976          if s.is_human_readable() {
 977              let bytes =
 978                  bincode::serialize(x).map_err(|e| serde::ser::Error::custom(format!("{e:?}")))?;
 979              s.serialize_str(&bytes.encode_hex::<String>())
 980          } else {
 981              Serialize::serialize(x, s)
 982          }
 983      }
 984  
 985      pub fn deserialize<'d, T: DeserializeOwned, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
 986          if d.is_human_readable() {
 987              let hex_str: Cow<str> = Deserialize::deserialize(d)?;
 988              let bytes = Vec::from_hex(hex_str.as_ref()).map_err(serde::de::Error::custom)?;
 989              bincode::deserialize(&bytes).map_err(|e| serde::de::Error::custom(format!("{e:?}")))
 990          } else {
 991              Deserialize::deserialize(d)
 992          }
 993      }
 994  }
 995  
 996  #[cfg(test)]
 997  mod tests {
 998      use fedimint_core::config::{ClientConfig, GlobalClientConfig};
 999  
1000      use crate::module::CoreConsensusVersion;
1001  
1002      #[test]
1003      fn test_dcode_meta() {
1004          let config = ClientConfig {
1005              global: GlobalClientConfig {
1006                  api_endpoints: Default::default(),
1007                  consensus_version: CoreConsensusVersion { major: 0, minor: 0 },
1008                  meta: vec![
1009                      ("foo".to_string(), "bar".to_string()),
1010                      ("baz".to_string(), "\"bam\"".to_string()),
1011                      ("arr".to_string(), "[\"1\", \"2\"]".to_string()),
1012                  ]
1013                  .into_iter()
1014                  .collect(),
1015              },
1016              modules: Default::default(),
1017          };
1018  
1019          assert_eq!(
1020              config
1021                  .meta::<String>("foo")
1022                  .expect("parsing legacy string failed"),
1023              Some("bar".to_string())
1024          );
1025          assert_eq!(
1026              config.meta::<String>("baz").expect("parsing string failed"),
1027              Some("bam".to_string())
1028          );
1029          assert_eq!(
1030              config
1031                  .meta::<Vec<String>>("arr")
1032                  .expect("parsing array failed"),
1033              Some(vec!["1".to_string(), "2".to_string()])
1034          );
1035  
1036          assert!(config.meta::<Vec<String>>("foo").is_err());
1037          assert!(config.meta::<Vec<String>>("baz").is_err());
1038          assert_eq!(
1039              config
1040                  .meta::<String>("arr")
1041                  .expect("parsing via legacy fallback failed"),
1042              Some("[\"1\", \"2\"]".to_string())
1043          );
1044      }
1045  }