#![allow(clippy::pedantic)]
use std::collections::{BTreeMap, BTreeSet};
use std::marker::PhantomData;
use std::sync::Arc;
use std::{any, marker};
use bitcoin::Network;
use fedimint_api_client::api::DynModuleApi;
use fedimint_core::config::{
ClientModuleConfig, CommonModuleInitRegistry, ModuleInitRegistry, ServerModuleConfig,
ServerModuleConsensusConfig,
};
use fedimint_core::core::{ModuleInstanceId, ModuleKind};
use fedimint_core::db::{Database, DatabaseVersion};
use fedimint_core::module::{
CommonModuleInit, CoreConsensusVersion, IDynCommonModuleInit, ModuleConsensusVersion,
ModuleInit, SupportedModuleApiVersions,
};
use fedimint_core::task::TaskGroup;
use fedimint_core::{NumPeers, PeerId, apply, async_trait_maybe_send, dyn_newtype_define};
use crate::bitcoin_rpc::ServerBitcoinRpcMonitor;
use crate::config::PeerHandleOps;
use crate::migration::{
DynServerDbMigrationFn, ServerDbMigrationFnContext, ServerModuleDbMigrationContext,
ServerModuleDbMigrationFn,
};
use crate::{DynServerModule, ServerModule};
#[derive(Debug, Clone, Copy)]
pub struct ConfigGenModuleArgs {
pub network: Network,
pub disable_base_fees: bool,
}
#[apply(async_trait_maybe_send!)]
pub trait IServerModuleInit: IDynCommonModuleInit {
fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static);
fn supported_api_versions(&self) -> SupportedModuleApiVersions;
#[allow(clippy::too_many_arguments)]
async fn init(
&self,
peer_num: NumPeers,
cfg: ServerModuleConfig,
db: Database,
task_group: &TaskGroup,
our_peer_id: PeerId,
module_api: DynModuleApi,
server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
) -> anyhow::Result<DynServerModule>;
fn trusted_dealer_gen(
&self,
peers: &[PeerId],
args: &ConfigGenModuleArgs,
) -> BTreeMap<PeerId, ServerModuleConfig>;
async fn distributed_gen(
&self,
peers: &(dyn PeerHandleOps + Send + Sync),
args: &ConfigGenModuleArgs,
) -> anyhow::Result<ServerModuleConfig>;
fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
fn get_client_config(
&self,
module_instance_id: ModuleInstanceId,
config: &ServerModuleConsensusConfig,
) -> anyhow::Result<ClientModuleConfig>;
fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, DynServerDbMigrationFn>;
fn used_db_prefixes(&self) -> Option<BTreeSet<u8>>;
fn is_enabled_by_default(&self) -> bool;
}
pub trait ServerModuleShared: any::Any + Send + Sync {
fn new(task_group: TaskGroup) -> Self;
}
pub struct ServerModuleInitArgs<S>
where
S: ServerModuleInit,
{
cfg: ServerModuleConfig,
db: Database,
task_group: TaskGroup,
our_peer_id: PeerId,
num_peers: NumPeers,
module_api: DynModuleApi,
server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
_marker: marker::PhantomData<S>,
}
impl<S> ServerModuleInitArgs<S>
where
S: ServerModuleInit,
{
pub fn cfg(&self) -> &ServerModuleConfig {
&self.cfg
}
pub fn db(&self) -> &Database {
&self.db
}
pub fn num_peers(&self) -> NumPeers {
self.num_peers
}
pub fn task_group(&self) -> &TaskGroup {
&self.task_group
}
pub fn our_peer_id(&self) -> PeerId {
self.our_peer_id
}
pub fn module_api(&self) -> &DynModuleApi {
&self.module_api
}
pub fn server_bitcoin_rpc_monitor(&self) -> ServerBitcoinRpcMonitor {
self.server_bitcoin_rpc_monitor.clone()
}
}
#[apply(async_trait_maybe_send!)]
pub trait ServerModuleInit: ModuleInit + Sized {
type Module: ServerModule + Send + Sync;
fn versions(&self, core: CoreConsensusVersion) -> &[ModuleConsensusVersion];
fn supported_api_versions(&self) -> SupportedModuleApiVersions;
fn kind() -> ModuleKind {
<Self as ModuleInit>::Common::KIND
}
async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<Self::Module>;
fn trusted_dealer_gen(
&self,
peers: &[PeerId],
args: &ConfigGenModuleArgs,
) -> BTreeMap<PeerId, ServerModuleConfig>;
async fn distributed_gen(
&self,
peers: &(dyn PeerHandleOps + Send + Sync),
args: &ConfigGenModuleArgs,
) -> anyhow::Result<ServerModuleConfig>;
fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
fn get_client_config(
&self,
config: &ServerModuleConsensusConfig,
) -> anyhow::Result<<<Self as ModuleInit>::Common as CommonModuleInit>::ClientConfig>;
fn get_database_migrations(
&self,
) -> BTreeMap<DatabaseVersion, ServerModuleDbMigrationFn<Self::Module>> {
BTreeMap::new()
}
fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
None
}
fn is_enabled_by_default(&self) -> bool {
true
}
}
#[apply(async_trait_maybe_send!)]
impl<T> IServerModuleInit for T
where
T: ServerModuleInit + 'static + Sync,
{
fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
self
}
fn supported_api_versions(&self) -> SupportedModuleApiVersions {
<Self as ServerModuleInit>::supported_api_versions(self)
}
async fn init(
&self,
num_peers: NumPeers,
cfg: ServerModuleConfig,
db: Database,
task_group: &TaskGroup,
our_peer_id: PeerId,
module_api: DynModuleApi,
server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
) -> anyhow::Result<DynServerModule> {
let module = <Self as ServerModuleInit>::init(
self,
&ServerModuleInitArgs {
num_peers,
cfg,
db,
task_group: task_group.clone(),
our_peer_id,
_marker: PhantomData,
module_api,
server_bitcoin_rpc_monitor,
},
)
.await?;
Ok(DynServerModule::from(module))
}
fn trusted_dealer_gen(
&self,
peers: &[PeerId],
args: &ConfigGenModuleArgs,
) -> BTreeMap<PeerId, ServerModuleConfig> {
<Self as ServerModuleInit>::trusted_dealer_gen(self, peers, args)
}
async fn distributed_gen(
&self,
peers: &(dyn PeerHandleOps + Send + Sync),
args: &ConfigGenModuleArgs,
) -> anyhow::Result<ServerModuleConfig> {
<Self as ServerModuleInit>::distributed_gen(self, peers, args).await
}
fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()> {
<Self as ServerModuleInit>::validate_config(self, identity, config)
}
fn get_client_config(
&self,
module_instance_id: ModuleInstanceId,
config: &ServerModuleConsensusConfig,
) -> anyhow::Result<ClientModuleConfig> {
ClientModuleConfig::from_typed(
module_instance_id,
<Self as ServerModuleInit>::kind(),
config.version,
<Self as ServerModuleInit>::get_client_config(self, config)?,
)
}
fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, DynServerDbMigrationFn> {
<Self as ServerModuleInit>::get_database_migrations(self)
.into_iter()
.map(|(k, f)| {
(k, {
let closure: DynServerDbMigrationFn =
Box::new(move |ctx: ServerDbMigrationFnContext<'_>| {
let map = ctx.map(ServerModuleDbMigrationContext::new);
Box::pin(f(map))
});
closure
})
})
.collect()
}
fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
<Self as ServerModuleInit>::used_db_prefixes(self)
}
fn is_enabled_by_default(&self) -> bool {
<Self as ServerModuleInit>::is_enabled_by_default(self)
}
}
dyn_newtype_define!(
#[derive(Clone)]
pub DynServerModuleInit(Arc<IServerModuleInit>)
);
impl AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static> for DynServerModuleInit {
fn as_ref(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
self.inner.as_common()
}
}
pub type ServerModuleInitRegistry = ModuleInitRegistry<DynServerModuleInit>;
pub trait ServerModuleInitRegistryExt {
fn to_common(&self) -> CommonModuleInitRegistry;
fn default_modules(&self) -> BTreeSet<ModuleKind>;
}
impl ServerModuleInitRegistryExt for ServerModuleInitRegistry {
fn to_common(&self) -> CommonModuleInitRegistry {
self.iter().map(|(_k, v)| v.to_dyn_common()).collect()
}
fn default_modules(&self) -> BTreeSet<ModuleKind> {
self.iter()
.filter(|(_kind, init)| init.is_enabled_by_default())
.map(|(kind, _init)| kind.clone())
.collect()
}
}