Skip to main content

fedimint_core/module/
mod.rs

1//! Core module system traits and types.
2//!
3//! Fedimint supports modules to allow extending its functionality.
4//! Some of the standard functionality is implemented in form of modules as
5//! well. This rust module houses the core trait
6//! [`fedimint_core::module::ModuleCommon`] used by both the server and client
7//! side module traits. Specific server and client traits exist in their
8//! respective crates.
9//!
10//! The top level server-side types are:
11//!
12//! * `fedimint_server::core::ServerModuleInit`
13//! * `fedimint_server::core::ServerModule`
14//!
15//! Top level client-side types are:
16//!
17//! * `ClientModuleInit` (in `fedimint_client`)
18//! * `ClientModule` (in `fedimint_client`)
19pub mod audit;
20pub mod registry;
21
22use std::collections::{BTreeMap, BTreeSet};
23use std::error::Error;
24use std::fmt::{self, Debug, Formatter};
25use std::marker::PhantomData;
26use std::ops;
27use std::pin::Pin;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicU64, Ordering};
30
31use fedimint_logging::LOG_NET_API;
32use futures::Future;
33use jsonrpsee_core::JsonValue;
34use registry::ModuleRegistry;
35use serde::{Deserialize, Serialize};
36use tracing::Instrument;
37
38// TODO: Make this module public and remove theDkgPeerMessage`pub use` below
39mod version;
40pub use self::version::*;
41use crate::core::{
42    ClientConfig, Decoder, DecoderBuilder, Input, InputError, ModuleConsensusItem,
43    ModuleInstanceId, ModuleKind, Output, OutputError, OutputOutcome,
44};
45use crate::db::{
46    Database, DatabaseError, DatabaseKey, DatabaseKeyWithNotify, DatabaseRecord,
47    DatabaseTransaction,
48};
49use crate::encoding::{Decodable, DecodeError, Encodable};
50use crate::fmt_utils::AbbreviateHexBytes;
51use crate::task::MaybeSend;
52use crate::util::FmtCompact;
53use crate::{Amount, apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync};
54
55#[derive(Debug, PartialEq, Eq)]
56pub struct InputMeta {
57    pub amount: TransactionItemAmounts,
58    pub pub_key: secp256k1::PublicKey,
59}
60
61/// Unit of account for a given amount.
62#[derive(
63    Debug,
64    Clone,
65    Copy,
66    Eq,
67    PartialEq,
68    Hash,
69    PartialOrd,
70    Ord,
71    Deserialize,
72    Serialize,
73    Encodable,
74    Decodable,
75    Default,
76)]
77pub struct AmountUnit(u64);
78
79#[cfg(feature = "uniffi")]
80uniffi::custom_newtype!(AmountUnit, u64);
81
82impl AmountUnit {
83    /// [`AmountUnit`] with id `0` is reserved for the native Bitcoin currency.
84    /// So e.g. for a mainnet Federation it's a real Bitcoin (msats), for a
85    /// signet one it's a Signet msats, etc.
86    pub const BITCOIN: Self = Self(0);
87
88    pub fn is_bitcoin(self) -> bool {
89        self == Self::BITCOIN
90    }
91
92    pub fn new_custom(unit: u64) -> Self {
93        Self(unit)
94    }
95
96    pub const fn bitcoin() -> Self {
97        Self::BITCOIN
98    }
99}
100
101impl fmt::Display for AmountUnit {
102    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103        if self.is_bitcoin() {
104            f.write_str("bitcoin")
105        } else {
106            write!(f, "{}", self.0)
107        }
108    }
109}
110
111#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
112pub struct AmountWithUnit {
113    amounts: Amount,
114    unit: AmountUnit,
115}
116
117/// Multi-unit amount
118///
119/// Basically (potentially) multiple amounts, each of different unit.
120///
121/// Note: implementation must be careful not to add zero-amount
122/// entries, as these could mess up equality comparisons, etc.
123#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
124pub struct Amounts(BTreeMap<AmountUnit, Amount>);
125
126// Note: no `impl ops::DerefMut` as it could easily accidentally break the
127// invariant
128impl ops::Deref for Amounts {
129    type Target = BTreeMap<AmountUnit, Amount>;
130
131    fn deref(&self) -> &Self::Target {
132        &self.0
133    }
134}
135
136impl Amounts {
137    pub const ZERO: Self = Self(BTreeMap::new());
138
139    pub fn new_bitcoin(amount: Amount) -> Self {
140        if amount == Amount::ZERO {
141            Self(BTreeMap::from([]))
142        } else {
143            Self(BTreeMap::from([(AmountUnit::BITCOIN, amount)]))
144        }
145    }
146
147    pub fn new_bitcoin_msats(msats: u64) -> Self {
148        Self::new_bitcoin(Amount::from_msats(msats))
149    }
150
151    pub fn new_custom(unit: AmountUnit, amount: Amount) -> Self {
152        if amount == Amount::ZERO {
153            Self(BTreeMap::from([]))
154        } else {
155            Self(BTreeMap::from([(unit, amount)]))
156        }
157    }
158
159    pub fn checked_add(mut self, rhs: &Self) -> Option<Self> {
160        self.checked_add_mut(rhs)?;
161
162        Some(self)
163    }
164
165    pub fn checked_add_mut(&mut self, rhs: &Self) -> Option<&mut Self> {
166        for (unit, amount) in &rhs.0 {
167            debug_assert!(
168                *amount != Amount::ZERO,
169                "`Amounts` must not add (/remove) zero-amount entries"
170            );
171            let prev = self.0.entry(*unit).or_default();
172
173            *prev = prev.checked_add(*amount)?;
174        }
175
176        Some(self)
177    }
178
179    pub fn checked_add_bitcoin(self, amount: Amount) -> Option<Self> {
180        self.checked_add_unit(amount, AmountUnit::BITCOIN)
181    }
182
183    pub fn checked_add_unit(mut self, amount: Amount, unit: AmountUnit) -> Option<Self> {
184        if amount == Amount::ZERO {
185            return Some(self);
186        }
187
188        let prev = self.0.entry(unit).or_default();
189
190        *prev = prev.checked_add(amount)?;
191
192        Some(self)
193    }
194
195    pub fn checked_sub(&self, other: &Self) -> Option<Self> {
196        let mut result = self.clone();
197
198        for (unit, amount) in &other.0 {
199            if *amount == Amount::ZERO {
200                continue;
201            }
202
203            let prev = result.0.entry(*unit).or_default();
204
205            *prev = prev.checked_sub(*amount)?;
206
207            if *prev == Amount::ZERO {
208                result.0.remove(unit);
209            }
210        }
211
212        Some(result)
213    }
214
215    pub fn remove(&mut self, unit: &AmountUnit) -> Option<Amount> {
216        self.0.remove(unit)
217    }
218
219    pub fn get_bitcoin(&self) -> Amount {
220        self.get(&AmountUnit::BITCOIN).copied().unwrap_or_default()
221    }
222
223    pub fn expect_only_bitcoin(&self) -> Amount {
224        #[allow(clippy::option_if_let_else)] // I like it explicitly split into two cases --dpc
225        match self.get(&AmountUnit::BITCOIN) {
226            Some(amount) => {
227                assert!(
228                    self.len() == 1,
229                    "Amounts expected to contain only bitcoin and no other currencies"
230                );
231                *amount
232            }
233            None => Amount::ZERO,
234        }
235    }
236
237    pub fn iter_units(&self) -> impl Iterator<Item = AmountUnit> {
238        self.0.keys().copied()
239    }
240
241    pub fn units(&self) -> BTreeSet<AmountUnit> {
242        self.0.keys().copied().collect()
243    }
244}
245
246impl IntoIterator for Amounts {
247    type Item = (AmountUnit, Amount);
248
249    type IntoIter = <BTreeMap<AmountUnit, Amount> as IntoIterator>::IntoIter;
250
251    fn into_iter(self) -> Self::IntoIter {
252        self.0.into_iter()
253    }
254}
255
256/// Information about the amount represented by an input or output.
257///
258/// * For **inputs** the amount is funding the transaction while the fee is
259///   consuming funding
260/// * For **outputs** the amount and the fee consume funding
261#[derive(Debug, Clone, Eq, PartialEq, Hash)]
262pub struct TransactionItemAmounts {
263    pub amounts: Amounts,
264    pub fees: Amounts,
265}
266
267impl TransactionItemAmounts {
268    pub fn checked_add(self, rhs: &Self) -> Option<Self> {
269        Some(Self {
270            amounts: self.amounts.checked_add(&rhs.amounts)?,
271            fees: self.fees.checked_add(&rhs.fees)?,
272        })
273    }
274}
275
276impl TransactionItemAmounts {
277    pub const ZERO: Self = Self {
278        amounts: Amounts::ZERO,
279        fees: Amounts::ZERO,
280    };
281}
282
283/// All requests from client to server contain these fields
284#[derive(Debug, Serialize, Deserialize, Clone)]
285pub struct ApiRequest<T> {
286    /// Authentication secret for this API request, if required
287    pub auth: Option<ApiAuth>,
288    /// Parameters required by the API
289    pub params: T,
290}
291
292pub type ApiRequestErased = ApiRequest<JsonValue>;
293
294impl Default for ApiRequestErased {
295    fn default() -> Self {
296        Self {
297            auth: None,
298            params: JsonValue::Null,
299        }
300    }
301}
302
303impl ApiRequestErased {
304    pub fn new<T: Serialize>(params: T) -> Self {
305        Self {
306            auth: None,
307            params: serde_json::to_value(params)
308                .expect("parameter serialization error - this should not happen"),
309        }
310    }
311
312    pub fn to_json(&self) -> JsonValue {
313        serde_json::to_value(self).expect("parameter serialization error - this should not happen")
314    }
315
316    pub fn with_auth(self, auth: ApiAuth) -> Self {
317        Self {
318            auth: Some(auth),
319            params: self.params,
320        }
321    }
322
323    pub fn to_typed<T: serde::de::DeserializeOwned>(
324        self,
325    ) -> Result<ApiRequest<T>, serde_json::Error> {
326        Ok(ApiRequest {
327            auth: self.auth,
328            params: serde_json::from_value::<T>(self.params)?,
329        })
330    }
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub enum ApiMethod {
335    Core(String),
336    Module(ModuleInstanceId, String),
337}
338
339impl fmt::Display for ApiMethod {
340    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341        match self {
342            Self::Core(s) => f.write_str(s),
343            Self::Module(module_id, s) => f.write_fmt(format_args!("{module_id}-{s}")),
344        }
345    }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct IrohApiRequest {
350    pub method: ApiMethod,
351    pub request: ApiRequestErased,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct IrohGatewayRequest {
356    /// REST API route for specifying which action to take
357    pub route: String,
358
359    /// Parameters for the request
360    pub params: Option<serde_json::Value>,
361
362    /// Password for authenticated requests to the gateway
363    pub password: Option<String>,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct IrohGatewayResponse {
368    pub status: u16,
369    pub body: serde_json::Value,
370}
371
372pub const FEDIMINT_API_ALPN: &[u8] = b"FEDIMINT_API_ALPN";
373pub const FEDIMINT_GATEWAY_ALPN: &[u8] = b"FEDIMINT_GATEWAY_ALPN";
374
375/// Authentication secret used to verify privileged API requests.
376///
377/// The inner value is private to prevent timing leaks via direct comparison.
378/// Use [`Self::verify`] for authentication checks.
379#[derive(Clone, Serialize, Deserialize)]
380pub struct ApiAuth(String);
381
382impl ApiAuth {
383    pub fn new(s: String) -> Self {
384        Self(s)
385    }
386
387    pub fn as_str(&self) -> &str {
388        &self.0
389    }
390
391    pub fn verify(&self, password: &str) -> bool {
392        use subtle::ConstantTimeEq as _;
393        bool::from(self.0.as_bytes().ct_eq(password.as_bytes()))
394    }
395}
396
397impl Debug for ApiAuth {
398    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399        write!(f, "ApiAuth(****)")
400    }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct ApiError {
405    pub code: i32,
406    pub message: String,
407}
408
409impl Error for ApiError {}
410
411impl fmt::Display for ApiError {
412    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
413        f.write_fmt(format_args!("{} {}", self.code, self.message))
414    }
415}
416
417pub type ApiResult<T> = Result<T, ApiError>;
418
419impl ApiError {
420    pub fn new(code: i32, message: String) -> Self {
421        Self { code, message }
422    }
423
424    pub fn not_found(message: String) -> Self {
425        Self::new(404, message)
426    }
427
428    pub fn bad_request(message: String) -> Self {
429        Self::new(400, message)
430    }
431
432    pub fn unauthorized() -> Self {
433        Self::new(401, "Invalid authorization".to_string())
434    }
435
436    pub fn server_error(message: String) -> Self {
437        Self::new(500, message)
438    }
439}
440
441impl From<DatabaseError> for ApiError {
442    fn from(err: DatabaseError) -> Self {
443        Self {
444            code: 500,
445            message: format!("API server error when writing to database: {err}"),
446        }
447    }
448}
449
450/// State made available to all API endpoints for handling a request
451pub struct ApiEndpointContext {
452    db: Database,
453    has_auth: bool,
454}
455
456impl ApiEndpointContext {
457    /// `db` should be isolated.
458    pub fn new(db: Database, has_auth: bool) -> Self {
459        Self { db, has_auth }
460    }
461
462    /// Whether the request was authenticated as the guardian who controls this
463    /// fedimint server
464    pub fn has_auth(&self) -> bool {
465        self.has_auth
466    }
467
468    pub fn db(&self) -> Database {
469        self.db.clone()
470    }
471
472    /// Waits for key to be present in database.
473    pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value> + use<K>
474    where
475        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
476    {
477        let db = self.db.clone();
478        // self contains dbtx which is !Send
479        // try removing this and see the error.
480        async move { db.wait_key_exists(&key).await }
481    }
482
483    /// Waits for key to have a value that matches.
484    pub fn wait_value_matches<K>(
485        &self,
486        key: K,
487        matcher: impl Fn(&K::Value) -> bool + Copy,
488    ) -> impl Future<Output = K::Value>
489    where
490        K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
491    {
492        let db = self.db.clone();
493        async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 }
494    }
495}
496
497#[apply(async_trait_maybe_send!)]
498pub trait TypedApiEndpoint {
499    type State: Sync;
500
501    /// example: /transaction
502    const PATH: &'static str;
503    const VERSION: ApiVersion;
504    type Param: serde::de::DeserializeOwned + Send;
505    type Response: serde::Serialize;
506
507    async fn handle<'state, 'context>(
508        state: &'state Self::State,
509        context: &'context mut ApiEndpointContext,
510        request: Self::Param,
511    ) -> Result<Self::Response, ApiError>;
512}
513
514pub use serde_json;
515
516/// Declares an API endpoint that does not require guardian authentication.
517///
518/// # Example
519///
520/// ```rust
521/// # use fedimint_core::module::ApiVersion;
522/// # use fedimint_core::module::{public_api_endpoint, ApiEndpoint, registry::ModuleInstanceId};
523/// struct State;
524///
525/// let _: ApiEndpoint<State> = public_api_endpoint! {
526///     "/foobar",
527///     ApiVersion::new(0, 3),
528///     async |state: &State, _dbtx, params: ()| -> i32 {
529///         Ok(0)
530///     }
531/// };
532/// ```
533#[macro_export]
534macro_rules! __public_api_endpoint {
535    (
536        $path:expr_2021,
537        // API version this endpoint was introduced in, at the current consensus level.
538        $version_introduced:expr_2021,
539        async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
540    ) => {{
541        struct Endpoint;
542
543        #[$crate::apply($crate::async_trait_maybe_send!)]
544        impl $crate::module::TypedApiEndpoint for Endpoint {
545            #[allow(deprecated)]
546            const PATH: &'static str = $path;
547            const VERSION: $crate::module::ApiVersion = $version_introduced;
548            type State = $state_ty;
549            type Param = $param_ty;
550            type Response = $resp_ty;
551
552            async fn handle<'state, 'context>(
553                $state: &'state Self::State,
554                $context: &'context mut $crate::module::ApiEndpointContext,
555                $param: Self::Param,
556            ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> {
557                $body
558            }
559        }
560
561        $crate::module::ApiEndpoint::from_typed::<Endpoint>()
562    }};
563}
564
565pub use __public_api_endpoint as public_api_endpoint;
566
567/// Declares an API endpoint that verifies guardian authentication before
568/// entering the handler body.
569///
570/// An optional token argument between the context and request binds the
571/// verified [`GuardianAuthToken`](crate::net::auth::GuardianAuthToken) for
572/// handlers that pass the proof of authentication to privileged inner
573/// functions.
574#[macro_export]
575macro_rules! __admin_api_endpoint {
576    (
577        $path:expr_2021,
578        $version_introduced:expr_2021,
579        async |$state:ident: &$state_ty:ty, $context:ident, $auth:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
580    ) => {{
581        $crate::module::public_api_endpoint! {
582            $path,
583            $version_introduced,
584            async |$state: &$state_ty, $context, $param: $param_ty| -> $resp_ty {
585                // Match normal function-parameter behavior: duplicate binder
586                // names must fail to compile instead of shadowing.
587                #[allow(unused_variables)]
588                let _ = |$state: (), $context: (), $auth: (), $param: ()| {};
589                let $auth = $crate::net::auth::check_auth($context)?;
590                $body
591            }
592        }
593    }};
594    (
595        $path:expr_2021,
596        $version_introduced:expr_2021,
597        async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
598    ) => {{
599        $crate::module::public_api_endpoint! {
600            $path,
601            $version_introduced,
602            async |$state: &$state_ty, $context, $param: $param_ty| -> $resp_ty {
603                $crate::net::auth::check_auth($context)?;
604                $body
605            }
606        }
607    }};
608}
609
610pub use __admin_api_endpoint as admin_api_endpoint;
611
612use self::registry::ModuleDecoderRegistry;
613
614type HandlerFnReturn<'a> =
615    Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>;
616type HandlerFn<M> = Box<
617    maybe_add_send_sync!(
618        dyn for<'a> Fn(&'a M, ApiEndpointContext, ApiRequestErased) -> HandlerFnReturn<'a>
619    ),
620>;
621
622/// Definition of an API endpoint defined by a module `M`.
623pub struct ApiEndpoint<M> {
624    /// Path under which the API endpoint can be reached. It should start with a
625    /// `/` e.g. `/transaction`. E.g. this API endpoint would be reachable
626    /// under `module_module_instance_id_transaction` depending on the
627    /// module name returned by `[FedertionModule::api_base_name]`.
628    pub path: &'static str,
629    /// Handler for the API call that takes the following arguments:
630    ///   * Reference to the module which defined it
631    ///   * Request parameters parsed into JSON `[Value](serde_json::Value)`
632    pub handler: HandlerFn<M>,
633    /// API version this endpoint was introduced in.
634    pub version: ApiVersion,
635}
636
637/// Global request ID used for logging
638static REQ_ID: AtomicU64 = AtomicU64::new(0);
639
640// <()> is used to avoid specify state.
641impl ApiEndpoint<()> {
642    pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State>
643    where
644        <E as TypedApiEndpoint>::Response: MaybeSend,
645        E::Param: Debug,
646        E::Response: Debug,
647    {
648        async fn handle_request<'state, 'context, E>(
649            state: &'state E::State,
650            context: &'context mut ApiEndpointContext,
651            request: ApiRequest<E::Param>,
652        ) -> Result<E::Response, ApiError>
653        where
654            E: TypedApiEndpoint,
655            E::Param: Debug,
656            E::Response: Debug,
657        {
658            tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request");
659            let result = E::handle(state, context, request.params).await;
660            match &result {
661                Err(err) => {
662                    tracing::warn!(target: LOG_NET_API, path = E::PATH, err = %err.fmt_compact(), "api request error");
663                }
664                _ => {
665                    tracing::trace!(target: LOG_NET_API, path = E::PATH, "api request complete");
666                }
667            }
668            result
669        }
670
671        ApiEndpoint {
672            path: E::PATH,
673            version: E::VERSION,
674            handler: Box::new(|m, mut context, request| {
675                Box::pin(async move {
676                    let request = request
677                        .to_typed()
678                        .map_err(|e| ApiError::bad_request(e.to_string()))?;
679
680                    let span = tracing::info_span!(
681                        target: LOG_NET_API,
682                        "api_req",
683                        id = REQ_ID.fetch_add(1, Ordering::SeqCst),
684                        method = E::PATH,
685                    );
686                    let ret = handle_request::<E>(m, &mut context, request)
687                        .instrument(span)
688                        .await?;
689
690                    Ok(serde_json::to_value(ret).expect("encoding error"))
691                })
692            }),
693        }
694    }
695}
696
697/// Operations common to Server and Client side module gen dyn newtypes
698///
699/// Due to conflict of `impl Trait for T` for both `ServerModuleInit` and
700/// `ClientModuleInit`, we can't really have a `ICommonModuleInit`, so to unify
701/// them in `ModuleInitRegistry` we move the common functionality to be an
702/// interface over their dyn newtype wrappers. A bit weird, but works.
703#[apply(async_trait_maybe_send!)]
704pub trait IDynCommonModuleInit: Debug {
705    fn decoder(&self) -> Decoder;
706
707    fn module_kind(&self) -> ModuleKind;
708
709    fn to_dyn_common(&self) -> DynCommonModuleInit;
710
711    async fn dump_database(
712        &self,
713        dbtx: &mut DatabaseTransaction<'_>,
714        prefix_names: Vec<String>,
715    ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>;
716}
717
718/// Trait implemented by every `*ModuleInit` (server or client side)
719pub trait ModuleInit: Debug + Clone + Send + Sync + 'static {
720    type Common: CommonModuleInit;
721
722    fn dump_database(
723        &self,
724        dbtx: &mut DatabaseTransaction<'_>,
725        prefix_names: Vec<String>,
726    ) -> maybe_add_send!(
727        impl Future<
728            Output = Box<
729                dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_,
730            >,
731        >
732    );
733}
734
735#[apply(async_trait_maybe_send!)]
736impl<T> IDynCommonModuleInit for T
737where
738    T: ModuleInit,
739{
740    fn decoder(&self) -> Decoder {
741        T::Common::decoder()
742    }
743
744    fn module_kind(&self) -> ModuleKind {
745        T::Common::KIND
746    }
747
748    fn to_dyn_common(&self) -> DynCommonModuleInit {
749        DynCommonModuleInit::from_inner(Arc::new(self.clone()))
750    }
751
752    async fn dump_database(
753        &self,
754        dbtx: &mut DatabaseTransaction<'_>,
755        prefix_names: Vec<String>,
756    ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
757        <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await
758    }
759}
760
761dyn_newtype_define!(
762    #[derive(Clone)]
763    pub DynCommonModuleInit(Arc<IDynCommonModuleInit>)
764);
765
766impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit {
767    fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) {
768        self.inner.as_ref()
769    }
770}
771
772impl DynCommonModuleInit {
773    pub fn from_inner(
774        inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
775    ) -> Self {
776        Self { inner }
777    }
778}
779
780/// Logic and constant common between server side and client side modules
781#[apply(async_trait_maybe_send!)]
782pub trait CommonModuleInit: Debug + Sized {
783    const CONSENSUS_VERSION: ModuleConsensusVersion;
784    const KIND: ModuleKind;
785
786    type ClientConfig: ClientConfig;
787
788    fn decoder() -> Decoder;
789}
790
791/// Module associated types required by both client and server
792pub trait ModuleCommon {
793    type ClientConfig: ClientConfig;
794    type Input: Input;
795    type Output: Output;
796    type OutputOutcome: OutputOutcome;
797    type ConsensusItem: ModuleConsensusItem;
798    type InputError: InputError;
799    type OutputError: OutputError;
800
801    fn decoder_builder() -> DecoderBuilder {
802        let mut decoder_builder = Decoder::builder();
803        decoder_builder.with_decodable_type::<Self::ClientConfig>();
804        decoder_builder.with_decodable_type::<Self::Input>();
805        decoder_builder.with_decodable_type::<Self::Output>();
806        decoder_builder.with_decodable_type::<Self::OutputOutcome>();
807        decoder_builder.with_decodable_type::<Self::ConsensusItem>();
808        decoder_builder.with_decodable_type::<Self::InputError>();
809        decoder_builder.with_decodable_type::<Self::OutputError>();
810
811        decoder_builder
812    }
813
814    fn decoder() -> Decoder {
815        Self::decoder_builder().build()
816    }
817}
818
819/// Creates a struct that can be used to make our module-decodable structs
820/// interact with `serde`-based APIs (AlephBFT, jsonrpsee). It creates a wrapper
821/// that holds the data as serialized
822// bytes internally.
823#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
824pub struct SerdeModuleEncoding<T: Encodable + Decodable>(
825    #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>,
826    #[serde(skip)] PhantomData<T>,
827);
828
829/// Same as [`SerdeModuleEncoding`] but uses base64 instead of hex encoding.
830#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
831pub struct SerdeModuleEncodingBase64<T: Encodable + Decodable>(
832    #[serde(with = "::fedimint_core::encoding::as_base64")] Vec<u8>,
833    #[serde(skip)] PhantomData<T>,
834);
835
836impl<T> fmt::Debug for SerdeModuleEncoding<T>
837where
838    T: Encodable + Decodable,
839{
840    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
841        f.write_str("SerdeModuleEncoding(")?;
842        fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
843        f.write_str(")")?;
844        Ok(())
845    }
846}
847
848impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> {
849    fn from(value: &T) -> Self {
850        let mut bytes = vec![];
851        fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
852            .expect("Writing to buffer can never fail");
853        Self(bytes, PhantomData)
854    }
855}
856
857impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> {
858    pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
859        Decodable::consensus_decode_whole(&self.0, modules)
860    }
861
862    /// In cases where we know exactly which module kind we expect but don't
863    /// have access to all decoders this function can be used instead.
864    ///
865    /// Note that it just assumes the decoded module instance id to be valid
866    /// since it cannot validate against the decoder registry. The lack of
867    /// access to a decoder registry also makes decoding structs impossible that
868    /// themselves contain module dyn-types (e.g. a module output containing a
869    /// fedimint transaction).
870    pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
871        let mut reader = std::io::Cursor::new(&self.0);
872        let module_instance = ModuleInstanceId::consensus_decode_partial(
873            &mut reader,
874            &ModuleDecoderRegistry::default(),
875        )?;
876
877        let total_len =
878            u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
879
880        // No recursive module decoding is supported since we give an empty decoder
881        // registry to the decode function
882        decoder.decode_complete(
883            &mut reader,
884            total_len,
885            module_instance,
886            &ModuleRegistry::default(),
887        )
888    }
889}
890
891impl<T: Encodable + Decodable> Encodable for SerdeModuleEncoding<T> {
892    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
893        self.0.consensus_encode(writer)
894    }
895}
896
897impl<T: Encodable + Decodable> Decodable for SerdeModuleEncoding<T> {
898    fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
899        reader: &mut R,
900        modules: &ModuleDecoderRegistry,
901    ) -> Result<Self, DecodeError> {
902        Ok(Self(
903            Vec::<u8>::consensus_decode_partial_from_finite_reader(reader, modules)?,
904            PhantomData,
905        ))
906    }
907}
908
909impl<T> fmt::Debug for SerdeModuleEncodingBase64<T>
910where
911    T: Encodable + Decodable,
912{
913    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
914        f.write_str("SerdeModuleEncoding2(")?;
915        fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
916        f.write_str(")")?;
917        Ok(())
918    }
919}
920
921impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncodingBase64<T> {
922    fn from(value: &T) -> Self {
923        let mut bytes = vec![];
924        fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
925            .expect("Writing to buffer can never fail");
926        Self(bytes, PhantomData)
927    }
928}
929
930impl<T: Encodable + Decodable + 'static> SerdeModuleEncodingBase64<T> {
931    pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
932        Decodable::consensus_decode_whole(&self.0, modules)
933    }
934
935    /// In cases where we know exactly which module kind we expect but don't
936    /// have access to all decoders this function can be used instead.
937    ///
938    /// Note that it just assumes the decoded module instance id to be valid
939    /// since it cannot validate against the decoder registry. The lack of
940    /// access to a decoder registry also makes decoding structs impossible that
941    /// themselves contain module dyn-types (e.g. a module output containing a
942    /// fedimint transaction).
943    pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
944        let mut reader = std::io::Cursor::new(&self.0);
945        let module_instance = ModuleInstanceId::consensus_decode_partial(
946            &mut reader,
947            &ModuleDecoderRegistry::default(),
948        )?;
949
950        let total_len =
951            u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
952
953        // No recursive module decoding is supported since we give an empty decoder
954        // registry to the decode function
955        decoder.decode_complete(
956            &mut reader,
957            total_len,
958            module_instance,
959            &ModuleRegistry::default(),
960        )
961    }
962}