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}