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 }