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