fedimint_client_module/module/
init.rs
1pub mod recovery;
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use fedimint_api_client::api::{DynGlobalApi, DynModuleApi};
6use fedimint_core::config::FederationId;
7use fedimint_core::core::ModuleKind;
8use fedimint_core::db::{Database, DatabaseVersion};
9use fedimint_core::module::{ApiAuth, ApiVersion, CommonModuleInit, ModuleInit, MultiApiVersion};
10use fedimint_core::task::TaskGroup;
11use fedimint_core::{NumPeers, apply, async_trait_maybe_send};
12use fedimint_derive_secret::DerivableSecret;
13use fedimint_logging::LOG_CLIENT;
14use tracing::warn;
15
16use super::ClientContext;
17use super::recovery::RecoveryProgress;
18use crate::db::ClientModuleMigrationFn;
19use crate::module::ClientModule;
20use crate::sm::ModuleNotifier;
21
22pub struct ClientModuleInitArgs<C>
23where
24 C: ClientModuleInit,
25{
26 pub federation_id: FederationId,
27 pub peer_num: usize,
28 pub cfg: <<C as ModuleInit>::Common as CommonModuleInit>::ClientConfig,
29 pub db: Database,
30 pub core_api_version: ApiVersion,
31 pub module_api_version: ApiVersion,
32 pub module_root_secret: DerivableSecret,
33 pub notifier: ModuleNotifier<<<C as ClientModuleInit>::Module as ClientModule>::States>,
34 pub api: DynGlobalApi,
35 pub admin_auth: Option<ApiAuth>,
36 pub module_api: DynModuleApi,
37 pub context: ClientContext<<C as ClientModuleInit>::Module>,
38 pub task_group: TaskGroup,
39}
40
41impl<C> ClientModuleInitArgs<C>
42where
43 C: ClientModuleInit,
44{
45 pub fn federation_id(&self) -> &FederationId {
46 &self.federation_id
47 }
48
49 pub fn peer_num(&self) -> usize {
50 self.peer_num
51 }
52
53 pub fn cfg(&self) -> &<<C as ModuleInit>::Common as CommonModuleInit>::ClientConfig {
54 &self.cfg
55 }
56
57 pub fn db(&self) -> &Database {
58 &self.db
59 }
60
61 pub fn core_api_version(&self) -> &ApiVersion {
62 &self.core_api_version
63 }
64
65 pub fn module_api_version(&self) -> &ApiVersion {
66 &self.module_api_version
67 }
68
69 pub fn module_root_secret(&self) -> &DerivableSecret {
70 &self.module_root_secret
71 }
72
73 pub fn notifier(
74 &self,
75 ) -> &ModuleNotifier<<<C as ClientModuleInit>::Module as ClientModule>::States> {
76 &self.notifier
77 }
78
79 pub fn api(&self) -> &DynGlobalApi {
80 &self.api
81 }
82
83 pub fn admin_auth(&self) -> Option<&ApiAuth> {
84 self.admin_auth.as_ref()
85 }
86
87 pub fn module_api(&self) -> &DynModuleApi {
88 &self.module_api
89 }
90
91 pub fn context(&self) -> ClientContext<<C as ClientModuleInit>::Module> {
98 self.context.clone()
99 }
100
101 pub fn task_group(&self) -> &TaskGroup {
102 &self.task_group
103 }
104}
105
106pub struct ClientModuleRecoverArgs<C>
107where
108 C: ClientModuleInit,
109{
110 pub federation_id: FederationId,
111 pub num_peers: NumPeers,
112 pub cfg: <<C as ModuleInit>::Common as CommonModuleInit>::ClientConfig,
113 pub db: Database,
114 pub core_api_version: ApiVersion,
115 pub module_api_version: ApiVersion,
116 pub module_root_secret: DerivableSecret,
117 pub notifier: ModuleNotifier<<<C as ClientModuleInit>::Module as ClientModule>::States>,
118 pub api: DynGlobalApi,
119 pub admin_auth: Option<ApiAuth>,
120 pub module_api: DynModuleApi,
121 pub context: ClientContext<<C as ClientModuleInit>::Module>,
122 pub progress_tx: tokio::sync::watch::Sender<RecoveryProgress>,
123 pub task_group: TaskGroup,
124}
125
126impl<C> ClientModuleRecoverArgs<C>
127where
128 C: ClientModuleInit,
129{
130 pub fn federation_id(&self) -> &FederationId {
131 &self.federation_id
132 }
133
134 pub fn num_peers(&self) -> NumPeers {
135 self.num_peers
136 }
137
138 pub fn cfg(&self) -> &<<C as ModuleInit>::Common as CommonModuleInit>::ClientConfig {
139 &self.cfg
140 }
141
142 pub fn db(&self) -> &Database {
143 &self.db
144 }
145
146 pub fn task_group(&self) -> &TaskGroup {
147 &self.task_group
148 }
149
150 pub fn core_api_version(&self) -> &ApiVersion {
151 &self.core_api_version
152 }
153
154 pub fn module_api_version(&self) -> &ApiVersion {
155 &self.module_api_version
156 }
157
158 pub fn module_root_secret(&self) -> &DerivableSecret {
159 &self.module_root_secret
160 }
161
162 pub fn notifier(
163 &self,
164 ) -> &ModuleNotifier<<<C as ClientModuleInit>::Module as ClientModule>::States> {
165 &self.notifier
166 }
167
168 pub fn api(&self) -> &DynGlobalApi {
169 &self.api
170 }
171
172 pub fn admin_auth(&self) -> Option<&ApiAuth> {
173 self.admin_auth.as_ref()
174 }
175
176 pub fn module_api(&self) -> &DynModuleApi {
177 &self.module_api
178 }
179
180 pub fn context(&self) -> ClientContext<<C as ClientModuleInit>::Module> {
185 self.context.clone()
186 }
187
188 pub fn update_recovery_progress(&self, progress: RecoveryProgress) {
189 #[allow(clippy::disallowed_methods)]
191 if progress.is_done() {
192 warn!(target: LOG_CLIENT, "Module trying to send a completed recovery progress. Ignoring");
195 } else if progress.is_none() {
196 warn!(target: LOG_CLIENT, "Module trying to send a none recovery progress. Ignoring");
199 } else if self.progress_tx.send(progress).is_err() {
200 warn!(target: LOG_CLIENT, "Module trying to send a recovery progress but nothing is listening");
201 }
202 }
203}
204
205#[apply(async_trait_maybe_send!)]
206pub trait ClientModuleInit: ModuleInit + Sized {
207 type Module: ClientModule;
208
209 fn supported_api_versions(&self) -> MultiApiVersion;
212
213 fn kind() -> ModuleKind {
214 <Self::Module as ClientModule>::kind()
215 }
216
217 async fn recover(
223 &self,
224 _args: &ClientModuleRecoverArgs<Self>,
225 _snapshot: Option<&<Self::Module as ClientModule>::Backup>,
226 ) -> anyhow::Result<()> {
227 warn!(
228 target: LOG_CLIENT,
229 kind = %<Self::Module as ClientModule>::kind(),
230 "Module does not support recovery, completing without doing anything"
231 );
232 Ok(())
233 }
234
235 async fn init(&self, args: &ClientModuleInitArgs<Self>) -> anyhow::Result<Self::Module>;
237
238 fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ClientModuleMigrationFn> {
242 BTreeMap::new()
243 }
244
245 fn used_db_prefixes(&self) -> Option<BTreeSet<u8>> {
255 None
256 }
257}