1pub 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
38mod 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#[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 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#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
111pub struct Amounts(BTreeMap<AmountUnit, Amount>);
112
113impl 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)] 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#[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#[derive(Debug, Serialize, Deserialize, Clone)]
272pub struct ApiRequest<T> {
273 pub auth: Option<ApiAuth>,
275 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 pub route: String,
345
346 pub params: Option<serde_json::Value>,
348
349 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#[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
437pub struct ApiEndpointContext {
439 db: Database,
440 has_auth: bool,
441 request_auth: Option<ApiAuth>,
442}
443
444impl ApiEndpointContext {
445 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 pub fn request_auth(&self) -> Option<ApiAuth> {
457 self.request_auth.clone()
458 }
459
460 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 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 async move { db.wait_key_exists(&key).await }
479 }
480
481 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 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#[macro_export]
530macro_rules! __api_endpoint {
531 (
532 $path:expr_2021,
533 $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 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
577pub struct ApiEndpoint<M> {
579 pub path: &'static str,
584 pub handler: HandlerFn<M>,
588}
589
590static REQ_ID: AtomicU64 = AtomicU64::new(0);
592
593impl 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#[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
670pub 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#[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
743pub 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#[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#[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 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 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 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 decoder.decode_complete(
908 &mut reader,
909 total_len,
910 module_instance,
911 &ModuleRegistry::default(),
912 )
913 }
914}