fedimint_client/
module_init.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt;
3use std::sync::Arc;
4
5use fedimint_api_client::api::DynGlobalApi;
6use fedimint_client_module::db::ClientModuleMigrationFn;
7use fedimint_client_module::module::init::{
8    ClientModuleInit, ClientModuleInitArgs, ClientModuleRecoverArgs,
9};
10use fedimint_client_module::module::recovery::{DynModuleBackup, RecoveryProgress};
11use fedimint_client_module::module::{ClientContext, DynClientModule, FinalClientIface};
12use fedimint_client_module::{ClientModule, ModuleInstanceId, ModuleKind};
13use fedimint_connectors::ConnectorRegistry;
14use fedimint_core::config::{ClientModuleConfig, FederationId, ModuleInitRegistry};
15use fedimint_core::core::Decoder;
16use fedimint_core::db::{Database, DatabaseVersion};
17use fedimint_core::module::{
18    ApiAuth, ApiVersion, CommonModuleInit, IDynCommonModuleInit, ModuleInit, MultiApiVersion,
19};
20use fedimint_core::task::{MaybeSend, MaybeSync, TaskGroup};
21use fedimint_core::{NumPeers, apply, async_trait_maybe_send, dyn_newtype_define};
22use fedimint_derive_secret::DerivableSecret;
23use tokio::sync::watch;
24
25use crate::sm::notifier::Notifier;
26
27pub type ClientModuleInitRegistry = ModuleInitRegistry<DynClientModuleInit>;
28
29#[apply(async_trait_maybe_send!)]
30pub trait IClientModuleInit: IDynCommonModuleInit + fmt::Debug + MaybeSend + MaybeSync {
31    fn decoder(&self) -> Decoder;
32
33    fn module_kind(&self) -> ModuleKind;
34
35    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static);
36
37    /// See [`ClientModuleInit::supported_api_versions`]
38    fn supported_api_versions(&self) -> MultiApiVersion;
39
40    #[allow(clippy::too_many_arguments)]
41    async fn recover(
42        &self,
43        final_client: FinalClientIface,
44        federation_id: FederationId,
45        num_peers: NumPeers,
46        cfg: ClientModuleConfig,
47        db: Database,
48        instance_id: ModuleInstanceId,
49        core_api_version: ApiVersion,
50        module_api_version: ApiVersion,
51        module_root_secret: DerivableSecret,
52        notifier: Notifier,
53        api: DynGlobalApi,
54        admin_auth: Option<ApiAuth>,
55        snapshot: Option<&DynModuleBackup>,
56        progress_tx: watch::Sender<RecoveryProgress>,
57        task_group: TaskGroup,
58    ) -> anyhow::Result<()>;
59
60    #[allow(clippy::too_many_arguments)]
61    async fn init(
62        &self,
63        final_client: FinalClientIface,
64        federation_id: FederationId,
65        peer_num: usize,
66        cfg: ClientModuleConfig,
67        db: Database,
68        instance_id: ModuleInstanceId,
69        core_api_version: ApiVersion,
70        module_api_version: ApiVersion,
71        module_root_secret: DerivableSecret,
72        notifier: Notifier,
73        api: DynGlobalApi,
74        admin_auth: Option<ApiAuth>,
75        task_group: TaskGroup,
76        connector_registry: ConnectorRegistry,
77    ) -> anyhow::Result<DynClientModule>;
78
79    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ClientModuleMigrationFn>;
80
81    /// See [`ClientModuleInit::used_db_prefixes`]
82    fn used_db_prefixes(&self) -> Option<BTreeSet<u8>>;
83}
84
85#[apply(async_trait_maybe_send!)]
86impl<T> IClientModuleInit for T
87where
88    T: ClientModuleInit + 'static + MaybeSend + Sync,
89{
90    fn decoder(&self) -> Decoder {
91        <<T as ClientModuleInit>::Module as ClientModule>::decoder()
92    }
93
94    fn module_kind(&self) -> ModuleKind {
95        <Self as ModuleInit>::Common::KIND
96    }
97
98    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
99        self
100    }
101
102    fn supported_api_versions(&self) -> MultiApiVersion {
103        <Self as ClientModuleInit>::supported_api_versions(self)
104    }
105
106    async fn recover(
107        &self,
108        final_client: FinalClientIface,
109        federation_id: FederationId,
110        num_peers: NumPeers,
111        cfg: ClientModuleConfig,
112        db: Database,
113        instance_id: ModuleInstanceId,
114        core_api_version: ApiVersion,
115        module_api_version: ApiVersion,
116        module_root_secret: DerivableSecret,
117        // TODO: make dyn type for notifier
118        notifier: Notifier,
119        api: DynGlobalApi,
120        admin_auth: Option<ApiAuth>,
121        snapshot: Option<&DynModuleBackup>,
122        progress_tx: watch::Sender<RecoveryProgress>,
123        task_group: TaskGroup,
124    ) -> anyhow::Result<()> {
125        let typed_cfg: &<<T as fedimint_core::module::ModuleInit>::Common as CommonModuleInit>::ClientConfig = cfg.cast()?;
126        let snapshot: Option<&<<Self as ClientModuleInit>::Module as ClientModule>::Backup> =
127            snapshot.map(|s| {
128                s.as_any()
129                    .downcast_ref()
130                    .expect("can't convert client module backup to desired type")
131            });
132
133        let (module_db, global_dbtx_access_token) = db.with_prefix_module_id(instance_id);
134        Ok(<Self as ClientModuleInit>::recover(
135            self,
136            &ClientModuleRecoverArgs {
137                federation_id,
138                num_peers,
139                cfg: typed_cfg.clone(),
140                db: module_db.clone(),
141                core_api_version,
142                module_api_version,
143                module_root_secret,
144                notifier: notifier.module_notifier(instance_id, final_client.clone()),
145                api: api.clone(),
146                admin_auth,
147                module_api: api.with_module(instance_id),
148                context: ClientContext::new(
149                    final_client,
150                    instance_id,
151                    global_dbtx_access_token,
152                    module_db,
153                ),
154                progress_tx,
155                task_group,
156            },
157            snapshot,
158        )
159        .await?)
160    }
161
162    async fn init(
163        &self,
164        final_client: FinalClientIface,
165        federation_id: FederationId,
166        peer_num: usize,
167        cfg: ClientModuleConfig,
168        db: Database,
169        instance_id: ModuleInstanceId,
170        core_api_version: ApiVersion,
171        module_api_version: ApiVersion,
172        module_root_secret: DerivableSecret,
173        // TODO: make dyn type for notifier
174        notifier: Notifier,
175        api: DynGlobalApi,
176        admin_auth: Option<ApiAuth>,
177        task_group: TaskGroup,
178        connector_registry: ConnectorRegistry,
179    ) -> anyhow::Result<DynClientModule> {
180        let typed_cfg: &<<T as fedimint_core::module::ModuleInit>::Common as CommonModuleInit>::ClientConfig = cfg.cast()?;
181        let (module_db, global_dbtx_access_token) = db.with_prefix_module_id(instance_id);
182        Ok(<Self as ClientModuleInit>::init(
183            self,
184            &ClientModuleInitArgs {
185                federation_id,
186                peer_num,
187                cfg: typed_cfg.clone(),
188                db: module_db.clone(),
189                core_api_version,
190                module_api_version,
191                module_root_secret,
192                notifier: notifier.module_notifier(instance_id, final_client.clone()),
193                api: api.clone(),
194                admin_auth,
195                module_api: api.with_module(instance_id),
196                context: ClientContext::new(
197                    final_client,
198                    instance_id,
199                    global_dbtx_access_token,
200                    module_db,
201                ),
202                task_group,
203                connector_registry,
204            },
205        )
206        .await?
207        .into())
208    }
209
210    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ClientModuleMigrationFn> {
211        <Self as ClientModuleInit>::get_database_migrations(self)
212    }
213
214    fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
215        <Self as ClientModuleInit>::used_db_prefixes(self)
216    }
217}
218
219dyn_newtype_define!(
220    #[derive(Clone)]
221    pub DynClientModuleInit(Arc<IClientModuleInit>)
222);
223
224impl AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static> for DynClientModuleInit {
225    fn as_ref(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
226        self.inner.as_common()
227    }
228}
229
230impl AsRef<dyn IClientModuleInit + 'static> for DynClientModuleInit {
231    fn as_ref(&self) -> &(dyn IClientModuleInit + 'static) {
232        self.inner.as_ref()
233    }
234}