Skip to main content

fedimint_server_core/
init.rs

1// TODO: remove and fix nits
2#![allow(clippy::pedantic)]
3
4use std::collections::{BTreeMap, BTreeSet};
5use std::marker::PhantomData;
6use std::sync::Arc;
7use std::{any, marker};
8
9use bitcoin::Network;
10use fedimint_api_client::api::DynModuleApi;
11use fedimint_core::config::{
12    ClientModuleConfig, CommonModuleInitRegistry, ModuleInitRegistry, ServerModuleConfig,
13    ServerModuleConsensusConfig,
14};
15use fedimint_core::core::{ModuleInstanceId, ModuleKind};
16use fedimint_core::db::{Database, DatabaseVersion};
17use fedimint_core::module::{
18    CommonModuleInit, CoreConsensusVersion, IDynCommonModuleInit, ModuleConsensusVersion,
19    ModuleInit, SupportedModuleApiVersions,
20};
21use fedimint_core::task::TaskGroup;
22use fedimint_core::{NumPeers, PeerId, apply, async_trait_maybe_send, dyn_newtype_define};
23
24use crate::bitcoin_rpc::ServerBitcoinRpcMonitor;
25use crate::config::PeerHandleOps;
26use crate::migration::{
27    DynServerDbMigrationFn, ServerDbMigrationFnContext, ServerModuleDbMigrationContext,
28    ServerModuleDbMigrationFn,
29};
30use crate::{DynServerModule, ServerModule};
31
32/// Arguments passed to modules during config generation
33///
34/// This replaces the per-module GenParams approach with a unified struct
35/// containing all the information modules need for DKG/config generation.
36#[derive(Debug, Clone, Copy)]
37pub struct ConfigGenModuleArgs {
38    /// Bitcoin network for the federation
39    pub network: Network,
40    /// Whether to disable base fees for this federation
41    pub disable_base_fees: bool,
42}
43
44/// Interface for Module Generation
45///
46/// This trait contains the methods responsible for the module's
47/// - initialization
48/// - config generation
49/// - config validation
50///
51/// Once the module configuration is ready, the module can be instantiated via
52/// `[Self::init]`.
53#[apply(async_trait_maybe_send!)]
54pub trait IServerModuleInit: IDynCommonModuleInit {
55    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static);
56
57    fn supported_api_versions(&self) -> SupportedModuleApiVersions;
58
59    /// Initialize the [`DynServerModule`] instance from its config
60    #[allow(clippy::too_many_arguments)]
61    async fn init(
62        &self,
63        peer_num: NumPeers,
64        cfg: ServerModuleConfig,
65        db: Database,
66        task_group: &TaskGroup,
67        our_peer_id: PeerId,
68        module_api: DynModuleApi,
69        server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
70    ) -> anyhow::Result<DynServerModule>;
71
72    fn trusted_dealer_gen(
73        &self,
74        peers: &[PeerId],
75        args: &ConfigGenModuleArgs,
76    ) -> BTreeMap<PeerId, ServerModuleConfig>;
77
78    async fn distributed_gen(
79        &self,
80        peers: &(dyn PeerHandleOps + Send + Sync),
81        args: &ConfigGenModuleArgs,
82    ) -> anyhow::Result<ServerModuleConfig>;
83
84    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
85
86    fn get_client_config(
87        &self,
88        module_instance_id: ModuleInstanceId,
89        config: &ServerModuleConsensusConfig,
90    ) -> anyhow::Result<ClientModuleConfig>;
91
92    /// Retrieves the migrations map from the server module to be applied to the
93    /// database before the module is initialized. The migrations map is
94    /// indexed on the from version.
95    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, DynServerDbMigrationFn>;
96
97    /// See [`ServerModuleInit::used_db_prefixes`]
98    fn used_db_prefixes(&self) -> Option<BTreeSet<u8>>;
99
100    /// Whether this module should be enabled by default in the setup UI
101    fn is_enabled_by_default(&self) -> bool;
102}
103
104/// A type that can be used as module-shared value inside
105/// [`ServerModuleInitArgs`]
106pub trait ServerModuleShared: any::Any + Send + Sync {
107    fn new(task_group: TaskGroup) -> Self;
108}
109
110pub struct ServerModuleInitArgs<S>
111where
112    S: ServerModuleInit,
113{
114    cfg: ServerModuleConfig,
115    db: Database,
116    task_group: TaskGroup,
117    our_peer_id: PeerId,
118    num_peers: NumPeers,
119    module_api: DynModuleApi,
120    server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
121    // ClientModuleInitArgs needs a bound because sometimes we need
122    // to pass associated-types data, so let's just put it here right away
123    _marker: marker::PhantomData<S>,
124}
125
126impl<S> ServerModuleInitArgs<S>
127where
128    S: ServerModuleInit,
129{
130    pub fn cfg(&self) -> &ServerModuleConfig {
131        &self.cfg
132    }
133
134    pub fn db(&self) -> &Database {
135        &self.db
136    }
137
138    pub fn num_peers(&self) -> NumPeers {
139        self.num_peers
140    }
141
142    pub fn task_group(&self) -> &TaskGroup {
143        &self.task_group
144    }
145
146    pub fn our_peer_id(&self) -> PeerId {
147        self.our_peer_id
148    }
149
150    pub fn module_api(&self) -> &DynModuleApi {
151        &self.module_api
152    }
153
154    pub fn server_bitcoin_rpc_monitor(&self) -> ServerBitcoinRpcMonitor {
155        self.server_bitcoin_rpc_monitor.clone()
156    }
157}
158/// Module Generation trait with associated types
159///
160/// Needs to be implemented by module generation type
161///
162/// For examples, take a look at one of the `MintConfigGenerator`,
163/// `WalletConfigGenerator`, or `LightningConfigGenerator` structs.
164#[apply(async_trait_maybe_send!)]
165pub trait ServerModuleInit: ModuleInit + Sized {
166    type Module: ServerModule + Send + Sync;
167
168    /// Version of the module consensus supported by this implementation given a
169    /// certain [`CoreConsensusVersion`].
170    ///
171    /// Refer to [`ModuleConsensusVersion`] for more information about
172    /// versioning.
173    ///
174    /// One module implementation ([`ServerModuleInit`] of a given
175    /// [`ModuleKind`]) can potentially implement multiple versions of the
176    /// consensus, and depending on the config module instance config,
177    /// instantiate the desired one. This method should expose all the
178    /// available versions, purely for information, setup UI and sanity
179    /// checking purposes.
180    fn versions(&self, core: CoreConsensusVersion) -> &[ModuleConsensusVersion];
181
182    fn supported_api_versions(&self) -> SupportedModuleApiVersions;
183
184    fn kind() -> ModuleKind {
185        <Self as ModuleInit>::Common::KIND
186    }
187
188    /// Initialize the module instance from its config
189    async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<Self::Module>;
190
191    fn trusted_dealer_gen(
192        &self,
193        peers: &[PeerId],
194        args: &ConfigGenModuleArgs,
195    ) -> BTreeMap<PeerId, ServerModuleConfig>;
196
197    async fn distributed_gen(
198        &self,
199        peers: &(dyn PeerHandleOps + Send + Sync),
200        args: &ConfigGenModuleArgs,
201    ) -> anyhow::Result<ServerModuleConfig>;
202
203    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
204
205    /// Converts the consensus config into the client config
206    fn get_client_config(
207        &self,
208        config: &ServerModuleConsensusConfig,
209    ) -> anyhow::Result<<<Self as ModuleInit>::Common as CommonModuleInit>::ClientConfig>;
210
211    /// Retrieves the migrations map from the server module to be applied to the
212    /// database before the module is initialized. The migrations map is
213    /// indexed on the from version.
214    fn get_database_migrations(
215        &self,
216    ) -> BTreeMap<DatabaseVersion, ServerModuleDbMigrationFn<Self::Module>> {
217        BTreeMap::new()
218    }
219
220    /// Db prefixes used by the module
221    ///
222    /// If `Some` is returned, it should contain list of database
223    /// prefixes actually used by the module for it's keys.
224    ///
225    /// In (some subset of) non-production tests,
226    /// module database will be scanned for presence of keys
227    /// that do not belong to this list to verify integrity
228    /// of data and possibly catch any unforeseen bugs.
229    fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
230        None
231    }
232
233    /// Whether this module should be enabled by default in the setup UI.
234    /// Modules return `true` by default.
235    fn is_enabled_by_default(&self) -> bool {
236        true
237    }
238}
239
240#[apply(async_trait_maybe_send!)]
241impl<T> IServerModuleInit for T
242where
243    T: ServerModuleInit + 'static + Sync,
244{
245    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
246        self
247    }
248
249    fn supported_api_versions(&self) -> SupportedModuleApiVersions {
250        <Self as ServerModuleInit>::supported_api_versions(self)
251    }
252
253    async fn init(
254        &self,
255        num_peers: NumPeers,
256        cfg: ServerModuleConfig,
257        db: Database,
258        task_group: &TaskGroup,
259        our_peer_id: PeerId,
260        module_api: DynModuleApi,
261        server_bitcoin_rpc_monitor: ServerBitcoinRpcMonitor,
262    ) -> anyhow::Result<DynServerModule> {
263        let module = <Self as ServerModuleInit>::init(
264            self,
265            &ServerModuleInitArgs {
266                num_peers,
267                cfg,
268                db,
269                task_group: task_group.clone(),
270                our_peer_id,
271                _marker: PhantomData,
272                module_api,
273                server_bitcoin_rpc_monitor,
274            },
275        )
276        .await?;
277
278        Ok(DynServerModule::from(module))
279    }
280
281    fn trusted_dealer_gen(
282        &self,
283        peers: &[PeerId],
284        args: &ConfigGenModuleArgs,
285    ) -> BTreeMap<PeerId, ServerModuleConfig> {
286        <Self as ServerModuleInit>::trusted_dealer_gen(self, peers, args)
287    }
288
289    async fn distributed_gen(
290        &self,
291        peers: &(dyn PeerHandleOps + Send + Sync),
292        args: &ConfigGenModuleArgs,
293    ) -> anyhow::Result<ServerModuleConfig> {
294        <Self as ServerModuleInit>::distributed_gen(self, peers, args).await
295    }
296
297    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()> {
298        <Self as ServerModuleInit>::validate_config(self, identity, config)
299    }
300
301    fn get_client_config(
302        &self,
303        module_instance_id: ModuleInstanceId,
304        config: &ServerModuleConsensusConfig,
305    ) -> anyhow::Result<ClientModuleConfig> {
306        ClientModuleConfig::from_typed(
307            module_instance_id,
308            <Self as ServerModuleInit>::kind(),
309            config.version,
310            <Self as ServerModuleInit>::get_client_config(self, config)?,
311        )
312    }
313    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, DynServerDbMigrationFn> {
314        <Self as ServerModuleInit>::get_database_migrations(self)
315            .into_iter()
316            .map(|(k, f)| {
317                (k, {
318                    let closure: DynServerDbMigrationFn =
319                        Box::new(move |ctx: ServerDbMigrationFnContext<'_>| {
320                            let map = ctx.map(ServerModuleDbMigrationContext::new);
321                            Box::pin(f(map))
322                        });
323                    closure
324                })
325            })
326            .collect()
327    }
328
329    fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
330        <Self as ServerModuleInit>::used_db_prefixes(self)
331    }
332
333    fn is_enabled_by_default(&self) -> bool {
334        <Self as ServerModuleInit>::is_enabled_by_default(self)
335    }
336}
337
338dyn_newtype_define!(
339    #[derive(Clone)]
340    pub DynServerModuleInit(Arc<IServerModuleInit>)
341);
342
343impl AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static> for DynServerModuleInit {
344    fn as_ref(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
345        self.inner.as_common()
346    }
347}
348
349pub type ServerModuleInitRegistry = ModuleInitRegistry<DynServerModuleInit>;
350
351pub trait ServerModuleInitRegistryExt {
352    fn to_common(&self) -> CommonModuleInitRegistry;
353    fn default_modules(&self) -> BTreeSet<ModuleKind>;
354}
355
356impl ServerModuleInitRegistryExt for ServerModuleInitRegistry {
357    fn to_common(&self) -> CommonModuleInitRegistry {
358        self.iter().map(|(_k, v)| v.to_dyn_common()).collect()
359    }
360
361    fn default_modules(&self) -> BTreeSet<ModuleKind> {
362        self.iter()
363            .filter(|(_kind, init)| init.is_enabled_by_default())
364            .map(|(kind, _init)| kind.clone())
365            .collect()
366    }
367}