use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{Debug, Display};
use std::hash::Hash;
use std::ops::Mul;
use std::path::Path;
use std::str::FromStr;
use anyhow::{bail, format_err, Context};
use bitcoin::hashes::sha256::{Hash as Sha256, HashEngine};
use bitcoin::hashes::{hex, sha256, Hash as BitcoinHash};
use bls12_381::Scalar;
use fedimint_core::core::{ModuleInstanceId, ModuleKind};
use fedimint_core::encoding::{DynRawFallback, Encodable};
use fedimint_core::module::registry::ModuleRegistry;
use fedimint_core::task::Cancelled;
use fedimint_core::util::SafeUrl;
use fedimint_core::{format_hex, ModuleDecoderRegistry};
use fedimint_logging::LOG_CORE;
use hex::FromHex;
use secp256k1::PublicKey;
use serde::de::DeserializeOwned;
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::json;
use thiserror::Error;
use threshold_crypto::group::{Curve, Group, GroupEncoding};
use threshold_crypto::{G1Projective, G2Projective};
use tracing::warn;
use crate::core::DynClientConfig;
use crate::encoding::Decodable;
use crate::module::{
CoreConsensusVersion, DynCommonModuleInit, DynServerModuleInit, IDynCommonModuleInit,
ModuleConsensusVersion,
};
use crate::{bls12_381_serde, maybe_add_send_sync, PeerId};
pub const ALEPH_BFT_UNIT_BYTE_LIMIT: usize = 50_000;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct JsonWithKind {
kind: ModuleKind,
#[serde(flatten)]
value: serde_json::Value,
}
impl JsonWithKind {
pub fn new(kind: ModuleKind, value: serde_json::Value) -> Self {
Self { kind, value }
}
pub fn with_fixed_empty_value(self) -> Self {
if let serde_json::Value::Object(ref o) = self.value {
if o.is_empty() {
return Self {
kind: self.kind,
value: serde_json::Value::Null,
};
}
}
self
}
pub fn value(&self) -> &serde_json::Value {
&self.value
}
pub fn kind(&self) -> &ModuleKind {
&self.kind
}
pub fn is_kind(&self, kind: &ModuleKind) -> bool {
&self.kind == kind
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Encodable, Decodable)]
pub struct PeerUrl {
pub url: SafeUrl,
pub name: String,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct ClientConfigV0 {
#[serde(flatten)]
pub global: GlobalClientConfigV0,
#[serde(deserialize_with = "de_int_key")]
pub modules: BTreeMap<ModuleInstanceId, ClientModuleConfig>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct ClientConfig {
#[serde(flatten)]
pub global: GlobalClientConfig,
#[serde(deserialize_with = "de_int_key")]
pub modules: BTreeMap<ModuleInstanceId, ClientModuleConfig>,
}
fn de_int_key<'de, D, K, V>(deserializer: D) -> Result<BTreeMap<K, V>, D::Error>
where
D: Deserializer<'de>,
K: Eq + Ord + FromStr,
K::Err: Display,
V: Deserialize<'de>,
{
let string_map = <BTreeMap<String, V>>::deserialize(deserializer)?;
let map = string_map
.into_iter()
.map(|(key_str, value)| {
let key = K::from_str(&key_str).map_err(serde::de::Error::custom)?;
Ok((key, value))
})
.collect::<Result<BTreeMap<_, _>, _>>()?;
Ok(map)
}
fn optional_de_int_key<'de, D, K, V>(deserializer: D) -> Result<Option<BTreeMap<K, V>>, D::Error>
where
D: Deserializer<'de>,
K: Eq + Ord + FromStr,
K::Err: Display,
V: Deserialize<'de>,
{
let Some(string_map) = <Option<BTreeMap<String, V>>>::deserialize(deserializer)? else {
return Ok(None);
};
let map = string_map
.into_iter()
.map(|(key_str, value)| {
let key = K::from_str(&key_str).map_err(serde::de::Error::custom)?;
Ok((key, value))
})
.collect::<Result<BTreeMap<_, _>, _>>()?;
Ok(Some(map))
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct JsonClientConfig {
pub global: GlobalClientConfig,
pub modules: BTreeMap<ModuleInstanceId, JsonWithKind>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct GlobalClientConfigV0 {
#[serde(deserialize_with = "de_int_key")]
pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
pub consensus_version: CoreConsensusVersion,
pub meta: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct GlobalClientConfig {
#[serde(deserialize_with = "de_int_key")]
pub api_endpoints: BTreeMap<PeerId, PeerUrl>,
#[serde(default, deserialize_with = "optional_de_int_key")]
pub broadcast_public_keys: Option<BTreeMap<PeerId, PublicKey>>,
pub consensus_version: CoreConsensusVersion,
pub meta: BTreeMap<String, String>,
}
impl GlobalClientConfig {
pub fn calculate_federation_id(&self) -> FederationId {
FederationId(self.api_endpoints.consensus_hash())
}
pub fn federation_name(&self) -> Option<&str> {
self.meta.get(META_FEDERATION_NAME_KEY).map(|x| &**x)
}
}
impl ClientConfig {
pub fn redecode_raw(
self,
modules: &ModuleDecoderRegistry,
) -> Result<Self, crate::encoding::DecodeError> {
Ok(Self {
modules: self
.modules
.into_iter()
.map(|(k, v)| {
let kind = v.kind.clone();
v.redecode_raw(modules)
.context(format!("redecode_raw: instance: {k}, kind: {kind}"))
.map(|v| (k, v))
})
.collect::<Result<_, _>>()?,
..self
})
}
pub fn calculate_federation_id(&self) -> FederationId {
self.global.calculate_federation_id()
}
pub fn meta<V: serde::de::DeserializeOwned + 'static>(
&self,
key: &str,
) -> Result<Option<V>, anyhow::Error> {
let Some(str_value) = self.global.meta.get(key) else {
return Ok(None);
};
let res = serde_json::from_str(str_value)
.map(Some)
.context(format!("Decoding meta field '{key}' failed"));
if res.is_err() && std::any::TypeId::of::<V>() == std::any::TypeId::of::<String>() {
let string_ret = Box::new(str_value.clone());
let ret = unsafe {
std::mem::transmute::<Box<String>, Box<V>>(string_ret)
};
Ok(Some(*ret))
} else {
res
}
}
pub fn to_json(&self) -> JsonClientConfig {
JsonClientConfig {
global: self.global.clone(),
modules: self
.modules
.iter()
.map(|(&module_instance_id, module_config)| {
let module_config_json = JsonWithKind {
kind: module_config.kind.clone(),
value: module_config.config
.clone()
.decoded()
.and_then(|dyn_cfg| dyn_cfg.to_json())
.unwrap_or_else(|| json!({
"unknown_module_hex": module_config.config.consensus_encode_to_hex()
})),
};
(module_instance_id, module_config_json)
})
.collect(),
}
}
}
#[derive(
Debug,
Copy,
Serialize,
Deserialize,
Clone,
Eq,
Hash,
PartialEq,
Encodable,
Decodable,
Ord,
PartialOrd,
)]
pub struct FederationId(pub sha256::Hash);
#[derive(
Debug,
Copy,
Serialize,
Deserialize,
Clone,
Eq,
Hash,
PartialEq,
Encodable,
Decodable,
Ord,
PartialOrd,
)]
pub struct FederationIdPrefix([u8; 4]);
impl Display for FederationIdPrefix {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
format_hex(&self.0, f)
}
}
impl Display for FederationId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
format_hex(&self.0.to_byte_array(), f)
}
}
impl FromStr for FederationIdPrefix {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(<[u8; 4]>::from_hex(s)?))
}
}
impl FederationIdPrefix {
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_vec()
}
}
impl FederationId {
pub fn dummy() -> Self {
Self(sha256::Hash::from_byte_array([42; 32]))
}
pub(crate) fn from_byte_array(bytes: [u8; 32]) -> Self {
Self(sha256::Hash::from_byte_array(bytes))
}
pub fn to_prefix(&self) -> FederationIdPrefix {
FederationIdPrefix(self.0[..4].try_into().expect("can't fail"))
}
pub fn to_fake_ln_pub_key(
&self,
secp: &bitcoin::secp256k1::Secp256k1<bitcoin::secp256k1::All>,
) -> anyhow::Result<bitcoin::secp256k1::PublicKey> {
let sk = bitcoin::secp256k1::SecretKey::from_slice(&self.0.to_byte_array())?;
Ok(bitcoin::secp256k1::PublicKey::from_secret_key(secp, &sk))
}
}
impl FromStr for FederationId {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_byte_array(<[u8; 32]>::from_hex(s)?))
}
}
impl ClientConfig {
pub fn consensus_hash(&self) -> sha256::Hash {
let mut engine = HashEngine::default();
self.consensus_encode(&mut engine)
.expect("Consensus hashing should never fail");
sha256::Hash::from_engine(engine)
}
pub fn get_module<T: Decodable + 'static>(&self, id: ModuleInstanceId) -> anyhow::Result<&T> {
self.modules.get(&id).map_or_else(
|| Err(format_err!("Client config for module id {id} not found")),
|client_cfg| client_cfg.cast(),
)
}
pub fn get_module_cfg(&self, id: ModuleInstanceId) -> anyhow::Result<ClientModuleConfig> {
self.modules.get(&id).map_or_else(
|| Err(format_err!("Client config for module id {id} not found")),
|client_cfg| Ok(client_cfg.clone()),
)
}
pub fn get_first_module_by_kind<T: Decodable + 'static>(
&self,
kind: impl Into<ModuleKind>,
) -> anyhow::Result<(ModuleInstanceId, &T)> {
let kind: ModuleKind = kind.into();
let Some((id, module_cfg)) = self.modules.iter().find(|(_, v)| v.is_kind(&kind)) else {
anyhow::bail!("Module kind {kind} not found")
};
Ok((*id, module_cfg.cast()?))
}
pub fn get_first_module_by_kind_cfg(
&self,
kind: impl Into<ModuleKind>,
) -> anyhow::Result<(ModuleInstanceId, ClientModuleConfig)> {
let kind: ModuleKind = kind.into();
self.modules
.iter()
.find(|(_, v)| v.is_kind(&kind))
.map(|(id, v)| (*id, v.clone()))
.ok_or_else(|| anyhow::format_err!("Module kind {kind} not found"))
}
}
#[derive(Clone, Debug)]
pub struct ModuleInitRegistry<M>(BTreeMap<ModuleKind, M>);
impl<M> Default for ModuleInitRegistry<M> {
fn default() -> Self {
Self(BTreeMap::new())
}
}
#[derive(Debug, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ConfigGenModuleParams {
pub local: serde_json::Value,
pub consensus: serde_json::Value,
}
pub type ServerModuleInitRegistry = ModuleInitRegistry<DynServerModuleInit>;
impl ConfigGenModuleParams {
pub fn new(local: serde_json::Value, consensus: serde_json::Value) -> Self {
Self { local, consensus }
}
pub fn to_typed<P: ModuleInitParams>(&self) -> anyhow::Result<P> {
Ok(P::from_parts(
Self::parse("local", self.local.clone())?,
Self::parse("consensus", self.consensus.clone())?,
))
}
fn parse<P: DeserializeOwned>(name: &str, json: serde_json::Value) -> anyhow::Result<P> {
serde_json::from_value(json).with_context(|| format!("Schema mismatch for {name} argument"))
}
pub fn from_typed<P: ModuleInitParams>(p: P) -> anyhow::Result<Self> {
let (local, consensus) = p.to_parts();
Ok(Self {
local: serde_json::to_value(local)?,
consensus: serde_json::to_value(consensus)?,
})
}
}
pub type CommonModuleInitRegistry = ModuleInitRegistry<DynCommonModuleInit>;
pub type ServerModuleConfigGenParamsRegistry = ModuleRegistry<ConfigGenModuleParams>;
impl Eq for ServerModuleConfigGenParamsRegistry {}
impl PartialEq for ServerModuleConfigGenParamsRegistry {
fn eq(&self, other: &Self) -> bool {
self.iter_modules().eq(other.iter_modules())
}
}
impl Serialize for ServerModuleConfigGenParamsRegistry {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let modules: Vec<_> = self.iter_modules().collect();
let mut serializer = serializer.serialize_map(Some(modules.len()))?;
for (id, kind, params) in modules {
serializer.serialize_key(&id)?;
serializer.serialize_value(&(kind.clone(), params.clone()))?;
}
serializer.end()
}
}
impl<'de> Deserialize<'de> for ServerModuleConfigGenParamsRegistry {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json: BTreeMap<ModuleInstanceId, (ModuleKind, ConfigGenModuleParams)> =
Deserialize::deserialize(deserializer)?;
let mut params = BTreeMap::new();
for (id, (kind, module)) in json {
params.insert(id, (kind, module));
}
Ok(Self::from(params))
}
}
impl<M> From<Vec<M>> for ModuleInitRegistry<M>
where
M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
{
fn from(value: Vec<M>) -> Self {
Self(
value
.into_iter()
.map(|i| (i.as_ref().module_kind(), i))
.collect::<BTreeMap<_, _>>(),
)
}
}
impl<M> FromIterator<M> for ModuleInitRegistry<M>
where
M: AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
{
fn from_iter<T: IntoIterator<Item = M>>(iter: T) -> Self {
Self(
iter.into_iter()
.map(|i| (i.as_ref().module_kind(), i))
.collect::<BTreeMap<_, _>>(),
)
}
}
impl<M> ModuleInitRegistry<M> {
pub fn new() -> Self {
Self::default()
}
pub fn attach<T>(&mut self, gen: T)
where
T: Into<M> + 'static + Send + Sync,
M: AsRef<dyn IDynCommonModuleInit + 'static + Send + Sync>,
{
let gen: M = gen.into();
let kind = gen.as_ref().module_kind();
assert!(
self.0.insert(kind.clone(), gen).is_none(),
"Can't insert module of same kind twice: {kind}"
);
}
pub fn kinds(&self) -> BTreeSet<ModuleKind> {
self.0.keys().cloned().collect()
}
pub fn get(&self, k: &ModuleKind) -> Option<&M> {
self.0.get(k)
}
}
impl ModuleRegistry<ConfigGenModuleParams> {
pub fn attach_config_gen_params_by_id<T: ModuleInitParams>(
&mut self,
id: ModuleInstanceId,
kind: ModuleKind,
gen: T,
) -> &mut Self {
let params = ConfigGenModuleParams::from_typed(gen)
.unwrap_or_else(|err| panic!("Invalid config gen params for {kind}: {err}"));
self.register_module(id, kind, params);
self
}
pub fn attach_config_gen_params<T: ModuleInitParams>(
&mut self,
kind: ModuleKind,
gen: T,
) -> &mut Self {
let params = ConfigGenModuleParams::from_typed(gen)
.unwrap_or_else(|err| panic!("Invalid config gen params for {kind}: {err}"));
self.append_module(kind, params);
self
}
}
impl ServerModuleInitRegistry {
pub fn to_common(&self) -> CommonModuleInitRegistry {
ModuleInitRegistry(
self.0
.iter()
.map(|(k, v)| (k.clone(), v.to_dyn_common()))
.collect(),
)
}
}
impl<M> ModuleInitRegistry<M>
where
M: AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static>,
{
#[deprecated(
note = "You probably want `available_decoders` to support missing module kinds. If you really want a strict behavior, use `decoders_strict`"
)]
pub fn decoders<'a>(
&self,
modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
) -> anyhow::Result<ModuleDecoderRegistry> {
self.decoders_strict(modules)
}
pub fn decoders_strict<'a>(
&self,
modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
) -> anyhow::Result<ModuleDecoderRegistry> {
let mut decoders = BTreeMap::new();
for (id, kind) in modules {
let Some(init) = self.0.get(kind) else {
anyhow::bail!(
"Detected configuration for unsupported module id: {id}, kind: {kind}"
)
};
decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
}
Ok(ModuleDecoderRegistry::from(decoders))
}
pub fn available_decoders<'a>(
&self,
modules: impl Iterator<Item = (ModuleInstanceId, &'a ModuleKind)>,
) -> anyhow::Result<ModuleDecoderRegistry> {
let mut decoders = BTreeMap::new();
for (id, kind) in modules {
let Some(init) = self.0.get(kind) else {
warn!(target: LOG_CORE, "Unsupported module id: {id}, kind: {kind}");
continue;
};
decoders.insert(id, (kind.clone(), init.as_ref().decoder()));
}
Ok(ModuleDecoderRegistry::from(decoders))
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct EmptyGenParams {}
pub trait ModuleInitParams: serde::Serialize + serde::de::DeserializeOwned {
type Local: DeserializeOwned + Serialize;
type Consensus: DeserializeOwned + Serialize;
fn from_parts(local: Self::Local, consensus: Self::Consensus) -> Self;
fn to_parts(self) -> (Self::Local, Self::Consensus);
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct ServerModuleConsensusConfig {
pub kind: ModuleKind,
pub version: ModuleConsensusVersion,
#[serde(with = "::hex::serde")]
pub config: Vec<u8>,
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Encodable, Decodable)]
pub struct ClientModuleConfig {
pub kind: ModuleKind,
pub version: ModuleConsensusVersion,
#[serde(with = "::fedimint_core::encoding::as_hex")]
pub config: DynRawFallback<DynClientConfig>,
}
impl ClientModuleConfig {
pub fn from_typed<T: fedimint_core::core::ClientConfig>(
module_instance_id: ModuleInstanceId,
kind: ModuleKind,
version: ModuleConsensusVersion,
value: T,
) -> anyhow::Result<Self> {
Ok(Self {
kind,
version,
config: fedimint_core::core::DynClientConfig::from_typed(module_instance_id, value)
.into(),
})
}
pub fn redecode_raw(
self,
modules: &ModuleDecoderRegistry,
) -> Result<Self, crate::encoding::DecodeError> {
Ok(Self {
config: self.config.redecode_raw(modules)?,
..self
})
}
pub fn is_kind(&self, kind: &ModuleKind) -> bool {
&self.kind == kind
}
pub fn kind(&self) -> &ModuleKind {
&self.kind
}
}
impl ClientModuleConfig {
pub fn cast<T>(&self) -> anyhow::Result<&T>
where
T: 'static,
{
self.config
.expect_decoded_ref()
.as_any()
.downcast_ref::<T>()
.context("can't convert client module config to desired type")
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct ServerModuleConfig {
pub local: JsonWithKind,
pub private: JsonWithKind,
pub consensus: ServerModuleConsensusConfig,
}
impl ServerModuleConfig {
pub fn from(
local: JsonWithKind,
private: JsonWithKind,
consensus: ServerModuleConsensusConfig,
) -> Self {
Self {
local,
private,
consensus,
}
}
pub fn to_typed<T: TypedServerModuleConfig>(&self) -> anyhow::Result<T> {
let local = serde_json::from_value(self.local.value().clone())?;
let private = serde_json::from_value(self.private.value().clone())?;
let consensus = <T::Consensus>::consensus_decode(
&mut &self.consensus.config[..],
&ModuleRegistry::default(),
)?;
Ok(TypedServerModuleConfig::from_parts(
local, private, consensus,
))
}
}
pub trait TypedServerModuleConsensusConfig:
DeserializeOwned + Serialize + Encodable + Decodable
{
fn kind(&self) -> ModuleKind;
fn version(&self) -> ModuleConsensusVersion;
fn from_erased(erased: &ServerModuleConsensusConfig) -> anyhow::Result<Self> {
Ok(Self::consensus_decode(
&mut &erased.config[..],
&ModuleRegistry::default(),
)?)
}
}
pub trait TypedServerModuleConfig: DeserializeOwned + Serialize {
type Local: DeserializeOwned + Serialize;
type Private: DeserializeOwned + Serialize;
type Consensus: TypedServerModuleConsensusConfig;
fn from_parts(local: Self::Local, private: Self::Private, consensus: Self::Consensus) -> Self;
fn to_parts(self) -> (ModuleKind, Self::Local, Self::Private, Self::Consensus);
fn to_erased(self) -> ServerModuleConfig {
let (kind, local, private, consensus) = self.to_parts();
ServerModuleConfig {
local: JsonWithKind::new(
kind.clone(),
serde_json::to_value(local).expect("serialization can't fail"),
),
private: JsonWithKind::new(
kind,
serde_json::to_value(private).expect("serialization can't fail"),
),
consensus: ServerModuleConsensusConfig {
kind: consensus.kind(),
version: consensus.version(),
config: consensus.consensus_encode_to_vec(),
},
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum DkgPeerMsg {
PublicKey(secp256k1::PublicKey),
DistributedGen(SupportedDkgMessage),
Module(Vec<u8>),
Done,
}
pub type DkgResult<T> = Result<T, DkgError>;
#[derive(Error, Debug)]
pub enum DkgError {
#[error("Operation cancelled")]
Cancelled(#[from] Cancelled),
#[error("Running DKG failed due to {0}")]
Failed(#[from] anyhow::Error),
#[error("The module was not found {0}")]
ModuleNotFound(ModuleKind),
#[error("Params for modules were not found {0:?}")]
ParamsNotFound(BTreeSet<ModuleKind>),
#[error("Failed to decode module message {0:?}")]
ModuleDecodeError(ModuleKind),
}
pub trait ISupportedDkgMessage: Sized + Serialize + DeserializeOwned {
fn to_msg(self) -> SupportedDkgMessage;
fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self>;
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SupportedDkgMessage {
G1(DkgMessage<G1Projective>),
G2(DkgMessage<G2Projective>),
}
impl ISupportedDkgMessage for DkgMessage<G1Projective> {
fn to_msg(self) -> SupportedDkgMessage {
SupportedDkgMessage::G1(self)
}
fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self> {
match msg {
SupportedDkgMessage::G1(s) => Ok(s),
SupportedDkgMessage::G2(_) => bail!("Incorrect DkgGroup: G2"),
}
}
}
impl ISupportedDkgMessage for DkgMessage<G2Projective> {
fn to_msg(self) -> SupportedDkgMessage {
SupportedDkgMessage::G2(self)
}
fn from_msg(msg: SupportedDkgMessage) -> anyhow::Result<Self> {
match msg {
SupportedDkgMessage::G1(_) => bail!("Incorrect DkgGroup: G1"),
SupportedDkgMessage::G2(s) => Ok(s),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub enum DkgMessage<G: DkgGroup> {
HashedCommit(Sha256),
Commit(#[serde(with = "serde_commit")] Vec<G>),
Share(
#[serde(with = "bls12_381_serde::scalar")] Scalar,
#[serde(with = "bls12_381_serde::scalar")] Scalar,
),
Extract(#[serde(with = "serde_commit")] Vec<G>),
}
pub trait DkgGroup:
Group + Mul<Scalar, Output = Self> + Curve + GroupEncoding + SGroup + Unpin
{
}
impl<T: Group + Mul<Scalar, Output = T> + Curve + GroupEncoding + SGroup + Unpin> DkgGroup for T {}
mod serde_commit {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::config::DkgGroup;
pub fn serialize<S: Serializer, G: DkgGroup>(vec: &[G], s: S) -> Result<S::Ok, S::Error> {
let wrap_vec: Vec<Wrap<G>> = vec.iter().copied().map(Wrap).collect();
wrap_vec.serialize(s)
}
pub fn deserialize<'d, D: Deserializer<'d>, G: DkgGroup>(d: D) -> Result<Vec<G>, D::Error> {
let wrap_vec = <Vec<Wrap<G>>>::deserialize(d)?;
Ok(wrap_vec.into_iter().map(|wrap| wrap.0).collect())
}
struct Wrap<G: DkgGroup>(G);
impl<G: DkgGroup> Serialize for Wrap<G> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.0.serialize2(s)
}
}
impl<'d, G: DkgGroup> Deserialize<'d> for Wrap<G> {
fn deserialize<D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
G::deserialize2(d).map(Wrap)
}
}
}
pub trait SGroup: Sized {
fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error>;
fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error>;
}
impl SGroup for G2Projective {
fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
bls12_381_serde::g2::serialize(&self.to_affine(), s)
}
fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
bls12_381_serde::g2::deserialize(d).map(Self::from)
}
}
impl SGroup for G1Projective {
fn serialize2<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
bls12_381_serde::g1::serialize(&self.to_affine(), s)
}
fn deserialize2<'d, D: Deserializer<'d>>(d: D) -> Result<Self, D::Error> {
bls12_381_serde::g1::deserialize(d).map(Self::from)
}
}
pub const META_FEDERATION_NAME_KEY: &str = "federation_name";
pub fn load_from_file<T: DeserializeOwned>(path: &Path) -> Result<T, anyhow::Error> {
let file = std::fs::File::open(path)?;
Ok(serde_json::from_reader(file)?)
}
pub mod serde_binary_human_readable {
use std::borrow::Cow;
use hex::{FromHex, ToHex};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<T: Serialize, S: Serializer>(x: &T, s: S) -> Result<S::Ok, S::Error> {
if s.is_human_readable() {
let bytes =
bincode::serialize(x).map_err(|e| serde::ser::Error::custom(format!("{e:?}")))?;
s.serialize_str(&bytes.encode_hex::<String>())
} else {
Serialize::serialize(x, s)
}
}
pub fn deserialize<'d, T: DeserializeOwned, D: Deserializer<'d>>(d: D) -> Result<T, D::Error> {
if d.is_human_readable() {
let hex_str: Cow<str> = Deserialize::deserialize(d)?;
let bytes = Vec::from_hex(hex_str.as_ref()).map_err(serde::de::Error::custom)?;
bincode::deserialize(&bytes).map_err(|e| serde::de::Error::custom(format!("{e:?}")))
} else {
Deserialize::deserialize(d)
}
}
}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use fedimint_core::config::{ClientConfig, GlobalClientConfig};
use crate::module::CoreConsensusVersion;
#[test]
fn test_dcode_meta() {
let config = ClientConfig {
global: GlobalClientConfig {
api_endpoints: BTreeMap::new(),
broadcast_public_keys: None,
consensus_version: CoreConsensusVersion { major: 0, minor: 0 },
meta: vec![
("foo".to_string(), "bar".to_string()),
("baz".to_string(), "\"bam\"".to_string()),
("arr".to_string(), "[\"1\", \"2\"]".to_string()),
]
.into_iter()
.collect(),
},
modules: BTreeMap::new(),
};
assert_eq!(
config
.meta::<String>("foo")
.expect("parsing legacy string failed"),
Some("bar".to_string())
);
assert_eq!(
config.meta::<String>("baz").expect("parsing string failed"),
Some("bam".to_string())
);
assert_eq!(
config
.meta::<Vec<String>>("arr")
.expect("parsing array failed"),
Some(vec!["1".to_string(), "2".to_string()])
);
assert!(config.meta::<Vec<String>>("foo").is_err());
assert!(config.meta::<Vec<String>>("baz").is_err());
assert_eq!(
config
.meta::<String>("arr")
.expect("parsing via legacy fallback failed"),
Some("[\"1\", \"2\"]".to_string())
);
}
}