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}