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 net;
14pub mod setup_ui;
15
16use std::any::Any;
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, CommonModuleInit, InputMeta, ModuleCommon,
29    ModuleInit, TransactionItemAmount,
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<TransactionItemAmount, <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
199/// Backend side module interface
200///
201/// Server side Fedimint module needs to implement this trait.
202#[apply(async_trait_maybe_send!)]
203pub trait IServerModule: Debug {
204    fn as_any(&self) -> &dyn Any;
205
206    /// Returns the decoder belonging to the server module
207    fn decoder(&self) -> Decoder;
208
209    fn module_kind(&self) -> ModuleKind;
210
211    /// This module's contribution to the next consensus proposal
212    async fn consensus_proposal(
213        &self,
214        dbtx: &mut DatabaseTransaction<'_>,
215        module_instance_id: ModuleInstanceId,
216    ) -> Vec<DynModuleConsensusItem>;
217
218    /// This function is called once for every consensus item. The function
219    /// returns an error if any only if the consensus item does not change
220    /// our state and therefore may be safely discarded by the atomic broadcast.
221    async fn process_consensus_item<'a, 'b>(
222        &self,
223        dbtx: &mut DatabaseTransaction<'a>,
224        consensus_item: &'b DynModuleConsensusItem,
225        peer_id: PeerId,
226    ) -> anyhow::Result<()>;
227
228    // Use this function to parallelise stateless cryptographic verification of
229    // inputs across a transaction. All inputs of a transaction are verified
230    // before any input is processed.
231    fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError>;
232
233    /// Try to spend a transaction input. On success all necessary updates will
234    /// be part of the database transaction. On failure (e.g. double spend)
235    /// the database transaction is rolled back and the operation will take
236    /// no effect.
237    async fn process_input<'a, 'b, 'c>(
238        &'a self,
239        dbtx: &mut DatabaseTransaction<'c>,
240        input: &'b DynInput,
241        in_point: InPoint,
242    ) -> Result<InputMeta, DynInputError>;
243
244    /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
245    /// all necessary updates to the database will be part of the database
246    /// transaction. On failure (e.g. double spend) the database transaction
247    /// is rolled back and the operation will take no effect.
248    ///
249    /// The supplied `out_point` identifies the operation (e.g. a peg-out or
250    /// note issuance) and can be used to retrieve its outcome later using
251    /// `output_status`.
252    async fn process_output<'a>(
253        &self,
254        dbtx: &mut DatabaseTransaction<'a>,
255        output: &DynOutput,
256        out_point: OutPoint,
257    ) -> Result<TransactionItemAmount, DynOutputError>;
258
259    /// See [`ServerModule::verify_input_submission`]
260    #[doc(hidden)]
261    async fn verify_input_submission<'a, 'b, 'c>(
262        &'a self,
263        dbtx: &mut DatabaseTransaction<'c>,
264        input: &'b DynInput,
265    ) -> Result<(), DynInputError>;
266
267    /// See [`ServerModule::verify_output_submission`]
268    #[doc(hidden)]
269    async fn verify_output_submission<'a>(
270        &self,
271        _dbtx: &mut DatabaseTransaction<'a>,
272        _output: &DynOutput,
273        _out_point: OutPoint,
274    ) -> Result<(), DynOutputError>;
275
276    /// See [`ServerModule::output_status`]
277    #[deprecated(note = "https://github.com/fedimint/fedimint/issues/6671")]
278    async fn output_status(
279        &self,
280        dbtx: &mut DatabaseTransaction<'_>,
281        out_point: OutPoint,
282        module_instance_id: ModuleInstanceId,
283    ) -> Option<DynOutputOutcome>;
284
285    /// Queries the database and returns all assets and liabilities of the
286    /// module.
287    ///
288    /// Summing over all modules, if liabilities > assets then an error has
289    /// occurred in the database and consensus should halt.
290    async fn audit(
291        &self,
292        dbtx: &mut DatabaseTransaction<'_>,
293        audit: &mut Audit,
294        module_instance_id: ModuleInstanceId,
295    );
296
297    /// Returns a list of custom API endpoints defined by the module. These are
298    /// made available both to users as well as to other modules. They thus
299    /// should be deterministic, only dependant on their input and the
300    /// current epoch.
301    fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>>;
302}
303
304dyn_newtype_define!(
305    #[derive(Clone)]
306    pub DynServerModule(Arc<IServerModule>)
307);
308
309#[apply(async_trait_maybe_send!)]
310impl<T> IServerModule for T
311where
312    T: ServerModule + 'static + Sync,
313{
314    fn decoder(&self) -> Decoder {
315        <T::Common as ModuleCommon>::decoder_builder().build()
316    }
317
318    fn as_any(&self) -> &dyn Any {
319        self
320    }
321
322    fn module_kind(&self) -> ModuleKind {
323        <Self as ServerModule>::module_kind()
324    }
325
326    /// This module's contribution to the next consensus proposal
327    async fn consensus_proposal(
328        &self,
329        dbtx: &mut DatabaseTransaction<'_>,
330        module_instance_id: ModuleInstanceId,
331    ) -> Vec<DynModuleConsensusItem> {
332        <Self as ServerModule>::consensus_proposal(self, dbtx)
333            .await
334            .into_iter()
335            .map(|v| DynModuleConsensusItem::from_typed(module_instance_id, v))
336            .collect()
337    }
338
339    /// This function is called once for every consensus item. The function
340    /// returns an error if any only if the consensus item does not change
341    /// our state and therefore may be safely discarded by the atomic broadcast.
342    async fn process_consensus_item<'a, 'b>(
343        &self,
344        dbtx: &mut DatabaseTransaction<'a>,
345        consensus_item: &'b DynModuleConsensusItem,
346        peer_id: PeerId,
347    ) -> anyhow::Result<()> {
348        <Self as ServerModule>::process_consensus_item(
349            self,
350            dbtx,
351            Clone::clone(
352                consensus_item.as_any()
353                    .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::ConsensusItem>()
354                    .expect("incorrect consensus item type passed to module plugin"),
355            ),
356            peer_id
357        )
358        .await
359    }
360
361    // Use this function to parallelise stateless cryptographic verification of
362    // inputs across a transaction. All inputs of a transaction are verified
363    // before any input is processed.
364    fn verify_input(&self, input: &DynInput) -> Result<(), DynInputError> {
365        <Self as ServerModule>::verify_input(
366            self,
367            input
368                .as_any()
369                .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
370                .expect("incorrect input type passed to module plugin"),
371        )
372        .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
373    }
374
375    /// Try to spend a transaction input. On success all necessary updates will
376    /// be part of the database transaction. On failure (e.g. double spend)
377    /// the database transaction is rolled back and the operation will take
378    /// no effect.
379    async fn process_input<'a, 'b, 'c>(
380        &'a self,
381        dbtx: &mut DatabaseTransaction<'c>,
382        input: &'b DynInput,
383        in_point: InPoint,
384    ) -> Result<InputMeta, DynInputError> {
385        <Self as ServerModule>::process_input(
386            self,
387            dbtx,
388            input
389                .as_any()
390                .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
391                .expect("incorrect input type passed to module plugin"),
392            in_point,
393        )
394        .await
395        .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
396    }
397
398    /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
399    /// all necessary updates to the database will be part of the database
400    /// transaction. On failure (e.g. double spend) the database transaction
401    /// is rolled back and the operation will take no effect.
402    ///
403    /// The supplied `out_point` identifies the operation (e.g. a peg-out or
404    /// note issuance) and can be used to retrieve its outcome later using
405    /// `output_status`.
406    async fn process_output<'a>(
407        &self,
408        dbtx: &mut DatabaseTransaction<'a>,
409        output: &DynOutput,
410        out_point: OutPoint,
411    ) -> Result<TransactionItemAmount, DynOutputError> {
412        <Self as ServerModule>::process_output(
413            self,
414            dbtx,
415            output
416                .as_any()
417                .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
418                .expect("incorrect output type passed to module plugin"),
419            out_point,
420        )
421        .await
422        .map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
423    }
424
425    async fn verify_input_submission<'a, 'b, 'c>(
426        &'a self,
427        dbtx: &mut DatabaseTransaction<'c>,
428        input: &'b DynInput,
429    ) -> Result<(), DynInputError> {
430        <Self as ServerModule>::verify_input_submission(
431            self,
432            dbtx,
433            input
434                .as_any()
435                .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Input>()
436                .expect("incorrect input type passed to module plugin"),
437        )
438        .await
439        .map_err(|v| DynInputError::from_typed(input.module_instance_id(), v))
440    }
441
442    async fn verify_output_submission<'a>(
443        &self,
444        dbtx: &mut DatabaseTransaction<'a>,
445        output: &DynOutput,
446        out_point: OutPoint,
447    ) -> Result<(), DynOutputError> {
448        <Self as ServerModule>::verify_output_submission(
449            self,
450            dbtx,
451            output
452                .as_any()
453                .downcast_ref::<<<Self as ServerModule>::Common as ModuleCommon>::Output>()
454                .expect("incorrect output type passed to module plugin"),
455            out_point,
456        )
457        .await
458        .map_err(|v| DynOutputError::from_typed(output.module_instance_id(), v))
459    }
460
461    /// See [`ServerModule::output_status`]
462    async fn output_status(
463        &self,
464        dbtx: &mut DatabaseTransaction<'_>,
465        out_point: OutPoint,
466        module_instance_id: ModuleInstanceId,
467    ) -> Option<DynOutputOutcome> {
468        #[allow(deprecated)]
469        <Self as ServerModule>::output_status(self, dbtx, out_point)
470            .await
471            .map(|v| DynOutputOutcome::from_typed(module_instance_id, v))
472    }
473
474    /// Queries the database and returns all assets and liabilities of the
475    /// module.
476    ///
477    /// Summing over all modules, if liabilities > assets then an error has
478    /// occurred in the database and consensus should halt.
479    async fn audit(
480        &self,
481        dbtx: &mut DatabaseTransaction<'_>,
482        audit: &mut Audit,
483        module_instance_id: ModuleInstanceId,
484    ) {
485        <Self as ServerModule>::audit(self, dbtx, audit, module_instance_id).await;
486    }
487
488    fn api_endpoints(&self) -> Vec<ApiEndpoint<DynServerModule>> {
489        <Self as ServerModule>::api_endpoints(self)
490            .into_iter()
491            .map(|ApiEndpoint { path, handler }| ApiEndpoint {
492                path,
493                handler: Box::new(
494                    move |module: &DynServerModule,
495                          context: ApiEndpointContext<'_>,
496                          value: ApiRequestErased| {
497                        let typed_module = module
498                            .as_any()
499                            .downcast_ref::<T>()
500                            .expect("the dispatcher should always call with the right module");
501                        Box::pin(handler(typed_module, context, value))
502                    },
503                ),
504            })
505            .collect()
506    }
507}
508
509/// Collection of server modules
510pub type ServerModuleRegistry = ModuleRegistry<DynServerModule>;
511
512pub trait ServerModuleRegistryExt {
513    fn decoder_registry(&self) -> ModuleDecoderRegistry;
514}
515
516impl ServerModuleRegistryExt for ServerModuleRegistry {
517    /// Generate a `ModuleDecoderRegistry` from this `ModuleRegistry`
518    fn decoder_registry(&self) -> ModuleDecoderRegistry {
519        // TODO: cache decoders
520        self.iter_modules()
521            .map(|(id, kind, module)| (id, kind.clone(), module.decoder()))
522            .collect::<ModuleDecoderRegistry>()
523    }
524}