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