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