Skip to main content

fedimint_core/
config.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt::{Debug, Display};
3use std::hash::Hash;
4use std::path::{Path, PathBuf};
5use std::str::FromStr;
6
7use bitcoin::hashes::sha256::HashEngine;
8use bitcoin::hashes::{Hash as BitcoinHash, hex, sha256};
9use bls12_381::Scalar;
10use fedimint_core::core::{ModuleInstanceId, ModuleKind};
11use fedimint_core::encoding::{DynRawFallback, Encodable};
12use fedimint_core::module::registry::ModuleRegistry;
13use fedimint_core::util::SafeUrl;
14use fedimint_core::{ModuleDecoderRegistry, format_hex};
15use fedimint_logging::LOG_CORE;
16use hex::FromHex;
17use secp256k1::PublicKey;
18use serde::de::DeserializeOwned;
19use serde::{Deserialize, Deserializer, Serialize, Serializer};
20use serde_json::json;
21use thiserror::Error;
22use threshold_crypto::{G1Projective, G2Projective};
23use tracing::warn;
24
25use crate::core::DynClientConfig;
26use crate::encoding::{Decodable, DecodeContext as _, DecodeError};
27use crate::module::{
28    CoreConsensusVersion, DynCommonModuleInit, IDynCommonModuleInit, ModuleConsensusVersion,
29    SerdeModuleEncoding,
30};
31use crate::session_outcome::SignedSessionOutcome;
32use crate::util::FmtCompact as _;
33use crate::{PeerId, maybe_add_send_sync, secp256k1};
34
35// TODO: make configurable
36/// This limits the RAM consumption of a AlephBFT Unit to roughly 50kB
37pub const ALEPH_BFT_UNIT_BYTE_LIMIT: usize = 50_000;
38
39/// [`serde_json::Value`] that must contain `kind: String` field
40///
41/// TODO: enforce at ser/deserialization
42/// TODO: make inside prive and enforce `kind` on construction, to
43/// other functions non-falliable
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45pub struct JsonWithKind {
46    kind: ModuleKind,
47    #[serde(flatten)]
48    value: serde_json::Value,
49}
50
51impl JsonWithKind {
52    pub fn new(kind: ModuleKind, value: serde_json::Value) -> Self {
53        Self { kind, value }
54    }
55
56    /// Workaround for a serde `flatten` quirk
57    ///
58    /// We serialize config with no fields as: eg. `{ kind: "ln" }`.
59    ///
60    /// When `kind` gets removed and `value` is parsed, it will
61    /// parse as `Value::Object` that is empty.
62    ///
63    /// However empty module structs, like `struct FooConfigLocal;` (unit
64    /// struct), will fail to deserialize with this value, as they expect
65    /// `Value::Null`.
66    ///
67    /// We can turn manually empty object into null, and that's what
68    /// we do in this function. This fixes the deserialization into
69    /// unit type, but in turn breaks deserialization into `struct Foo{}`,
70    /// which is arguably much less common, but valid.
71    ///
72    /// TODO: In the future, we should have a typed and erased versions of
73    /// module construction traits, and then we can try with and
74    /// without the workaround to have both cases working.
75    /// See <https://github.com/fedimint/fedimint/issues/1303>
76    pub fn with_fixed_empty_value(self) -> Self {
77        if let serde_json::Value::Object(ref o) = self.value
78            && o.is_empty()
79        {
80            return Self {
81                kind: self.kind,
82                value: serde_json::Value::Null,
83            };
84        }
85
86        self
87    }
88
89    pub fn value(&self) -> &serde_json::Value {
90        &self.value
91    }
92
93    pub fn kind(&self) -> &ModuleKind {
94        &self.kind
95    }
96
97    pub fn is_kind(&self, kind: &ModuleKind) -> bool {
98        &self.kind == kind
99    }
100}
101
102#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
103pub struct PeerUrl {
104    /// The peer's public URL (e.g. `wss://fedimint-server-1:5000`)
105    pub url: SafeUrl,
106    /// The peer's name
107    pub name: String,
108}
109
110/// Total client config v0 (<0.4.0). Does not contain broadcast public keys.
111///
112/// This includes global settings and client-side module configs.
113#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
114pub struct ClientConfigV0 {
115    #[serde(flatten)]
116    pub global: GlobalClientConfigV0,
117    #[serde(deserialize_with = "de_int_key")]
118    pub modules: BTreeMap<ModuleInstanceId, ClientModuleConfig>,
119}
120
121/// Total client config
122///
123/// This includes global settings and client-side module configs.
124#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
125pub struct ClientConfig {
126    #[serde(flatten)]
127    pub global: GlobalClientConfig,
128    #[serde(deserialize_with = "de_int_key")]
129    pub modules: BTreeMap<ModuleInstanceId, ClientModuleConfig>,
130}
131
132// FIXME: workaround for https://github.com/serde-rs/json/issues/989
133fn de_int_key<'de, D, K, V>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
134where
135    D: Deserializer<'de>,
136    K: Eq + Ord + FromStr,
137    K::Err: Display,
138    V: Deserialize<'de>,
139{
140    let string_map = <BTreeMap<String, V>>::deserialize(deserializer)?;
141    let map = string_map
142        .into_iter()
143        .map(|(key_str, value)| {
144            let key = K::from_str(&key_str).map_err(serde::de::Error::custom)?;
145            Ok((key, value))
146        })
147        .collect::<Result<BTreeMap<_, _>, _>>()?;
148    Ok(map)
149}
150
151fn optional_de_int_key<'de, D, K, V>(deserializer: D) -> Result<Option<BTreeMap<K, V>>, D::Error>
152where
153    D: Deserializer<'de>,
154    K: Eq + Ord + FromStr,
155    K::Err: Display,
156    V: Deserialize<'de>,
157{
158    let Some(string_map) = <Option<BTreeMap<String, V>>>::deserialize(deserializer)? else {
159        return Ok(None);
160    };
161
162    let map = string_map
163        .into_iter()
164        .map(|(key_str, value)| {
165            let key = K::from_str(&key_str).map_err(serde::de::Error::custom)?;
166            Ok((key, value))
167        })
168        .collect::<Result<BTreeMap<_, _>, _>>()?;
169
170    Ok(Some(map))
171}
172
173/// Client config that cannot be cryptographically verified but is easier to
174/// parse by external tools
175#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
176pub struct JsonClientConfig {
177    pub global: GlobalClientConfig,
178    pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
179}
180
181/// Federation-wide client config
182#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
183pub struct GlobalClientConfigV0 {
184    /// API endpoints for each federation member
185    #[serde(deserialize_with = "de_int_key")]
186    pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
187    /// Core consensus version
188    pub consensus_version: CoreConsensusVersion,
189    // TODO: make it a String -> serde_json::Value map?
190    /// Additional config the federation wants to transmit to the clients
191    pub meta: BTreeMap<String, String>,
192}
193
194/// Federation-wide client config
195#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
196pub struct GlobalClientConfig {
197    /// API endpoints for each federation member
198    #[serde(deserialize_with = "de_int_key")]
199    pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
200    /// Signing session keys for each federation member
201    /// Optional for 0.3.x backwards compatibility
202    #[serde(default, deserialize_with = "optional_de_int_key")]
203    pub broadcast_public_keys: Option<BTreeMap<PeerId, PublicKey>>,
204    /// Core consensus version
205    pub consensus_version: CoreConsensusVersion,
206    // TODO: make it a String -> serde_json::Value map?
207    /// Additional config the federation wants to transmit to the clients
208    pub meta: BTreeMap<String, String>,
209}
210
211impl GlobalClientConfig {
212    /// 0.4.0 and later uses a hash of broadcast public keys to calculate the
213    /// federation id. 0.3.x and earlier use a hash of api endpoints
214    pub fn calculate_federation_id(&self) -> FederationId {
215        FederationId(self.api_endpoints.consensus_hash())
216    }
217
218    /// Federation name from config metadata (if set)
219    pub fn federation_name(&self) -> Option<&str> {
220        self.meta.get(META_FEDERATION_NAME_KEY).map(|x| &**x)
221    }
222}
223
224impl ClientConfig {
225    /// See [`DynRawFallback::redecode_raw`].
226    pub fn redecode_raw(
227        self,
228        modules: &ModuleDecoderRegistry,
229    ) -> Result<Self, crate::encoding::DecodeError> {
230        Ok(Self {
231            modules: self
232                .modules
233                .into_iter()
234                .map(|(module_id, v)| {
235                    // Assuming this isn't running in any hot path it's better to have the debug
236                    // info than saving one allocation
237                    let kind = v.kind.clone();
238
239                    v.redecode_raw(modules)
240                        .context(format!("redecode_raw: instance: {module_id}, kind: {kind}"))
241                        .map(|v| (module_id, v))
242                })
243                .collect::<Result<_, _>>()?,
244            ..self
245        })
246    }
247
248    pub fn calculate_federation_id(&self) -> FederationId {
249        self.global.calculate_federation_id()
250    }
251
252    /// Get the value of a given meta field
253    pub fn meta<V: serde::de::DeserializeOwned + 'static>(
254        &self,
255        key: &str,
256    ) -> Result<Option<V>, ModuleConfigError> {
257        let Some(str_value) = self.global.meta.get(key) else {
258            return Ok(None);
259        };
260        let res =
261            serde_json::from_str(str_value)
262                .map(Some)
263                .map_err(|source| ModuleConfigError::Meta {
264                    key: key.to_owned(),
265                    source,
266                });
267
268        // In the past we encoded some string fields as "just a string" without quotes,
269        // this code ensures that old meta values still parse since config is hard to
270        // change
271        if res.is_err() && std::any::TypeId::of::<V>() == std::any::TypeId::of::<String>() {
272            let string_ret = Box::new(str_value.clone());
273            let ret = unsafe {
274                // We can transmute a String to V because we know that V==String
275                std::mem::transmute::<Box<String>, Box<V>>(string_ret)
276            };
277            Ok(Some(*ret))
278        } else {
279            res
280        }
281    }
282
283    /// Converts a consensus-encoded client config struct to a client config
284    /// struct that when encoded as JSON shows the fields of module configs
285    /// instead of a consensus-encoded hex string.
286    ///
287    /// In case of unknown module the config value is a hex string.
288    pub fn to_json(&self) -> JsonClientConfig {
289        JsonClientConfig {
290            global: self.global.clone(),
291            modules: self
292                .modules
293                .iter()
294                .map(|(&module_instance_id, module_config)| {
295                    let module_config_json = JsonWithKind {
296                        kind: module_config.kind.clone(),
297                        value: module_config.config
298                            .clone()
299                            .decoded()
300                            .and_then(|dyn_cfg| dyn_cfg.to_json())
301                            .unwrap_or_else(|| json!({
302                            "unknown_module_hex": module_config.config.consensus_encode_to_hex()
303                        })),
304                    };
305                    (module_instance_id, module_config_json)
306                })
307                .collect(),
308        }
309    }
310}
311
312/// The federation id is a copy of the authentication threshold public key of
313/// the federation
314///
315/// Stable id so long as guardians membership does not change
316/// Unique id so long as guardians do not all collude
317#[derive(
318    Debug,
319    Copy,
320    Serialize,
321    Deserialize,
322    Clone,
323    Eq,
324    Hash,
325    PartialEq,
326    Encodable,
327    Decodable,
328    Ord,
329    PartialOrd,
330)]
331pub struct FederationId(pub sha256::Hash);
332
333#[derive(
334    Debug,
335    Copy,
336    Serialize,
337    Deserialize,
338    Clone,
339    Eq,
340    Hash,
341    PartialEq,
342    Encodable,
343    Decodable,
344    Ord,
345    PartialOrd,
346)]
347/// Prefix of the [`FederationId`], useful for UX improvements
348///
349/// Intentionally compact to save on the encoding. With 4 billion
350/// combinations real-life non-malicious collisions should never
351/// happen.
352pub struct FederationIdPrefix([u8; 4]);
353
354impl Display for FederationIdPrefix {
355    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
356        format_hex(&self.0, f)
357    }
358}
359
360impl Display for FederationId {
361    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
362        format_hex(&self.0.to_byte_array(), f)
363    }
364}
365
366impl FromStr for FederationIdPrefix {
367    type Err = hex::HexToArrayError;
368
369    fn from_str(s: &str) -> Result<Self, Self::Err> {
370        Ok(Self(<[u8; 4]>::from_hex(s)?))
371    }
372}
373
374impl FederationIdPrefix {
375    pub fn to_bytes(&self) -> Vec<u8> {
376        self.0.to_vec()
377    }
378}
379
380/// Display as a hex encoding
381impl FederationId {
382    /// Random dummy id for testing
383    pub fn dummy() -> Self {
384        Self(sha256::Hash::from_byte_array([42; 32]))
385    }
386
387    pub(crate) fn from_byte_array(bytes: [u8; 32]) -> Self {
388        Self(sha256::Hash::from_byte_array(bytes))
389    }
390
391    pub fn to_prefix(&self) -> FederationIdPrefix {
392        FederationIdPrefix(self.0[..4].try_into().expect("can't fail"))
393    }
394
395    /// Converts a federation id to a public key to which we know but discard
396    /// the private key.
397    ///
398    /// Clients MUST never use this private key for any signing operations!
399    ///
400    /// That is ok because we only use the public key for adding a route
401    /// hint to LN invoices that tells fedimint clients that the invoice can
402    /// only be paid internally. Since no LN node with that pub key can exist
403    /// other LN senders will know that they cannot pay the invoice.
404    pub fn to_fake_ln_pub_key(
405        &self,
406        secp: &bitcoin::secp256k1::Secp256k1<bitcoin::secp256k1::All>,
407    ) -> Result<bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::Error> {
408        let sk = bitcoin::secp256k1::SecretKey::from_slice(&self.0.to_byte_array())?;
409        Ok(bitcoin::secp256k1::PublicKey::from_secret_key(secp, &sk))
410    }
411}
412
413impl FromStr for FederationId {
414    type Err = hex::HexToArrayError;
415
416    fn from_str(s: &str) -> Result<Self, Self::Err> {
417        Ok(Self::from_byte_array(<[u8; 32]>::from_hex(s)?))
418    }
419}
420
421impl ClientConfig {
422    /// Returns the consensus hash for a given client config
423    pub fn consensus_hash(&self) -> sha256::Hash {
424        let mut engine = HashEngine::default();
425        self.consensus_encode(&mut engine)
426            .expect("Consensus hashing should never fail");
427        sha256::Hash::from_engine(engine)
428    }
429
430    pub fn get_module<T: Decodable + 'static>(
431        &self,
432        id: ModuleInstanceId,
433    ) -> Result<&T, ModuleConfigError> {
434        self.modules.get(&id).map_or_else(
435            || Err(ModuleConfigError::ModuleNotFound { id }),
436            |client_cfg| client_cfg.cast(),
437        )
438    }
439
440    // TODO: rename this and one above
441    pub fn get_module_cfg(
442        &self,
443        id: ModuleInstanceId,
444    ) -> Result<ClientModuleConfig, ModuleConfigError> {
445        self.modules.get(&id).map_or_else(
446            || Err(ModuleConfigError::ModuleNotFound { id }),
447            |client_cfg| Ok(client_cfg.clone()),
448        )
449    }
450
451    /// (soft-deprecated): Get the first instance of a module of a given kind in
452    /// defined in config
453    ///
454    /// Since module ids are numerical and for time being we only support 1:1
455    /// mint, wallet, ln module code in the client, this is useful, but
456    /// please write any new code that avoids assumptions about available
457    /// modules.
458    pub fn get_first_module_by_kind<T: Decodable + 'static>(
459        &self,
460        kind: impl Into<ModuleKind>,
461    ) -> Result<(ModuleInstanceId, &T), ModuleConfigError> {
462        let kind: ModuleKind = kind.into();
463        let Some((id, module_cfg)) = self.modules.iter().find(|(_, v)| v.is_kind(&kind)) else {
464            return Err(ModuleConfigError::KindNotFound { kind });
465        };
466        Ok((*id, module_cfg.cast()?))
467    }
468
469    // TODO: rename this and above
470    pub fn get_first_module_by_kind_cfg(
471        &self,
472        kind: impl Into<ModuleKind>,
473    ) -> Result<(ModuleInstanceId, ClientModuleConfig), ModuleConfigError> {
474        let kind: ModuleKind = kind.into();
475        self.modules
476            .iter()
477            .find(|(_, v)| v.is_kind(&kind))
478            .map(|(id, v)| (*id, v.clone()))
479            .ok_or(ModuleConfigError::KindNotFound { kind })
480    }
481}
482
483#[derive(Clone, Debug)]
484pub struct ModuleInitRegistry<M>(BTreeMap<ModuleKind, M>);
485
486/// Legacy module ordering used before alphabetical ordering was introduced.
487/// This ordering was: ln, mint, wallet, lnv2, meta, unknown
488const LEGACY_MODULE_ORDER: &[&str] = &["ln", "mint", "wallet", "lnv2", "meta", "unknown"];
489
490impl<M> ModuleInitRegistry<M> {
491    pub fn iter(&self) -> impl Iterator<Item = (&ModuleKind, &M)> {
492        self.0.iter()
493    }
494
495    /// Iterate over modules in the legacy insertion order for backwards
496    /// compatibility. Modules not in the legacy order list are appended
497    /// at the end in alphabetical order.
498    pub fn iter_legacy_order(&self) -> Vec<(&ModuleKind, &M)> {
499        let mut ordered: Vec<(&ModuleKind, &M)> = Vec::new();
500
501        // First add modules in legacy order
502        for kind_str in LEGACY_MODULE_ORDER {
503            let kind = ModuleKind::from_static_str(kind_str);
504            if let Some((k, m)) = self.0.get_key_value(&kind) {
505                ordered.push((k, m));
506            }
507        }
508
509        // Then add any remaining modules in alphabetical order
510        for (kind, module) in &self.0 {
511            if !LEGACY_MODULE_ORDER.contains(&kind.as_str()) {
512                ordered.push((kind, module));
513            }
514        }
515
516        ordered
517    }
518}
519
520impl<M> Default for ModuleInitRegistry<M> {
521    fn default() -> Self {
522        Self(BTreeMap::new())
523    }
524}
525
526pub type CommonModuleInitRegistry = ModuleInitRegistry<DynCommonModuleInit>;
527
528impl<M> From<Vec<M>> for ModuleInitRegistry<M>
529where
530    M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
531{
532    fn from(value: Vec<M>) -> Self {
533        Self(
534            value
535                .into_iter()
536                .map(|i| (i.as_ref().module_kind(), i))
537                .collect::<BTreeMap<_, _>>(),
538        )
539    }
540}
541
542impl<M> FromIterator<M> for ModuleInitRegistry<M>
543where
544    M: AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
545{
546    fn from_iter<T: IntoIterator<Item = M>>(iter: T) -> Self {
547        Self(
548            iter.into_iter()
549                .map(|i| (i.as_ref().module_kind(), i))
550                .collect::<BTreeMap<_, _>>(),
551        )
552    }
553}
554
555impl<M> ModuleInitRegistry<M> {
556    pub fn new() -> Self {
557        Self::default()
558    }
559
560    pub fn attach<T>(&mut self, r#gen: T)
561    where
562        T: Into<M> + 'static + Send + Sync,
563        M: AsRef<dyn IDynCommonModuleInit + 'static + Send + Sync>,
564    {
565        let r#gen: M = r#gen.into();
566        let kind = r#gen.as_ref().module_kind();
567        assert!(
568            self.0.insert(kind.clone(), r#gen).is_none(),
569            "Can't insert module of same kind twice: {kind}"
570        );
571    }
572
573    pub fn kinds(&self) -> BTreeSet<ModuleKind> {
574        self.0.keys().cloned().collect()
575    }
576
577    pub fn get(&self, k: &ModuleKind) -> Option<&M> {
578        self.0.get(k)
579    }
580}
581
582impl<M> ModuleInitRegistry<M>
583where
584    M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
585{
586    #[deprecated(
587        note = "You probably want `available_decoders` to support missing module kinds. If you really want a strict behavior, use `decoders_strict`"
588    )]
589    pub fn decoders<'a>(
590        &self,
591        modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
592    ) -> Result<ModuleDecoderRegistry, ModuleConfigError> {
593        self.decoders_strict(modules)
594    }
595
596    /// Get decoders for `modules` and fail if any is unsupported
597    pub fn decoders_strict<'a>(
598        &self,
599        modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
600    ) -> Result<ModuleDecoderRegistry, ModuleConfigError> {
601        let mut decoders = BTreeMap::new();
602        for (id, kind) in modules {
603            let Some(init) = self.0.get(kind) else {
604                return Err(ModuleConfigError::UnsupportedModule {
605                    id,
606                    kind: kind.clone(),
607                });
608            };
609
610            decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
611        }
612        Ok(ModuleDecoderRegistry::from(decoders))
613    }
614
615    /// Get decoders for `modules` and skip unsupported ones
616    pub fn available_decoders<'a>(
617        &self,
618        modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
619    ) -> ModuleDecoderRegistry {
620        let mut decoders = BTreeMap::new();
621        for (id, kind) in modules {
622            let Some(init) = self.0.get(kind) else {
623                warn!(target: LOG_CORE, "Unsupported module id: {id}, kind: {kind}");
624                continue;
625            };
626
627            decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
628        }
629        ModuleDecoderRegistry::from(decoders)
630    }
631}
632
633#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
634pub struct ServerModuleConsensusConfig {
635    pub kind: ModuleKind,
636    pub version: ModuleConsensusVersion,
637    #[serde(with = "::hex::serde")]
638    pub config: Vec<u8>,
639}
640
641/// Config for the client-side of a particular Federation module
642///
643/// Since modules are (tbd.) pluggable into Federations,
644/// it needs to be some form of an abstract type-erased-like
645/// value.
646#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
647pub struct ClientModuleConfig {
648    pub kind: ModuleKind,
649    pub version: ModuleConsensusVersion,
650    #[serde(with = "::fedimint_core::encoding::as_hex")]
651    pub config: DynRawFallback<DynClientConfig>,
652}
653
654impl ClientModuleConfig {
655    pub fn from_typed<T: fedimint_core::core::ClientConfig>(
656        module_instance_id: ModuleInstanceId,
657        kind: ModuleKind,
658        version: ModuleConsensusVersion,
659        value: T,
660    ) -> Self {
661        Self {
662            kind,
663            version,
664            config: fedimint_core::core::DynClientConfig::from_typed(module_instance_id, value)
665                .into(),
666        }
667    }
668
669    pub fn redecode_raw(
670        self,
671        modules: &ModuleDecoderRegistry,
672    ) -> Result<Self, crate::encoding::DecodeError> {
673        Ok(Self {
674            config: self.config.redecode_raw(modules)?,
675            ..self
676        })
677    }
678
679    pub fn is_kind(&self, kind: &ModuleKind) -> bool {
680        &self.kind == kind
681    }
682
683    pub fn kind(&self) -> &ModuleKind {
684        &self.kind
685    }
686}
687
688impl ClientModuleConfig {
689    pub fn cast<T>(&self) -> Result<&T, ModuleConfigError>
690    where
691        T: 'static,
692    {
693        match &self.config {
694            DynRawFallback::Decoded(config) => {
695                config
696                    .as_any()
697                    .downcast_ref::<T>()
698                    .ok_or_else(|| ModuleConfigError::WrongType {
699                        kind: self.kind.clone(),
700                        expected: std::any::type_name::<T>(),
701                    })
702            }
703            DynRawFallback::Raw {
704                module_instance_id, ..
705            } => Err(ModuleConfigError::NotDecoded {
706                id: *module_instance_id,
707                kind: self.kind.clone(),
708            }),
709        }
710    }
711}
712
713/// Config for the server-side of a particular Federation module
714///
715/// See [`ClientModuleConfig`].
716#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
717pub struct ServerModuleConfig {
718    pub private: JsonWithKind,
719    pub consensus: ServerModuleConsensusConfig,
720}
721
722impl ServerModuleConfig {
723    pub fn from(private: JsonWithKind, consensus: ServerModuleConsensusConfig) -> Self {
724        Self { private, consensus }
725    }
726
727    pub fn to_typed<T: TypedServerModuleConfig>(&self) -> Result<T, ModuleConfigError> {
728        let private = serde_json::from_value(self.private.value().clone())?;
729        let consensus = <T::Consensus>::consensus_decode_whole(
730            &self.consensus.config[..],
731            &ModuleRegistry::default(),
732        )?;
733
734        Ok(TypedServerModuleConfig::from_parts(private, consensus))
735    }
736}
737
738/// Consensus-critical part of a server side module config
739pub trait TypedServerModuleConsensusConfig:
740    DeserializeOwned + Serialize + Encodable + Decodable
741{
742    fn kind(&self) -> ModuleKind;
743
744    fn version(&self) -> ModuleConsensusVersion;
745
746    fn from_erased(erased: &ServerModuleConsensusConfig) -> Result<Self, ModuleConfigError> {
747        Ok(Self::consensus_decode_whole(
748            &erased.config[..],
749            &ModuleRegistry::default(),
750        )?)
751    }
752}
753
754/// Module (server side) config, typed
755pub trait TypedServerModuleConfig: DeserializeOwned + Serialize {
756    /// Private for this federation member data that are security sensitive and
757    /// will be encrypted at rest
758    type Private: DeserializeOwned + Serialize;
759    /// Shared consensus-critical config
760    type Consensus: TypedServerModuleConsensusConfig;
761
762    /// Assemble from the three functionally distinct parts
763    fn from_parts(private: Self::Private, consensus: Self::Consensus) -> Self;
764
765    /// Split the config into its two functionally distinct parts
766    fn to_parts(self) -> (ModuleKind, Self::Private, Self::Consensus);
767
768    /// Turn the typed config into type-erased version
769    fn to_erased(self) -> ServerModuleConfig {
770        let (kind, private, consensus) = self.to_parts();
771
772        ServerModuleConfig {
773            private: JsonWithKind::new(
774                kind,
775                serde_json::to_value(private).expect("serialization can't fail"),
776            ),
777            consensus: ServerModuleConsensusConfig {
778                kind: consensus.kind(),
779                version: consensus.version(),
780                config: consensus.consensus_encode_to_vec(),
781            },
782        }
783    }
784}
785
786#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Encodable, Decodable)]
787pub enum P2PMessage {
788    Aleph(Vec<u8>),
789    SessionSignature(secp256k1::schnorr::Signature),
790    SessionIndex(u64),
791    SignedSessionOutcome(SerdeModuleEncoding<SignedSessionOutcome>),
792    Checksum(sha256::Hash),
793    DkgG1(DkgMessageG1),
794    DkgG2(DkgMessageG2),
795    Encodable(Vec<u8>),
796    #[encodable_default]
797    Default {
798        variant: u64,
799        bytes: Vec<u8>,
800    },
801}
802
803#[derive(Debug, PartialEq, Eq, Clone, Encodable, Decodable)]
804pub enum DkgMessageG1 {
805    Hash(sha256::Hash),
806    Commitment(Vec<G1Projective>),
807    Share(Scalar),
808}
809
810#[derive(Debug, PartialEq, Eq, Clone, Encodable, Decodable)]
811pub enum DkgMessageG2 {
812    Hash(sha256::Hash),
813    Commitment(Vec<G2Projective>),
814    Share(Scalar),
815}
816
817// TODO: Remove the Serde encoding as soon as the p2p layer drops it as
818// requirement
819impl Serialize for DkgMessageG1 {
820    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
821    where
822        S: Serializer,
823    {
824        self.consensus_encode_to_hex().serialize(serializer)
825    }
826}
827
828impl<'de> Deserialize<'de> for DkgMessageG1 {
829    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
830    where
831        D: Deserializer<'de>,
832    {
833        Self::consensus_decode_hex(
834            &String::deserialize(deserializer)?,
835            &ModuleDecoderRegistry::default(),
836        )
837        .map_err(|e| serde::de::Error::custom(e.fmt_compact()))
838    }
839}
840
841// TODO: Remove the Serde encoding as soon as the p2p layer drops it as
842// requirement
843impl Serialize for DkgMessageG2 {
844    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
845    where
846        S: Serializer,
847    {
848        self.consensus_encode_to_hex().serialize(serializer)
849    }
850}
851
852impl<'de> Deserialize<'de> for DkgMessageG2 {
853    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
854    where
855        D: Deserializer<'de>,
856    {
857        Self::consensus_decode_hex(
858            &String::deserialize(deserializer)?,
859            &ModuleDecoderRegistry::default(),
860        )
861        .map_err(|e| serde::de::Error::custom(e.fmt_compact()))
862    }
863}
864
865/// Key under which the federation name can be sent to client in the `meta` part
866/// of the config
867pub const META_FEDERATION_NAME_KEY: &str = "federation_name";
868
869pub fn load_from_file<T: DeserializeOwned>(path: &Path) -> Result<T, ConfigFileError> {
870    let contents = std::fs::read(path).map_err(|source| ConfigFileError::Io {
871        path: path.to_owned(),
872        source,
873    })?;
874
875    serde_json::from_slice(&contents).map_err(|source| ConfigFileError::Json {
876        path: path.to_owned(),
877        source,
878    })
879}
880
881/// A module's configured relative fee is above what the module accepts.
882///
883/// Every module that charges a relative fee validates it the same way in its
884/// own `FeeConsensus::new`, so the failure is named once here rather than once
885/// per module.
886#[derive(Debug, Error)]
887#[error("The relative fee of {parts_per_million} parts per million is over the limit of {max}")]
888pub struct ExcessiveRelativeFeeError {
889    /// The fee that was asked for.
890    pub parts_per_million: u64,
891    /// The largest relative fee the module accepts.
892    pub max: u64,
893}
894
895/// Failure to look up or cast a module configuration.
896#[derive(Debug, Error)]
897#[non_exhaustive]
898pub enum ModuleConfigError {
899    /// No module with this instance id exists in the config.
900    #[error("Client config for module id {id} not found")]
901    ModuleNotFound { id: ModuleInstanceId },
902    /// No module of this kind exists in the config.
903    #[error("Module kind {kind} not found")]
904    KindNotFound { kind: ModuleKind },
905    /// The decoded module config is not of the requested type.
906    #[error("Client module config of kind {kind} is not a {expected}")]
907    WrongType {
908        kind: ModuleKind,
909        expected: &'static str,
910    },
911    /// The module config is still in its raw, undecoded form because no
912    /// decoder for its kind was available; see
913    /// [`ClientModuleConfig::redecode_raw`].
914    #[error("Client module config for module id {id} of kind {kind} has not been decoded")]
915    NotDecoded {
916        id: ModuleInstanceId,
917        kind: ModuleKind,
918    },
919    /// The config references a module kind this build has no support for.
920    #[error("Detected configuration for unsupported module id: {id}, kind: {kind}")]
921    UnsupportedModule {
922        id: ModuleInstanceId,
923        kind: ModuleKind,
924    },
925    /// A meta field exists but does not deserialize to the requested type.
926    #[error("Decoding meta field '{key}' failed")]
927    Meta {
928        key: String,
929        #[source]
930        source: serde_json::Error,
931    },
932    /// A JSON part of the config failed to (de)serialize.
933    #[error("Invalid JSON in module config")]
934    Json(#[from] serde_json::Error),
935    /// A consensus-encoded part of the config failed to decode.
936    #[error("Invalid consensus encoding in module config")]
937    Decode(#[from] DecodeError),
938}
939
940/// Failure to load a JSON config file.
941#[derive(Debug, Error)]
942#[non_exhaustive]
943pub enum ConfigFileError {
944    /// The file could not be opened or read.
945    #[error("Failed to read config file {}", path.display())]
946    Io {
947        path: PathBuf,
948        #[source]
949        source: std::io::Error,
950    },
951    /// The file is not valid JSON for the requested type.
952    #[error("Invalid JSON in config file {}", path.display())]
953    Json {
954        path: PathBuf,
955        #[source]
956        source: serde_json::Error,
957    },
958}
959
960#[cfg(test)]
961mod tests;