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