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