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