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