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