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
79#[cfg(feature = "uniffi")]
80uniffi::custom_newtype!(AmountUnit, u64);
81
82impl AmountUnit {
83 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
101#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
102pub struct AmountWithUnit {
103 amounts: Amount,
104 unit: AmountUnit,
105}
106
107#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
114pub struct Amounts(BTreeMap<AmountUnit, Amount>);
115
116impl ops::Deref for Amounts {
119 type Target = BTreeMap<AmountUnit, Amount>;
120
121 fn deref(&self) -> &Self::Target {
122 &self.0
123 }
124}
125
126impl Amounts {
127 pub const ZERO: Self = Self(BTreeMap::new());
128
129 pub fn new_bitcoin(amount: Amount) -> Self {
130 if amount == Amount::ZERO {
131 Self(BTreeMap::from([]))
132 } else {
133 Self(BTreeMap::from([(AmountUnit::BITCOIN, amount)]))
134 }
135 }
136
137 pub fn new_bitcoin_msats(msats: u64) -> Self {
138 Self::new_bitcoin(Amount::from_msats(msats))
139 }
140
141 pub fn new_custom(unit: AmountUnit, amount: Amount) -> Self {
142 if amount == Amount::ZERO {
143 Self(BTreeMap::from([]))
144 } else {
145 Self(BTreeMap::from([(unit, amount)]))
146 }
147 }
148
149 pub fn checked_add(mut self, rhs: &Self) -> Option<Self> {
150 self.checked_add_mut(rhs)?;
151
152 Some(self)
153 }
154
155 pub fn checked_add_mut(&mut self, rhs: &Self) -> Option<&mut Self> {
156 for (unit, amount) in &rhs.0 {
157 debug_assert!(
158 *amount != Amount::ZERO,
159 "`Amounts` must not add (/remove) zero-amount entries"
160 );
161 let prev = self.0.entry(*unit).or_default();
162
163 *prev = prev.checked_add(*amount)?;
164 }
165
166 Some(self)
167 }
168
169 pub fn checked_add_bitcoin(self, amount: Amount) -> Option<Self> {
170 self.checked_add_unit(amount, AmountUnit::BITCOIN)
171 }
172
173 pub fn checked_add_unit(mut self, amount: Amount, unit: AmountUnit) -> Option<Self> {
174 if amount == Amount::ZERO {
175 return Some(self);
176 }
177
178 let prev = self.0.entry(unit).or_default();
179
180 *prev = prev.checked_add(amount)?;
181
182 Some(self)
183 }
184
185 pub fn checked_sub(&self, other: &Self) -> Option<Self> {
186 let mut result = self.clone();
187
188 for (unit, amount) in &other.0 {
189 if *amount == Amount::ZERO {
190 continue;
191 }
192
193 let prev = result.0.entry(*unit).or_default();
194
195 *prev = prev.checked_sub(*amount)?;
196
197 if *prev == Amount::ZERO {
198 result.0.remove(unit);
199 }
200 }
201
202 Some(result)
203 }
204
205 pub fn remove(&mut self, unit: &AmountUnit) -> Option<Amount> {
206 self.0.remove(unit)
207 }
208
209 pub fn get_bitcoin(&self) -> Amount {
210 self.get(&AmountUnit::BITCOIN).copied().unwrap_or_default()
211 }
212
213 pub fn expect_only_bitcoin(&self) -> Amount {
214 #[allow(clippy::option_if_let_else)] match self.get(&AmountUnit::BITCOIN) {
216 Some(amount) => {
217 assert!(
218 self.len() == 1,
219 "Amounts expected to contain only bitcoin and no other currencies"
220 );
221 *amount
222 }
223 None => Amount::ZERO,
224 }
225 }
226
227 pub fn iter_units(&self) -> impl Iterator<Item = AmountUnit> {
228 self.0.keys().copied()
229 }
230
231 pub fn units(&self) -> BTreeSet<AmountUnit> {
232 self.0.keys().copied().collect()
233 }
234}
235
236impl IntoIterator for Amounts {
237 type Item = (AmountUnit, Amount);
238
239 type IntoIter = <BTreeMap<AmountUnit, Amount> as IntoIterator>::IntoIter;
240
241 fn into_iter(self) -> Self::IntoIter {
242 self.0.into_iter()
243 }
244}
245
246#[derive(Debug, Clone, Eq, PartialEq, Hash)]
252pub struct TransactionItemAmounts {
253 pub amounts: Amounts,
254 pub fees: Amounts,
255}
256
257impl TransactionItemAmounts {
258 pub fn checked_add(self, rhs: &Self) -> Option<Self> {
259 Some(Self {
260 amounts: self.amounts.checked_add(&rhs.amounts)?,
261 fees: self.fees.checked_add(&rhs.fees)?,
262 })
263 }
264}
265
266impl TransactionItemAmounts {
267 pub const ZERO: Self = Self {
268 amounts: Amounts::ZERO,
269 fees: Amounts::ZERO,
270 };
271}
272
273#[derive(Debug, Serialize, Deserialize, Clone)]
275pub struct ApiRequest<T> {
276 pub auth: Option<ApiAuth>,
278 pub params: T,
280}
281
282pub type ApiRequestErased = ApiRequest<JsonValue>;
283
284impl Default for ApiRequestErased {
285 fn default() -> Self {
286 Self {
287 auth: None,
288 params: JsonValue::Null,
289 }
290 }
291}
292
293impl ApiRequestErased {
294 pub fn new<T: Serialize>(params: T) -> Self {
295 Self {
296 auth: None,
297 params: serde_json::to_value(params)
298 .expect("parameter serialization error - this should not happen"),
299 }
300 }
301
302 pub fn to_json(&self) -> JsonValue {
303 serde_json::to_value(self).expect("parameter serialization error - this should not happen")
304 }
305
306 pub fn with_auth(self, auth: ApiAuth) -> Self {
307 Self {
308 auth: Some(auth),
309 params: self.params,
310 }
311 }
312
313 pub fn to_typed<T: serde::de::DeserializeOwned>(
314 self,
315 ) -> Result<ApiRequest<T>, serde_json::Error> {
316 Ok(ApiRequest {
317 auth: self.auth,
318 params: serde_json::from_value::<T>(self.params)?,
319 })
320 }
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize)]
324pub enum ApiMethod {
325 Core(String),
326 Module(ModuleInstanceId, String),
327}
328
329impl fmt::Display for ApiMethod {
330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
331 match self {
332 Self::Core(s) => f.write_str(s),
333 Self::Module(module_id, s) => f.write_fmt(format_args!("{module_id}-{s}")),
334 }
335 }
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct IrohApiRequest {
340 pub method: ApiMethod,
341 pub request: ApiRequestErased,
342}
343
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct IrohGatewayRequest {
346 pub route: String,
348
349 pub params: Option<serde_json::Value>,
351
352 pub password: Option<String>,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct IrohGatewayResponse {
358 pub status: u16,
359 pub body: serde_json::Value,
360}
361
362pub const FEDIMINT_API_ALPN: &[u8] = b"FEDIMINT_API_ALPN";
363pub const FEDIMINT_GATEWAY_ALPN: &[u8] = b"FEDIMINT_GATEWAY_ALPN";
364
365#[derive(Clone, Serialize, Deserialize)]
370pub struct ApiAuth(String);
371
372impl ApiAuth {
373 pub fn new(s: String) -> Self {
374 Self(s)
375 }
376
377 pub fn as_str(&self) -> &str {
378 &self.0
379 }
380
381 pub fn verify(&self, password: &str) -> bool {
382 use subtle::ConstantTimeEq as _;
383 bool::from(self.0.as_bytes().ct_eq(password.as_bytes()))
384 }
385}
386
387impl Debug for ApiAuth {
388 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
389 write!(f, "ApiAuth(****)")
390 }
391}
392
393#[derive(Debug, Clone, Serialize, Deserialize)]
394pub struct ApiError {
395 pub code: i32,
396 pub message: String,
397}
398
399impl Error for ApiError {}
400
401impl fmt::Display for ApiError {
402 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
403 f.write_fmt(format_args!("{} {}", self.code, self.message))
404 }
405}
406
407pub type ApiResult<T> = Result<T, ApiError>;
408
409impl ApiError {
410 pub fn new(code: i32, message: String) -> Self {
411 Self { code, message }
412 }
413
414 pub fn not_found(message: String) -> Self {
415 Self::new(404, message)
416 }
417
418 pub fn bad_request(message: String) -> Self {
419 Self::new(400, message)
420 }
421
422 pub fn unauthorized() -> Self {
423 Self::new(401, "Invalid authorization".to_string())
424 }
425
426 pub fn server_error(message: String) -> Self {
427 Self::new(500, message)
428 }
429}
430
431impl From<DatabaseError> for ApiError {
432 fn from(err: DatabaseError) -> Self {
433 Self {
434 code: 500,
435 message: format!("API server error when writing to database: {err}"),
436 }
437 }
438}
439
440pub struct ApiEndpointContext {
442 db: Database,
443 has_auth: bool,
444}
445
446impl ApiEndpointContext {
447 pub fn new(db: Database, has_auth: bool) -> Self {
449 Self { db, has_auth }
450 }
451
452 pub fn has_auth(&self) -> bool {
455 self.has_auth
456 }
457
458 pub fn db(&self) -> Database {
459 self.db.clone()
460 }
461
462 pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value> + use<K>
464 where
465 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
466 {
467 let db = self.db.clone();
468 async move { db.wait_key_exists(&key).await }
471 }
472
473 pub fn wait_value_matches<K>(
475 &self,
476 key: K,
477 matcher: impl Fn(&K::Value) -> bool + Copy,
478 ) -> impl Future<Output = K::Value>
479 where
480 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
481 {
482 let db = self.db.clone();
483 async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 }
484 }
485}
486
487#[apply(async_trait_maybe_send!)]
488pub trait TypedApiEndpoint {
489 type State: Sync;
490
491 const PATH: &'static str;
493 const VERSION: ApiVersion;
494 type Param: serde::de::DeserializeOwned + Send;
495 type Response: serde::Serialize;
496
497 async fn handle<'state, 'context>(
498 state: &'state Self::State,
499 context: &'context mut ApiEndpointContext,
500 request: Self::Param,
501 ) -> Result<Self::Response, ApiError>;
502}
503
504pub use serde_json;
505
506#[macro_export]
524macro_rules! __public_api_endpoint {
525 (
526 $path:expr_2021,
527 $version_introduced:expr_2021,
529 async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
530 ) => {{
531 struct Endpoint;
532
533 #[$crate::apply($crate::async_trait_maybe_send!)]
534 impl $crate::module::TypedApiEndpoint for Endpoint {
535 #[allow(deprecated)]
536 const PATH: &'static str = $path;
537 const VERSION: $crate::module::ApiVersion = $version_introduced;
538 type State = $state_ty;
539 type Param = $param_ty;
540 type Response = $resp_ty;
541
542 async fn handle<'state, 'context>(
543 $state: &'state Self::State,
544 $context: &'context mut $crate::module::ApiEndpointContext,
545 $param: Self::Param,
546 ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> {
547 $body
548 }
549 }
550
551 $crate::module::ApiEndpoint::from_typed::<Endpoint>()
552 }};
553}
554
555pub use __public_api_endpoint as public_api_endpoint;
556
557#[macro_export]
565macro_rules! __admin_api_endpoint {
566 (
567 $path:expr_2021,
568 $version_introduced:expr_2021,
569 async |$state:ident: &$state_ty:ty, $context:ident, $auth:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
570 ) => {{
571 $crate::module::public_api_endpoint! {
572 $path,
573 $version_introduced,
574 async |$state: &$state_ty, $context, $param: $param_ty| -> $resp_ty {
575 #[allow(unused_variables)]
578 let _ = |$state: (), $context: (), $auth: (), $param: ()| {};
579 let $auth = $crate::net::auth::check_auth($context)?;
580 $body
581 }
582 }
583 }};
584 (
585 $path:expr_2021,
586 $version_introduced:expr_2021,
587 async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
588 ) => {{
589 $crate::module::public_api_endpoint! {
590 $path,
591 $version_introduced,
592 async |$state: &$state_ty, $context, $param: $param_ty| -> $resp_ty {
593 $crate::net::auth::check_auth($context)?;
594 $body
595 }
596 }
597 }};
598}
599
600pub use __admin_api_endpoint as admin_api_endpoint;
601
602use self::registry::ModuleDecoderRegistry;
603
604type HandlerFnReturn<'a> =
605 Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>;
606type HandlerFn<M> = Box<
607 maybe_add_send_sync!(
608 dyn for<'a> Fn(&'a M, ApiEndpointContext, ApiRequestErased) -> HandlerFnReturn<'a>
609 ),
610>;
611
612pub struct ApiEndpoint<M> {
614 pub path: &'static str,
619 pub handler: HandlerFn<M>,
623 pub version: ApiVersion,
625}
626
627static REQ_ID: AtomicU64 = AtomicU64::new(0);
629
630impl ApiEndpoint<()> {
632 pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State>
633 where
634 <E as TypedApiEndpoint>::Response: MaybeSend,
635 E::Param: Debug,
636 E::Response: Debug,
637 {
638 async fn handle_request<'state, 'context, E>(
639 state: &'state E::State,
640 context: &'context mut ApiEndpointContext,
641 request: ApiRequest<E::Param>,
642 ) -> Result<E::Response, ApiError>
643 where
644 E: TypedApiEndpoint,
645 E::Param: Debug,
646 E::Response: Debug,
647 {
648 tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request");
649 let result = E::handle(state, context, request.params).await;
650 match &result {
651 Err(err) => {
652 tracing::warn!(target: LOG_NET_API, path = E::PATH, err = %err.fmt_compact(), "api request error");
653 }
654 _ => {
655 tracing::trace!(target: LOG_NET_API, path = E::PATH, "api request complete");
656 }
657 }
658 result
659 }
660
661 ApiEndpoint {
662 path: E::PATH,
663 version: E::VERSION,
664 handler: Box::new(|m, mut context, request| {
665 Box::pin(async move {
666 let request = request
667 .to_typed()
668 .map_err(|e| ApiError::bad_request(e.to_string()))?;
669
670 let span = tracing::info_span!(
671 target: LOG_NET_API,
672 "api_req",
673 id = REQ_ID.fetch_add(1, Ordering::SeqCst),
674 method = E::PATH,
675 );
676 let ret = handle_request::<E>(m, &mut context, request)
677 .instrument(span)
678 .await?;
679
680 Ok(serde_json::to_value(ret).expect("encoding error"))
681 })
682 }),
683 }
684 }
685}
686
687#[apply(async_trait_maybe_send!)]
694pub trait IDynCommonModuleInit: Debug {
695 fn decoder(&self) -> Decoder;
696
697 fn module_kind(&self) -> ModuleKind;
698
699 fn to_dyn_common(&self) -> DynCommonModuleInit;
700
701 async fn dump_database(
702 &self,
703 dbtx: &mut DatabaseTransaction<'_>,
704 prefix_names: Vec<String>,
705 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>;
706}
707
708pub trait ModuleInit: Debug + Clone + Send + Sync + 'static {
710 type Common: CommonModuleInit;
711
712 fn dump_database(
713 &self,
714 dbtx: &mut DatabaseTransaction<'_>,
715 prefix_names: Vec<String>,
716 ) -> maybe_add_send!(
717 impl Future<
718 Output = Box<
719 dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_,
720 >,
721 >
722 );
723}
724
725#[apply(async_trait_maybe_send!)]
726impl<T> IDynCommonModuleInit for T
727where
728 T: ModuleInit,
729{
730 fn decoder(&self) -> Decoder {
731 T::Common::decoder()
732 }
733
734 fn module_kind(&self) -> ModuleKind {
735 T::Common::KIND
736 }
737
738 fn to_dyn_common(&self) -> DynCommonModuleInit {
739 DynCommonModuleInit::from_inner(Arc::new(self.clone()))
740 }
741
742 async fn dump_database(
743 &self,
744 dbtx: &mut DatabaseTransaction<'_>,
745 prefix_names: Vec<String>,
746 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
747 <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await
748 }
749}
750
751dyn_newtype_define!(
752 #[derive(Clone)]
753 pub DynCommonModuleInit(Arc<IDynCommonModuleInit>)
754);
755
756impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit {
757 fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) {
758 self.inner.as_ref()
759 }
760}
761
762impl DynCommonModuleInit {
763 pub fn from_inner(
764 inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
765 ) -> Self {
766 Self { inner }
767 }
768}
769
770#[apply(async_trait_maybe_send!)]
772pub trait CommonModuleInit: Debug + Sized {
773 const CONSENSUS_VERSION: ModuleConsensusVersion;
774 const KIND: ModuleKind;
775
776 type ClientConfig: ClientConfig;
777
778 fn decoder() -> Decoder;
779}
780
781pub trait ModuleCommon {
783 type ClientConfig: ClientConfig;
784 type Input: Input;
785 type Output: Output;
786 type OutputOutcome: OutputOutcome;
787 type ConsensusItem: ModuleConsensusItem;
788 type InputError: InputError;
789 type OutputError: OutputError;
790
791 fn decoder_builder() -> DecoderBuilder {
792 let mut decoder_builder = Decoder::builder();
793 decoder_builder.with_decodable_type::<Self::ClientConfig>();
794 decoder_builder.with_decodable_type::<Self::Input>();
795 decoder_builder.with_decodable_type::<Self::Output>();
796 decoder_builder.with_decodable_type::<Self::OutputOutcome>();
797 decoder_builder.with_decodable_type::<Self::ConsensusItem>();
798 decoder_builder.with_decodable_type::<Self::InputError>();
799 decoder_builder.with_decodable_type::<Self::OutputError>();
800
801 decoder_builder
802 }
803
804 fn decoder() -> Decoder {
805 Self::decoder_builder().build()
806 }
807}
808
809#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
814pub struct SerdeModuleEncoding<T: Encodable + Decodable>(
815 #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>,
816 #[serde(skip)] PhantomData<T>,
817);
818
819#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
821pub struct SerdeModuleEncodingBase64<T: Encodable + Decodable>(
822 #[serde(with = "::fedimint_core::encoding::as_base64")] Vec<u8>,
823 #[serde(skip)] PhantomData<T>,
824);
825
826impl<T> fmt::Debug for SerdeModuleEncoding<T>
827where
828 T: Encodable + Decodable,
829{
830 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
831 f.write_str("SerdeModuleEncoding(")?;
832 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
833 f.write_str(")")?;
834 Ok(())
835 }
836}
837
838impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> {
839 fn from(value: &T) -> Self {
840 let mut bytes = vec![];
841 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
842 .expect("Writing to buffer can never fail");
843 Self(bytes, PhantomData)
844 }
845}
846
847impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> {
848 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
849 Decodable::consensus_decode_whole(&self.0, modules)
850 }
851
852 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
861 let mut reader = std::io::Cursor::new(&self.0);
862 let module_instance = ModuleInstanceId::consensus_decode_partial(
863 &mut reader,
864 &ModuleDecoderRegistry::default(),
865 )?;
866
867 let total_len =
868 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
869
870 decoder.decode_complete(
873 &mut reader,
874 total_len,
875 module_instance,
876 &ModuleRegistry::default(),
877 )
878 }
879}
880
881impl<T: Encodable + Decodable> Encodable for SerdeModuleEncoding<T> {
882 fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
883 self.0.consensus_encode(writer)
884 }
885}
886
887impl<T: Encodable + Decodable> Decodable for SerdeModuleEncoding<T> {
888 fn consensus_decode_partial_from_finite_reader<R: std::io::Read>(
889 reader: &mut R,
890 modules: &ModuleDecoderRegistry,
891 ) -> Result<Self, DecodeError> {
892 Ok(Self(
893 Vec::<u8>::consensus_decode_partial_from_finite_reader(reader, modules)?,
894 PhantomData,
895 ))
896 }
897}
898
899impl<T> fmt::Debug for SerdeModuleEncodingBase64<T>
900where
901 T: Encodable + Decodable,
902{
903 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
904 f.write_str("SerdeModuleEncoding2(")?;
905 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
906 f.write_str(")")?;
907 Ok(())
908 }
909}
910
911impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncodingBase64<T> {
912 fn from(value: &T) -> Self {
913 let mut bytes = vec![];
914 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
915 .expect("Writing to buffer can never fail");
916 Self(bytes, PhantomData)
917 }
918}
919
920impl<T: Encodable + Decodable + 'static> SerdeModuleEncodingBase64<T> {
921 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
922 Decodable::consensus_decode_whole(&self.0, modules)
923 }
924
925 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
934 let mut reader = std::io::Cursor::new(&self.0);
935 let module_instance = ModuleInstanceId::consensus_decode_partial(
936 &mut reader,
937 &ModuleDecoderRegistry::default(),
938 )?;
939
940 let total_len =
941 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
942
943 decoder.decode_complete(
946 &mut reader,
947 total_len,
948 module_instance,
949 &ModuleRegistry::default(),
950 )
951 }
952}