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
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#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
121pub struct Amounts(BTreeMap<AmountUnit, Amount>);
122
123impl 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)] 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#[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#[derive(Debug, Serialize, Deserialize, Clone)]
282pub struct ApiRequest<T> {
283 pub auth: Option<ApiAuth>,
285 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 pub route: String,
355
356 pub params: Option<serde_json::Value>,
358
359 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#[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
447pub struct ApiEndpointContext {
449 db: Database,
450 has_auth: bool,
451}
452
453impl ApiEndpointContext {
454 pub fn new(db: Database, has_auth: bool) -> Self {
456 Self { db, has_auth }
457 }
458
459 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 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 async move { db.wait_key_exists(&key).await }
478 }
479
480 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 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#[macro_export]
531macro_rules! __public_api_endpoint {
532 (
533 $path:expr_2021,
534 $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#[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 #[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
619pub struct ApiEndpoint<M> {
621 pub path: &'static str,
626 pub handler: HandlerFn<M>,
630 pub version: ApiVersion,
632}
633
634static REQ_ID: AtomicU64 = AtomicU64::new(0);
636
637impl 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#[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
715pub 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#[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
788pub 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#[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#[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 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 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 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 decoder.decode_complete(
953 &mut reader,
954 total_len,
955 module_instance,
956 &ModuleRegistry::default(),
957 )
958 }
959}