fedimint_core/module/
mod.rs

1//! Fedimint supports modules to allow extending it's functionality.
2//! Some of the standard functionality is implemented in form of modules as
3//! well.
4//!
5//! The top level server-side types are:
6//!
7//! * [`fedimint_core::module::ModuleInit`]
8//! * [`fedimint_core::module::ServerModule`]
9//!
10//! Top level client-side types are:
11//!
12//! * `ClientModuleInit` (in `fedimint_client`)
13//! * `ClientModule` (in `fedimint_client`)
14pub mod audit;
15pub mod registry;
16
17use std::collections::BTreeMap;
18use std::fmt::{self, Debug, Formatter};
19use std::marker::{self, PhantomData};
20use std::pin::Pin;
21use std::sync::atomic::{AtomicU64, Ordering};
22use std::sync::Arc;
23
24use fedimint_logging::LOG_NET_API;
25use futures::Future;
26use jsonrpsee_core::JsonValue;
27use registry::ModuleRegistry;
28use serde::{Deserialize, Serialize};
29use tracing::Instrument;
30
31// TODO: Make this module public and remove the wildcard `pub use` below
32mod version;
33pub use self::version::*;
34use crate::config::{
35    ClientModuleConfig, ConfigGenModuleParams, DkgPeerMsg, ModuleInitParams, ServerModuleConfig,
36    ServerModuleConsensusConfig,
37};
38use crate::core::{
39    ClientConfig, Decoder, DecoderBuilder, Input, InputError, ModuleConsensusItem,
40    ModuleInstanceId, ModuleKind, Output, OutputError, OutputOutcome,
41};
42use crate::db::{
43    Committable, CoreMigrationFn, Database, DatabaseKey, DatabaseKeyWithNotify, DatabaseRecord,
44    DatabaseTransaction, DatabaseVersion,
45};
46use crate::encoding::{Decodable, DecodeError, Encodable};
47use crate::fmt_utils::AbbreviateHexBytes;
48use crate::module::audit::Audit;
49use crate::net::peers::MuxPeerConnections;
50use crate::server::DynServerModule;
51use crate::task::{MaybeSend, TaskGroup};
52use crate::{
53    apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync, Amount, NumPeers, OutPoint,
54    PeerId,
55};
56
57#[derive(Debug, PartialEq, Eq)]
58pub struct InputMeta {
59    pub amount: TransactionItemAmount,
60    pub pub_key: secp256k1::PublicKey,
61}
62
63/// Information about the amount represented by an input or output.
64///
65/// * For **inputs** the amount is funding the transaction while the fee is
66///   consuming funding
67/// * For **outputs** the amount and the fee consume funding
68#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
69pub struct TransactionItemAmount {
70    pub amount: Amount,
71    pub fee: Amount,
72}
73
74impl TransactionItemAmount {
75    pub const ZERO: Self = Self {
76        amount: Amount::ZERO,
77        fee: Amount::ZERO,
78    };
79}
80
81/// All requests from client to server contain these fields
82#[derive(Debug, Serialize, Deserialize, Clone)]
83pub struct ApiRequest<T> {
84    /// Hashed user password if the API requires authentication
85    pub auth: Option<ApiAuth>,
86    /// Parameters required by the API
87    pub params: T,
88}
89
90pub type ApiRequestErased = ApiRequest<JsonValue>;
91
92impl Default for ApiRequestErased {
93    fn default() -> Self {
94        Self {
95            auth: None,
96            params: JsonValue::Null,
97        }
98    }
99}
100
101impl ApiRequestErased {
102    pub fn new<T: Serialize>(params: T) -> Self {
103        Self {
104            auth: None,
105            params: serde_json::to_value(params)
106                .expect("parameter serialization error - this should not happen"),
107        }
108    }
109
110    pub fn to_json(&self) -> JsonValue {
111        serde_json::to_value(self).expect("parameter serialization error - this should not happen")
112    }
113
114    pub fn with_auth(self, auth: ApiAuth) -> Self {
115        Self {
116            auth: Some(auth),
117            params: self.params,
118        }
119    }
120
121    pub fn to_typed<T: serde::de::DeserializeOwned>(
122        self,
123    ) -> Result<ApiRequest<T>, serde_json::Error> {
124        Ok(ApiRequest {
125            auth: self.auth,
126            params: serde_json::from_value::<T>(self.params)?,
127        })
128    }
129}
130
131/// Authentication uses the hashed user password in PHC format
132#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
133pub struct ApiAuth(pub String);
134
135impl Debug for ApiAuth {
136    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137        write!(f, "ApiAuth(****)")
138    }
139}
140
141#[derive(Debug, Clone)]
142pub struct ApiError {
143    pub code: i32,
144    pub message: String,
145}
146
147impl ApiError {
148    pub fn new(code: i32, message: String) -> Self {
149        Self { code, message }
150    }
151
152    pub fn not_found(message: String) -> Self {
153        Self::new(404, message)
154    }
155
156    pub fn bad_request(message: String) -> Self {
157        Self::new(400, message)
158    }
159
160    pub fn unauthorized() -> Self {
161        Self::new(401, "Invalid authorization".to_string())
162    }
163
164    pub fn server_error(message: String) -> Self {
165        Self::new(500, message)
166    }
167}
168
169/// State made available to all API endpoints for handling a request
170pub struct ApiEndpointContext<'dbtx> {
171    db: Database,
172    dbtx: DatabaseTransaction<'dbtx, Committable>,
173    has_auth: bool,
174    request_auth: Option<ApiAuth>,
175}
176
177impl<'a> ApiEndpointContext<'a> {
178    /// `db` and `dbtx` should be isolated.
179    pub fn new(
180        db: Database,
181        dbtx: DatabaseTransaction<'a, Committable>,
182        has_auth: bool,
183        request_auth: Option<ApiAuth>,
184    ) -> Self {
185        Self {
186            db,
187            dbtx,
188            has_auth,
189            request_auth,
190        }
191    }
192
193    /// Database tx handle, will be committed
194    pub fn dbtx<'s, 'mtx>(&'s mut self) -> DatabaseTransaction<'mtx, Committable>
195    where
196        'a: 'mtx,
197        's: 'mtx,
198    {
199        // dbtx is already isolated.
200        self.dbtx.to_ref()
201    }
202
203    /// Returns the auth set on the request (regardless of whether it was
204    /// correct)
205    pub fn request_auth(&self) -> Option<ApiAuth> {
206        self.request_auth.clone()
207    }
208
209    /// Whether the request was authenticated as the guardian who controls this
210    /// fedimint server
211    pub fn has_auth(&self) -> bool {
212        self.has_auth
213    }
214
215    pub fn db(&self) -> Database {
216        self.db.clone()
217    }
218
219    /// Waits for key to be present in database.
220    pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value>
221    where
222        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
223    {
224        let db = self.db.clone();
225        // self contains dbtx which is !Send
226        // try removing this and see the error.
227        async move { db.wait_key_exists(&key).await }
228    }
229
230    /// Waits for key to have a value that matches.
231    pub fn wait_value_matches<K>(
232        &self,
233        key: K,
234        matcher: impl Fn(&K::Value) -> bool + Copy,
235    ) -> impl Future<Output = K::Value>
236    where
237        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
238    {
239        let db = self.db.clone();
240        async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 }
241    }
242
243    /// Attempts to commit the dbtx or returns an `ApiError`
244    pub async fn commit_tx_result(self, path: &'static str) -> Result<(), ApiError> {
245        self.dbtx.commit_tx_result().await.map_err(|err| {
246            tracing::warn!(
247                target: fedimint_logging::LOG_NET_API,
248                path,
249                "API server error when writing to database: {:?}",
250                err
251            );
252            ApiError {
253                code: 500,
254                message: "API server error when writing to database".to_string(),
255            }
256        })
257    }
258}
259
260#[apply(async_trait_maybe_send!)]
261pub trait TypedApiEndpoint {
262    type State: Sync;
263
264    /// example: /transaction
265    const PATH: &'static str;
266
267    type Param: serde::de::DeserializeOwned + Send;
268    type Response: serde::Serialize;
269
270    async fn handle<'state, 'context, 'dbtx>(
271        state: &'state Self::State,
272        context: &'context mut ApiEndpointContext<'dbtx>,
273        request: Self::Param,
274    ) -> Result<Self::Response, ApiError>
275    where
276        'dbtx: 'context;
277}
278
279pub use serde_json;
280
281/// # Example
282///
283/// ```rust
284/// # use fedimint_core::module::ApiVersion;
285/// # use fedimint_core::module::{api_endpoint, ApiEndpoint, registry::ModuleInstanceId};
286/// struct State;
287///
288/// let _: ApiEndpoint<State> = api_endpoint! {
289///     "/foobar",
290///     ApiVersion::new(0, 3),
291///     async |state: &State, _dbtx, params: ()| -> i32 {
292///         Ok(0)
293///     }
294/// };
295/// ```
296#[macro_export]
297macro_rules! __api_endpoint {
298    (
299        $path:expr,
300        // Api Version this endpoint was introduced in, at the current consensus level
301        // Currently for documentation purposes only.
302        $version_introduced:expr,
303        async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
304    ) => {{
305        struct Endpoint;
306
307        #[$crate::apply($crate::async_trait_maybe_send!)]
308        impl $crate::module::TypedApiEndpoint for Endpoint {
309            const PATH: &'static str = $path;
310            type State = $state_ty;
311            type Param = $param_ty;
312            type Response = $resp_ty;
313
314            async fn handle<'state, 'context, 'dbtx>(
315                $state: &'state Self::State,
316                $context: &'context mut $crate::module::ApiEndpointContext<'dbtx>,
317                $param: Self::Param,
318            ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> {
319                {
320                    // just to enforce the correct type
321                    const __API_VERSION: $crate::module::ApiVersion = $version_introduced;
322                }
323                $body
324            }
325        }
326
327        $crate::module::ApiEndpoint::from_typed::<Endpoint>()
328    }};
329}
330
331pub use __api_endpoint as api_endpoint;
332use fedimint_core::config::DkgResult;
333
334use self::registry::ModuleDecoderRegistry;
335
336type HandlerFnReturn<'a> =
337    Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>;
338type HandlerFn<M> = Box<
339    maybe_add_send_sync!(
340        dyn for<'a> Fn(&'a M, ApiEndpointContext<'a>, ApiRequestErased) -> HandlerFnReturn<'a>
341    ),
342>;
343
344/// Definition of an API endpoint defined by a module `M`.
345pub struct ApiEndpoint<M> {
346    /// Path under which the API endpoint can be reached. It should start with a
347    /// `/` e.g. `/transaction`. E.g. this API endpoint would be reachable
348    /// under `module_module_instance_id_transaction` depending on the
349    /// module name returned by `[FedertionModule::api_base_name]`.
350    pub path: &'static str,
351    /// Handler for the API call that takes the following arguments:
352    ///   * Reference to the module which defined it
353    ///   * Request parameters parsed into JSON `[Value](serde_json::Value)`
354    pub handler: HandlerFn<M>,
355}
356
357/// Global request ID used for logging
358static REQ_ID: AtomicU64 = AtomicU64::new(0);
359
360// <()> is used to avoid specify state.
361impl ApiEndpoint<()> {
362    pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State>
363    where
364        <E as TypedApiEndpoint>::Response: MaybeSend,
365        E::Param: Debug,
366        E::Response: Debug,
367    {
368        async fn handle_request<'state, 'context, 'dbtx, E>(
369            state: &'state E::State,
370            context: &'context mut ApiEndpointContext<'dbtx>,
371            request: ApiRequest<E::Param>,
372        ) -> Result<E::Response, ApiError>
373        where
374            'dbtx: 'context,
375            E: TypedApiEndpoint,
376            E::Param: Debug,
377            E::Response: Debug,
378        {
379            tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request");
380            let result = E::handle(state, context, request.params).await;
381            if let Err(error) = &result {
382                tracing::warn!(target: LOG_NET_API, path = E::PATH, ?error, "api request error");
383            } else {
384                tracing::debug!(target: LOG_NET_API, path = E::PATH, "api request complete");
385            }
386            result
387        }
388
389        ApiEndpoint {
390            path: E::PATH,
391            handler: Box::new(|m, mut context, request| {
392                Box::pin(async {
393                    let request = request
394                        .to_typed()
395                        .map_err(|e| ApiError::bad_request(e.to_string()))?;
396
397                    let span = tracing::info_span!(
398                        target: LOG_NET_API,
399                        "api_req",
400                        id = REQ_ID.fetch_add(1, Ordering::SeqCst),
401                        method = E::PATH,
402                    );
403                    let ret = handle_request::<E>(m, &mut context, request)
404                        .instrument(span)
405                        .await?;
406
407                    context.commit_tx_result(E::PATH).await?;
408
409                    Ok(serde_json::to_value(ret).expect("encoding error"))
410                })
411            }),
412        }
413    }
414}
415
416/// Operations common to Server and Client side module gen dyn newtypes
417///
418/// Due to conflict of `impl Trait for T` for both `ServerModuleInit` and
419/// `ClientModuleInit`, we can't really have a `ICommonModuleInit`, so to unify
420/// them in `ModuleInitRegistry` we move the common functionality to be an
421/// interface over their dyn newtype wrappers. A bit weird, but works.
422#[apply(async_trait_maybe_send!)]
423pub trait IDynCommonModuleInit: Debug {
424    fn decoder(&self) -> Decoder;
425
426    fn module_kind(&self) -> ModuleKind;
427
428    fn to_dyn_common(&self) -> DynCommonModuleInit;
429
430    async fn dump_database(
431        &self,
432        dbtx: &mut DatabaseTransaction<'_>,
433        prefix_names: Vec<String>,
434    ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>;
435}
436
437/// Trait implemented by every `*ModuleInit` (server or client side)
438pub trait ModuleInit: Debug + Clone + Send + Sync + 'static {
439    type Common: CommonModuleInit;
440
441    fn dump_database(
442        &self,
443        dbtx: &mut DatabaseTransaction<'_>,
444        prefix_names: Vec<String>,
445    ) -> maybe_add_send!(
446        impl Future<
447            Output = Box<
448                dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_,
449            >,
450        >
451    );
452}
453
454#[apply(async_trait_maybe_send!)]
455impl<T> IDynCommonModuleInit for T
456where
457    T: ModuleInit,
458{
459    fn decoder(&self) -> Decoder {
460        T::Common::decoder()
461    }
462
463    fn module_kind(&self) -> ModuleKind {
464        T::Common::KIND
465    }
466
467    fn to_dyn_common(&self) -> DynCommonModuleInit {
468        DynCommonModuleInit::from_inner(Arc::new(self.clone()))
469    }
470
471    async fn dump_database(
472        &self,
473        dbtx: &mut DatabaseTransaction<'_>,
474        prefix_names: Vec<String>,
475    ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
476        <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await
477    }
478}
479
480/// Interface for Module Generation
481///
482/// This trait contains the methods responsible for the module's
483/// - initialization
484/// - config generation
485/// - config validation
486///
487/// Once the module configuration is ready, the module can be instantiated via
488/// `[Self::init]`.
489#[apply(async_trait_maybe_send!)]
490pub trait IServerModuleInit: IDynCommonModuleInit {
491    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static);
492
493    fn supported_api_versions(&self) -> SupportedModuleApiVersions;
494
495    /// Initialize the [`DynServerModule`] instance from its config
496    async fn init(
497        &self,
498        peer_num: NumPeers,
499        cfg: ServerModuleConfig,
500        db: Database,
501        task_group: &TaskGroup,
502        our_peer_id: PeerId,
503    ) -> anyhow::Result<DynServerModule>;
504
505    fn validate_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<()>;
506
507    fn trusted_dealer_gen(
508        &self,
509        peers: &[PeerId],
510        params: &ConfigGenModuleParams,
511    ) -> BTreeMap<PeerId, ServerModuleConfig>;
512
513    async fn distributed_gen(
514        &self,
515        peers: &PeerHandle,
516        params: &ConfigGenModuleParams,
517    ) -> DkgResult<ServerModuleConfig>;
518
519    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
520
521    fn get_client_config(
522        &self,
523        module_instance_id: ModuleInstanceId,
524        config: &ServerModuleConsensusConfig,
525    ) -> anyhow::Result<ClientModuleConfig>;
526
527    /// Retrieves the migrations map from the server module to be applied to the
528    /// database before the module is initialized. The migrations map is
529    /// indexed on the from version.
530    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, CoreMigrationFn>;
531}
532
533dyn_newtype_define!(
534    #[derive(Clone)]
535    pub DynCommonModuleInit(Arc<IDynCommonModuleInit>)
536);
537
538impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit {
539    fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) {
540        self.inner.as_ref()
541    }
542}
543
544impl DynCommonModuleInit {
545    pub fn from_inner(
546        inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
547    ) -> Self {
548        Self { inner }
549    }
550}
551
552dyn_newtype_define!(
553    #[derive(Clone)]
554    pub DynServerModuleInit(Arc<IServerModuleInit>)
555);
556
557impl AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static> for DynServerModuleInit {
558    fn as_ref(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
559        self.inner.as_common()
560    }
561}
562
563/// Logic and constant common between server side and client side modules
564#[apply(async_trait_maybe_send!)]
565pub trait CommonModuleInit: Debug + Sized {
566    const CONSENSUS_VERSION: ModuleConsensusVersion;
567    const KIND: ModuleKind;
568
569    type ClientConfig: ClientConfig;
570
571    fn decoder() -> Decoder;
572}
573
574pub struct ServerModuleInitArgs<S>
575where
576    S: ServerModuleInit,
577{
578    cfg: ServerModuleConfig,
579    db: Database,
580    task_group: TaskGroup,
581    our_peer_id: PeerId,
582    num_peers: NumPeers,
583    // ClientModuleInitArgs needs a bound because sometimes we need
584    // to pass associated-types data, so let's just put it here right away
585    _marker: marker::PhantomData<S>,
586}
587
588impl<S> ServerModuleInitArgs<S>
589where
590    S: ServerModuleInit,
591{
592    pub fn cfg(&self) -> &ServerModuleConfig {
593        &self.cfg
594    }
595
596    pub fn db(&self) -> &Database {
597        &self.db
598    }
599
600    pub fn num_peers(&self) -> NumPeers {
601        self.num_peers
602    }
603
604    pub fn task_group(&self) -> &TaskGroup {
605        &self.task_group
606    }
607
608    pub fn our_peer_id(&self) -> PeerId {
609        self.our_peer_id
610    }
611}
612/// Module Generation trait with associated types
613///
614/// Needs to be implemented by module generation type
615///
616/// For examples, take a look at one of the `MintConfigGenerator`,
617/// `WalletConfigGenerator`, or `LightningConfigGenerator` structs.
618#[apply(async_trait_maybe_send!)]
619pub trait ServerModuleInit: ModuleInit + Sized {
620    type Params: ModuleInitParams;
621
622    /// Version of the module consensus supported by this implementation given a
623    /// certain [`CoreConsensusVersion`].
624    ///
625    /// Refer to [`ModuleConsensusVersion`] for more information about
626    /// versioning.
627    ///
628    /// One module implementation ([`ServerModuleInit`] of a given
629    /// [`ModuleKind`]) can potentially implement multiple versions of the
630    /// consensus, and depending on the config module instance config,
631    /// instantiate the desired one. This method should expose all the
632    /// available versions, purely for information, setup UI and sanity
633    /// checking purposes.
634    fn versions(&self, core: CoreConsensusVersion) -> &[ModuleConsensusVersion];
635
636    fn supported_api_versions(&self) -> SupportedModuleApiVersions;
637
638    fn kind() -> ModuleKind {
639        <Self as ModuleInit>::Common::KIND
640    }
641
642    /// Initialize the [`DynServerModule`] instance from its config
643    async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<DynServerModule>;
644
645    fn parse_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<Self::Params> {
646        params.to_typed::<Self::Params>()
647    }
648
649    fn trusted_dealer_gen(
650        &self,
651        peers: &[PeerId],
652        params: &ConfigGenModuleParams,
653    ) -> BTreeMap<PeerId, ServerModuleConfig>;
654
655    async fn distributed_gen(
656        &self,
657        peer: &PeerHandle,
658        params: &ConfigGenModuleParams,
659    ) -> DkgResult<ServerModuleConfig>;
660
661    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>;
662
663    /// Converts the consensus config into the client config
664    fn get_client_config(
665        &self,
666        config: &ServerModuleConsensusConfig,
667    ) -> anyhow::Result<<<Self as ModuleInit>::Common as CommonModuleInit>::ClientConfig>;
668
669    /// Retrieves the migrations map from the server module to be applied to the
670    /// database before the module is initialized. The migrations map is
671    /// indexed on the from version.
672    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, CoreMigrationFn> {
673        BTreeMap::new()
674    }
675}
676
677#[apply(async_trait_maybe_send!)]
678impl<T> IServerModuleInit for T
679where
680    T: ServerModuleInit + 'static + Sync,
681{
682    fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) {
683        self
684    }
685
686    fn supported_api_versions(&self) -> SupportedModuleApiVersions {
687        <Self as ServerModuleInit>::supported_api_versions(self)
688    }
689
690    async fn init(
691        &self,
692        num_peers: NumPeers,
693        cfg: ServerModuleConfig,
694        db: Database,
695        task_group: &TaskGroup,
696        our_peer_id: PeerId,
697    ) -> anyhow::Result<DynServerModule> {
698        <Self as ServerModuleInit>::init(
699            self,
700            &ServerModuleInitArgs {
701                num_peers,
702                cfg,
703                db,
704                task_group: task_group.clone(),
705                our_peer_id,
706                _marker: PhantomData,
707            },
708        )
709        .await
710    }
711
712    fn validate_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<()> {
713        <Self as ServerModuleInit>::parse_params(self, params)?;
714        Ok(())
715    }
716
717    fn trusted_dealer_gen(
718        &self,
719        peers: &[PeerId],
720        params: &ConfigGenModuleParams,
721    ) -> BTreeMap<PeerId, ServerModuleConfig> {
722        <Self as ServerModuleInit>::trusted_dealer_gen(self, peers, params)
723    }
724
725    async fn distributed_gen(
726        &self,
727        peers: &PeerHandle,
728        params: &ConfigGenModuleParams,
729    ) -> DkgResult<ServerModuleConfig> {
730        <Self as ServerModuleInit>::distributed_gen(self, peers, params).await
731    }
732
733    fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()> {
734        <Self as ServerModuleInit>::validate_config(self, identity, config)
735    }
736
737    fn get_client_config(
738        &self,
739        module_instance_id: ModuleInstanceId,
740        config: &ServerModuleConsensusConfig,
741    ) -> anyhow::Result<ClientModuleConfig> {
742        ClientModuleConfig::from_typed(
743            module_instance_id,
744            <Self as ServerModuleInit>::kind(),
745            config.version,
746            <Self as ServerModuleInit>::get_client_config(self, config)?,
747        )
748    }
749
750    fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, CoreMigrationFn> {
751        <Self as ServerModuleInit>::get_database_migrations(self)
752    }
753}
754
755/// Module associated types required by both client and server
756pub trait ModuleCommon {
757    type ClientConfig: ClientConfig;
758    type Input: Input;
759    type Output: Output;
760    type OutputOutcome: OutputOutcome;
761    type ConsensusItem: ModuleConsensusItem;
762    type InputError: InputError;
763    type OutputError: OutputError;
764
765    fn decoder_builder() -> DecoderBuilder {
766        let mut decoder_builder = Decoder::builder();
767        decoder_builder.with_decodable_type::<Self::ClientConfig>();
768        decoder_builder.with_decodable_type::<Self::Input>();
769        decoder_builder.with_decodable_type::<Self::Output>();
770        decoder_builder.with_decodable_type::<Self::OutputOutcome>();
771        decoder_builder.with_decodable_type::<Self::ConsensusItem>();
772        decoder_builder.with_decodable_type::<Self::InputError>();
773        decoder_builder.with_decodable_type::<Self::OutputError>();
774
775        decoder_builder
776    }
777
778    fn decoder() -> Decoder {
779        Self::decoder_builder().build()
780    }
781}
782
783#[apply(async_trait_maybe_send!)]
784pub trait ServerModule: Debug + Sized {
785    type Common: ModuleCommon;
786
787    type Init: ServerModuleInit;
788
789    fn module_kind() -> ModuleKind {
790        // Note: All modules should define kinds as &'static str, so this doesn't
791        // allocate
792        <Self::Init as ModuleInit>::Common::KIND
793    }
794
795    /// Returns a decoder for the following associated types of this module:
796    /// * `ClientConfig`
797    /// * `Input`
798    /// * `Output`
799    /// * `OutputOutcome`
800    /// * `ConsensusItem`
801    /// * `InputError`
802    /// * `OutputError`
803    fn decoder() -> Decoder {
804        Self::Common::decoder_builder().build()
805    }
806
807    /// This module's contribution to the next consensus proposal. This method
808    /// is only guaranteed to be called once every few seconds. Consensus items
809    /// are not meant to be latency critical; do not create them as
810    /// a response to a processed transaction. Only use consensus items to
811    /// establish consensus on a value that is required to verify
812    /// transactions, like unix time, block heights and feerates, and model all
813    /// other state changes trough transactions. The intention for this method
814    /// is to always return all available consensus items even if they are
815    /// redundant while process_consensus_item returns an error for the
816    /// redundant proposals.
817    ///
818    /// If you think you actually do require latency critical consensus items or
819    /// have trouble designing your module in order to avoid them please contact
820    /// the Fedimint developers.
821    async fn consensus_proposal<'a>(
822        &'a self,
823        dbtx: &mut DatabaseTransaction<'_>,
824    ) -> Vec<<Self::Common as ModuleCommon>::ConsensusItem>;
825
826    /// This function is called once for every consensus item. The function
827    /// should return Ok if and only if the consensus item changes
828    /// the system state. *Therefore this method should return an error in case
829    /// of merely redundant consensus items such that they will be purged from
830    /// the history of the federation.* This enables consensus_proposal to
831    /// return all available consensus item without wasting disk
832    /// space with redundant consensus items.
833    async fn process_consensus_item<'a, 'b>(
834        &'a self,
835        dbtx: &mut DatabaseTransaction<'b>,
836        consensus_item: <Self::Common as ModuleCommon>::ConsensusItem,
837        peer_id: PeerId,
838    ) -> anyhow::Result<()>;
839
840    // Use this function to parallelise stateless cryptographic verification of
841    // inputs across a transaction. All inputs of a transaction are verified
842    // before any input is processed.
843    fn verify_input(
844        &self,
845        _input: &<Self::Common as ModuleCommon>::Input,
846    ) -> Result<(), <Self::Common as ModuleCommon>::InputError> {
847        Ok(())
848    }
849
850    /// Try to spend a transaction input. On success all necessary updates will
851    /// be part of the database transaction. On failure (e.g. double spend)
852    /// the database transaction is rolled back and the operation will take
853    /// no effect.
854    async fn process_input<'a, 'b, 'c>(
855        &'a self,
856        dbtx: &mut DatabaseTransaction<'c>,
857        input: &'b <Self::Common as ModuleCommon>::Input,
858    ) -> Result<InputMeta, <Self::Common as ModuleCommon>::InputError>;
859
860    /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success
861    /// all necessary updates to the database will be part of the database
862    /// transaction. On failure (e.g. double spend) the database transaction
863    /// is rolled back and the operation will take no effect.
864    ///
865    /// The supplied `out_point` identifies the operation (e.g. a peg-out or
866    /// note issuance) and can be used to retrieve its outcome later using
867    /// `output_status`.
868    async fn process_output<'a, 'b>(
869        &'a self,
870        dbtx: &mut DatabaseTransaction<'b>,
871        output: &'a <Self::Common as ModuleCommon>::Output,
872        out_point: OutPoint,
873    ) -> Result<TransactionItemAmount, <Self::Common as ModuleCommon>::OutputError>;
874
875    /// Retrieve the current status of the output. Depending on the module this
876    /// might contain data needed by the client to access funds or give an
877    /// estimate of when funds will be available. Returns `None` if the
878    /// output is unknown, **NOT** if it is just not ready yet.
879    async fn output_status(
880        &self,
881        dbtx: &mut DatabaseTransaction<'_>,
882        out_point: OutPoint,
883    ) -> Option<<Self::Common as ModuleCommon>::OutputOutcome>;
884
885    /// Queries the database and returns all assets and liabilities of the
886    /// module.
887    ///
888    /// Summing over all modules, if liabilities > assets then an error has
889    /// occurred in the database and consensus should halt.
890    async fn audit(
891        &self,
892        dbtx: &mut DatabaseTransaction<'_>,
893        audit: &mut Audit,
894        module_instance_id: ModuleInstanceId,
895    );
896
897    /// Returns a list of custom API endpoints defined by the module. These are
898    /// made available both to users as well as to other modules. They thus
899    /// should be deterministic, only dependant on their input and the
900    /// current epoch.
901    fn api_endpoints(&self) -> Vec<ApiEndpoint<Self>>;
902}
903
904/// Creates a struct that can be used to make our module-decodable structs
905/// interact with `serde`-based APIs (AlephBFT, jsonrpsee). It creates a wrapper
906/// that holds the data as serialized
907// bytes internally.
908#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
909pub struct SerdeModuleEncoding<T: Encodable + Decodable>(
910    #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>,
911    #[serde(skip)] PhantomData<T>,
912);
913
914impl<T> fmt::Debug for SerdeModuleEncoding<T>
915where
916    T: Encodable + Decodable,
917{
918    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
919        f.write_str("SerdeModuleEncoding(")?;
920        fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
921        f.write_str(")")?;
922        Ok(())
923    }
924}
925
926impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> {
927    fn from(value: &T) -> Self {
928        let mut bytes = vec![];
929        fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
930            .expect("Writing to buffer can never fail");
931        Self(bytes, PhantomData)
932    }
933}
934
935impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> {
936    pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
937        let mut reader = std::io::Cursor::new(&self.0);
938        Decodable::consensus_decode(&mut reader, modules)
939    }
940
941    /// In cases where we know exactly which module kind we expect but don't
942    /// have access to all decoders this function can be used instead.
943    ///
944    /// Note that it just assumes the decoded module instance id to be valid
945    /// since it cannot validate against the decoder registry. The lack of
946    /// access to a decoder registry also makes decoding structs impossible that
947    /// themselves contain module dyn-types (e.g. a module output containing a
948    /// fedimint transaction).
949    pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
950        let mut reader = std::io::Cursor::new(&self.0);
951        let module_instance =
952            ModuleInstanceId::consensus_decode(&mut reader, &ModuleDecoderRegistry::default())?;
953
954        let total_len = u64::consensus_decode(&mut reader, &ModuleDecoderRegistry::default())?;
955
956        // No recursive module decoding is supported since we give an empty decoder
957        // registry to the decode function
958        decoder.decode_complete(
959            &mut reader,
960            total_len,
961            module_instance,
962            &ModuleRegistry::default(),
963        )
964    }
965}
966
967/// A handle passed to [`ServerModuleInit::distributed_gen`]
968///
969/// This struct encapsulates dkg data that the module should not have a direct
970/// access to, and implements higher level dkg operations available to the
971/// module to complete its distributed initialization inside the federation.
972#[non_exhaustive]
973pub struct PeerHandle<'a> {
974    // TODO: this whole type should be a part of a `fedimint-server` and fields here inaccessible
975    // to outside crates, but until `ServerModule` is not in `fedimint-server` this is impossible
976    #[doc(hidden)]
977    pub connections: &'a MuxPeerConnections<(ModuleInstanceId, String), DkgPeerMsg>,
978    #[doc(hidden)]
979    pub module_instance_id: ModuleInstanceId,
980    #[doc(hidden)]
981    pub our_id: PeerId,
982    #[doc(hidden)]
983    pub peers: Vec<PeerId>,
984}
985
986impl<'a> PeerHandle<'a> {
987    pub fn new(
988        connections: &'a MuxPeerConnections<(ModuleInstanceId, String), DkgPeerMsg>,
989        module_instance_id: ModuleInstanceId,
990        our_id: PeerId,
991        peers: Vec<PeerId>,
992    ) -> Self {
993        Self {
994            connections,
995            module_instance_id,
996            our_id,
997            peers,
998        }
999    }
1000
1001    pub fn peer_ids(&self) -> &[PeerId] {
1002        self.peers.as_slice()
1003    }
1004}