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}