fedimint_server_core/lib.rs
1//! Fedimint Core Server module interface
2//!
3//! Fedimint supports externally implemented modules.
4//!
5//! This (Rust) module defines common interoperability types
6//! and functionality that are only used on the server side.
7
8pub mod config;
9pub mod dashboard_ui;
10mod init;
11pub mod migration;
12pub mod net;
13pub mod setup_ui;
14
15use std::any::Any;
16use std::fmt::Debug;
17use std::sync::Arc;
18
19use fedimint_core::core::{
20 Decoder, DynInput, DynInputError, DynModuleConsensusItem, DynOutput, DynOutputError,
21 DynOutputOutcome, ModuleInstanceId, ModuleKind,
22};
23use fedimint_core::db::DatabaseTransaction;
24use fedimint_core::module::audit::Audit;
25use fedimint_core::module::registry::{ModuleDecoderRegistry, ModuleRegistry};
26use fedimint_core::module::{
27 ApiEndpoint, ApiEndpointContext, ApiRequestErased, CommonModuleInit, InputMeta, ModuleCommon,
28 ModuleInit, TransactionItemAmount,
29};
30use fedimint_core::{InPoint, OutPoint, PeerId, apply, async_trait_maybe_send, dyn_newtype_define};
31pub use init::*;
32
33#[apply(async_trait_maybe_send!)]
34pub trait ServerModule: Debug + Sized {
35 type Common: ModuleCommon;
36
37 type Init: ServerModuleInit;
38
39 fn module_kind() -> ModuleKind {
40 // Note: All modules should define kinds as &'static str, so this doesn't
41 // allocate
42 <Self::Init as ModuleInit>::Common::KIND
43 }
44
45 /// Returns a decoder for the following associated types of this module:
46 /// * `ClientConfig`
47 /// * `Input`
48 /// * `Output`
49 /// * `OutputOutcome`
50 /// * `ConsensusItem`
51 /// * `InputError`
52 /// * `OutputError`
53 fn decoder() -> Decoder {
54 Self::Common::decoder_builder().build()
55 }
56
57 /// This module's contribution to the next consensus proposal. This method
58 /// is only guaranteed to be called once every few seconds. Consensus items
59 /// are not meant to be latency critical; do not create them as
60 /// a response to a processed transaction. Only use consensus items to
61 /// establish consensus on a value that is required to verify
62 /// transactions, like unix time, block heights and feerates, and model all
63 /// other state changes trough transactions. The intention for this method
64 /// is to always return all available consensus items even if they are
65 /// redundant while process_consensus_item returns an error for the
66 /// redundant proposals.
67 ///
68 /// If you think you actually do require latency critical consensus items or
69 /// have trouble designing your module in order to avoid them please contact
70 /// the Fedimint developers.
71 async fn consensus_proposal<'a>(
72 &'a self,
73 dbtx: &mut DatabaseTransaction<'_>,
74 ) -> Vec<<Self::Common as ModuleCommon>::ConsensusItem>;
75
76 /// This function is called once for every consensus item. The function
77 /// should return Ok if and only if the consensus item changes
78 /// the system state. *Therefore this method should return an error in case
79 /// of merely redundant consensus items such that they will be purged from
80 /// the history of the federation.* This enables consensus_proposal to
81 /// return all available consensus item without wasting disk
82 /// space with redundant consensus items.
83 async fn process_consensus_item<'a, 'b>(
84 &'a self,
85 dbtx: &mut DatabaseTransaction<'b>,
86 consensus_item: <Self::Common as ModuleCommon>::ConsensusItem,
87 peer_id: PeerId,
88 ) -> anyhow::Result<()>;
89
90 // Use this function to parallelise stateless cryptographic verification of
91 // inputs across a transaction. All inputs of a transaction are verified
92 // before any input is processed.
93 fn verify_input(
94 &self,
95 _input: &<Self::Common as ModuleCommon>::Input,
96 ) -> Result<(), <Self::Common as ModuleCommon>::InputError> {
97 Ok(())
98 }
99
100 /// Try to spend a transaction input. On success all necessary updates will
101 /// be part of the database transaction. On failure (e.g. double spend)
102 /// the database transaction is rolled back and the operation will take
103 /// no effect.
104 async fn process_input<'a, 'b, 'c>(
105 &'a self,
106 dbtx: &mut DatabaseTransaction<'c>,
107 input: &'b <Self::Common as ModuleCommon>::Input,
108 in_point: InPoint,
109 ) -> Result<InputMeta, <Self::Common as ModuleCommon>::InputError>;
110
111 /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
112 /// all necessary updates to the database will be part of the database
113 /// transaction. On failure (e.g. double spend) the database transaction
114 /// is rolled back and the operation will take no effect.
115 ///
116 /// The supplied `out_point` identifies the operation (e.g. a peg-out or
117 /// note issuance) and can be used to retrieve its outcome later using
118 /// `output_status`.
119 async fn process_output<'a, 'b>(
120 &'a self,
121 dbtx: &mut DatabaseTransaction<'b>,
122 output: &'a <Self::Common as ModuleCommon>::Output,
123 out_point: OutPoint,
124 ) -> Result<TransactionItemAmount, <Self::Common as ModuleCommon>::OutputError>;
125
126 /// **Deprecated**: Modules should not be using it. Instead, they should
127 /// implement their own custom endpoints with semantics, versioning,
128 /// serialization, etc. that suits them. Potentially multiple or none.
129 ///
130 /// Depending on the module this might contain data needed by the client to
131 /// access funds or give an estimate of when funds will be available.
132 ///
133 /// Returns `None` if the output is unknown, **NOT** if it is just not ready
134 /// yet.
135 ///
136 /// Since this has become deprecated you may return `None` even if the
137 /// output is known as long as the output outcome is not used inside the
138 /// module.
139 #[deprecated(note = "https://github.com/fedimint/fedimint/issues/6671")]
140 async fn output_status(
141 &self,
142 dbtx: &mut DatabaseTransaction<'_>,
143 out_point: OutPoint,
144 ) -> Option<<Self::Common as ModuleCommon>::OutputOutcome>;
145
146 /// Verify submission-only checks for an input
147 ///
148 /// Most modules should not need to know or implement it, so the default
149 /// implementation just returns OK.
150 ///
151 /// In special circumstances it is useful to enforce requirements on the
152 /// included transaction outside of the consensus, in a similar way
153 /// Bitcoin enforces mempool policies.
154 ///
155 /// This functionality might be removed in the future versions, as more
156 /// checks become part of the consensus, so it is advised not to use it.
157 #[doc(hidden)]
158 async fn verify_input_submission<'a, 'b, 'c>(
159 &'a self,
160 _dbtx: &mut DatabaseTransaction<'c>,
161 _input: &'b <Self::Common as ModuleCommon>::Input,
162 ) -> Result<(), <Self::Common as ModuleCommon>::InputError> {
163 Ok(())
164 }
165
166 /// Verify submission-only checks for an output
167 ///
168 /// See [`Self::verify_input_submission`] for more information.
169 #[doc(hidden)]
170 async fn verify_output_submission<'a, 'b>(
171 &'a self,
172 _dbtx: &mut DatabaseTransaction<'b>,
173 _output: &'a <Self::Common as ModuleCommon>::Output,
174 _out_point: OutPoint,
175 ) -> Result<(), <Self::Common as ModuleCommon>::OutputError> {
176 Ok(())
177 }
178
179 /// Queries the database and returns all assets and liabilities of the
180 /// module.
181 ///
182 /// Summing over all modules, if liabilities > assets then an error has
183 /// occurred in the database and consensus should halt.
184 async fn audit(
185 &self,
186 dbtx: &mut DatabaseTransaction<'_>,
187 audit: &mut Audit,
188 module_instance_id: ModuleInstanceId,
189 );
190
191 /// Returns a list of custom API endpoints defined by the module. These are
192 /// made available both to users as well as to other modules. They thus
193 /// should be deterministic, only dependant on their input and the
194 /// current epoch.
195 fn api_endpoints(&self) -> Vec<ApiEndpoint<Self>>;
196}
197
198/// Backend side module interface
199///
200/// Server side Fedimint module needs to implement this trait.
201#[apply(async_trait_maybe_send!)]
202pub trait IServerModule: Debug {
203 fn as_any(&self) -> &dyn Any;
204
205 /// Returns the decoder belonging to the server module
206 fn decoder(&self) -> Decoder;
207
208 fn module_kind(&self) -> ModuleKind;
209
210 /// This module's contribution to the next consensus proposal
211 async fn consensus_proposal(
212 &self,
213 dbtx: &mut DatabaseTransaction<'_>,
214 module_instance_id: ModuleInstanceId,
215 ) -> Vec<DynModuleConsensusItem>;
216
217 /// This function is called once for every consensus item. The function
218 /// returns an error if any only if the consensus item does not change
219 /// our state and therefore may be safely discarded by the atomic broadcast.
220 async fn process_consensus_item<'a, 'b>(
221 &self,
222 dbtx: &mut DatabaseTransaction<'a>,
223 consensus_item: &'b DynModuleConsensusItem,
224 peer_id: PeerId,
225 ) -> anyhow::Result<()>;
226
227 // Use this function to parallelise stateless cryptographic verification of
228 // inputs across a transaction. All inputs of a transaction are verified
229 // before any input is processed.
230 fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError>;
231
232 /// Try to spend a transaction input. On success all necessary updates will
233 /// be part of the database transaction. On failure (e.g. double spend)
234 /// the database transaction is rolled back and the operation will take
235 /// no effect.
236 async fn process_input<'a, 'b, 'c>(
237 &'a self,
238 dbtx: &mut DatabaseTransaction<'c>,
239 input: &'b DynInput,
240 in_point: InPoint,
241 ) -> Result<InputMeta, DynInputError>;
242
243 /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
244 /// all necessary updates to the database will be part of the database
245 /// transaction. On failure (e.g. double spend) the database transaction
246 /// is rolled back and the operation will take no effect.
247 ///
248 /// The supplied `out_point` identifies the operation (e.g. a peg-out or
249 /// note issuance) and can be used to retrieve its outcome later using
250 /// `output_status`.
251 async fn process_output<'a>(
252 &self,
253 dbtx: &mut DatabaseTransaction<'a>,
254 output: &DynOutput,
255 out_point: OutPoint,
256 ) -> Result<TransactionItemAmount, DynOutputError>;
257
258 /// See [`ServerModule::verify_input_submission`]
259 #[doc(hidden)]
260 async fn verify_input_submission<'a, 'b, 'c>(
261 &'a self,
262 dbtx: &mut DatabaseTransaction<'c>,
263 input: &'b DynInput,
264 ) -> Result<(), DynInputError>;
265
266 /// See [`ServerModule::verify_output_submission`]
267 #[doc(hidden)]
268 async fn verify_output_submission<'a>(
269 &self,
270 _dbtx: &mut DatabaseTransaction<'a>,
271 _output: &DynOutput,
272 _out_point: OutPoint,
273 ) -> Result<(), DynOutputError>;
274
275 /// See [`ServerModule::output_status`]
276 #[deprecated(note = "https://github.com/fedimint/fedimint/issues/6671")]
277 async fn output_status(
278 &self,
279 dbtx: &mut DatabaseTransaction<'_>,
280 out_point: OutPoint,
281 module_instance_id: ModuleInstanceId,
282 ) -> Option<DynOutputOutcome>;
283
284 /// Queries the database and returns all assets and liabilities of the
285 /// module.
286 ///
287 /// Summing over all modules, if liabilities > assets then an error has
288 /// occurred in the database and consensus should halt.
289 async fn audit(
290 &self,
291 dbtx: &mut DatabaseTransaction<'_>,
292 audit: &mut Audit,
293 module_instance_id: ModuleInstanceId,
294 );
295
296 /// Returns a list of custom API endpoints defined by the module. These are
297 /// made available both to users as well as to other modules. They thus
298 /// should be deterministic, only dependant on their input and the
299 /// current epoch.
300 fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>>;
301}
302
303dyn_newtype_define!(
304 #[derive(Clone)]
305 pub DynServerModule(Arc<IServerModule>)
306);
307
308#[apply(async_trait_maybe_send!)]
309impl<T> IServerModule for T
310where
311 T: ServerModule + 'static + Sync,
312{
313 fn decoder(&self) -> Decoder {
314 <T::Common as ModuleCommon>::decoder_builder().build()
315 }
316
317 fn as_any(&self) -> &dyn Any {
318 self
319 }
320
321 fn module_kind(&self) -> ModuleKind {
322 <Self as ServerModule>::module_kind()
323 }
324
325 /// This module's contribution to the next consensus proposal
326 async fn consensus_proposal(
327 &self,
328 dbtx: &mut DatabaseTransaction<'_>,
329 module_instance_id: ModuleInstanceId,
330 ) -> Vec<DynModuleConsensusItem> {
331 <Self as ServerModule>::consensus_proposal(self, dbtx)
332 .await
333 .into_iter()
334 .map(|v| DynModuleConsensusItem::from_typed(module_instance_id, v))
335 .collect()
336 }
337
338 /// This function is called once for every consensus item. The function
339 /// returns an error if any only if the consensus item does not change
340 /// our state and therefore may be safely discarded by the atomic broadcast.
341 async fn process_consensus_item<'a, 'b>(
342 &self,
343 dbtx: &mut DatabaseTransaction<'a>,
344 consensus_item: &'b DynModuleConsensusItem,
345 peer_id: PeerId,
346 ) -> anyhow::Result<()> {
347 <Self as ServerModule>::process_consensus_item(
348 self,
349 dbtx,
350 Clone::clone(
351 consensus_item.as_any()
352 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::ConsensusItem>()
353 .expect("incorrect consensus item type passed to module plugin"),
354 ),
355 peer_id
356 )
357 .await
358 }
359
360 // Use this function to parallelise stateless cryptographic verification of
361 // inputs across a transaction. All inputs of a transaction are verified
362 // before any input is processed.
363 fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError> {
364 <Self as ServerModule>::verify_input(
365 self,
366 input
367 .as_any()
368 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
369 .expect("incorrect input type passed to module plugin"),
370 )
371 .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
372 }
373
374 /// Try to spend a transaction input. On success all necessary updates will
375 /// be part of the database transaction. On failure (e.g. double spend)
376 /// the database transaction is rolled back and the operation will take
377 /// no effect.
378 async fn process_input<'a, 'b, 'c>(
379 &'a self,
380 dbtx: &mut DatabaseTransaction<'c>,
381 input: &'b DynInput,
382 in_point: InPoint,
383 ) -> Result<InputMeta, DynInputError> {
384 <Self as ServerModule>::process_input(
385 self,
386 dbtx,
387 input
388 .as_any()
389 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
390 .expect("incorrect input type passed to module plugin"),
391 in_point,
392 )
393 .await
394 .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
395 }
396
397 /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
398 /// all necessary updates to the database will be part of the database
399 /// transaction. On failure (e.g. double spend) the database transaction
400 /// is rolled back and the operation will take no effect.
401 ///
402 /// The supplied `out_point` identifies the operation (e.g. a peg-out or
403 /// note issuance) and can be used to retrieve its outcome later using
404 /// `output_status`.
405 async fn process_output<'a>(
406 &self,
407 dbtx: &mut DatabaseTransaction<'a>,
408 output: &DynOutput,
409 out_point: OutPoint,
410 ) -> Result<TransactionItemAmount, DynOutputError> {
411 <Self as ServerModule>::process_output(
412 self,
413 dbtx,
414 output
415 .as_any()
416 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
417 .expect("incorrect output type passed to module plugin"),
418 out_point,
419 )
420 .await
421 .map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
422 }
423
424 async fn verify_input_submission<'a, 'b, 'c>(
425 &'a self,
426 dbtx: &mut DatabaseTransaction<'c>,
427 input: &'b DynInput,
428 ) -> Result<(), DynInputError> {
429 <Self as ServerModule>::verify_input_submission(
430 self,
431 dbtx,
432 input
433 .as_any()
434 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
435 .expect("incorrect input type passed to module plugin"),
436 )
437 .await
438 .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
439 }
440
441 async fn verify_output_submission<'a>(
442 &self,
443 dbtx: &mut DatabaseTransaction<'a>,
444 output: &DynOutput,
445 out_point: OutPoint,
446 ) -> Result<(), DynOutputError> {
447 <Self as ServerModule>::verify_output_submission(
448 self,
449 dbtx,
450 output
451 .as_any()
452 .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
453 .expect("incorrect output type passed to module plugin"),
454 out_point,
455 )
456 .await
457 .map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
458 }
459
460 /// See [`ServerModule::output_status`]
461 async fn output_status(
462 &self,
463 dbtx: &mut DatabaseTransaction<'_>,
464 out_point: OutPoint,
465 module_instance_id: ModuleInstanceId,
466 ) -> Option<DynOutputOutcome> {
467 #[allow(deprecated)]
468 <Self as ServerModule>::output_status(self, dbtx, out_point)
469 .await
470 .map(|v| DynOutputOutcome::from_typed(module_instance_id, v))
471 }
472
473 /// Queries the database and returns all assets and liabilities of the
474 /// module.
475 ///
476 /// Summing over all modules, if liabilities > assets then an error has
477 /// occurred in the database and consensus should halt.
478 async fn audit(
479 &self,
480 dbtx: &mut DatabaseTransaction<'_>,
481 audit: &mut Audit,
482 module_instance_id: ModuleInstanceId,
483 ) {
484 <Self as ServerModule>::audit(self, dbtx, audit, module_instance_id).await;
485 }
486
487 fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>> {
488 <Self as ServerModule>::api_endpoints(self)
489 .into_iter()
490 .map(|ApiEndpoint { path, handler }| ApiEndpoint {
491 path,
492 handler: Box::new(
493 move |module: &DynServerModule,
494 context: ApiEndpointContext<'_>,
495 value: ApiRequestErased| {
496 let typed_module = module
497 .as_any()
498 .downcast_ref::<T>()
499 .expect("the dispatcher should always call with the right module");
500 Box::pin(handler(typed_module, context, value))
501 },
502 ),
503 })
504 .collect()
505 }
506}
507
508/// Collection of server modules
509pub type ServerModuleRegistry = ModuleRegistry<DynServerModule>;
510
511pub trait ServerModuleRegistryExt {
512 fn decoder_registry(&self) -> ModuleDecoderRegistry;
513}
514
515impl ServerModuleRegistryExt for ServerModuleRegistry {
516 /// Generate a `ModuleDecoderRegistry` from this `ModuleRegistry`
517 fn decoder_registry(&self) -> ModuleDecoderRegistry {
518 // TODO: cache decoders
519 self.iter_modules()
520 .map(|(id, kind, module)| (id, kind.clone(), module.decoder()))
521 .collect::<ModuleDecoderRegistry>()
522 }
523}