1pub mod audit;
20pub mod registry;
21
22use std::error::Error;
23use std::fmt::{self, Debug, Formatter};
24use std::marker::PhantomData;
25use std::pin::Pin;
26use std::sync::Arc;
27use std::sync::atomic::{AtomicU64, Ordering};
28
29use fedimint_logging::LOG_NET_API;
30use futures::Future;
31use jsonrpsee_core::JsonValue;
32use registry::ModuleRegistry;
33use serde::{Deserialize, Serialize};
34use tracing::Instrument;
35
36mod version;
38pub use self::version::*;
39use crate::core::{
40 ClientConfig, Decoder, DecoderBuilder, Input, InputError, ModuleConsensusItem,
41 ModuleInstanceId, ModuleKind, Output, OutputError, OutputOutcome,
42};
43use crate::db::{
44 Committable, Database, DatabaseKey, DatabaseKeyWithNotify, DatabaseRecord, DatabaseTransaction,
45 NonCommittable,
46};
47use crate::encoding::{Decodable, DecodeError, Encodable};
48use crate::fmt_utils::AbbreviateHexBytes;
49use crate::task::MaybeSend;
50use crate::util::FmtCompact;
51use crate::{Amount, apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync};
52
53#[derive(Debug, PartialEq, Eq)]
54pub struct InputMeta {
55 pub amount: TransactionItemAmount,
56 pub pub_key: secp256k1::PublicKey,
57}
58
59#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
65pub struct TransactionItemAmount {
66 pub amount: Amount,
67 pub fee: Amount,
68}
69
70impl TransactionItemAmount {
71 pub const ZERO: Self = Self {
72 amount: Amount::ZERO,
73 fee: Amount::ZERO,
74 };
75}
76
77#[derive(Debug, Serialize, Deserialize, Clone)]
79pub struct ApiRequest<T> {
80 pub auth: Option<ApiAuth>,
82 pub params: T,
84}
85
86pub type ApiRequestErased = ApiRequest<JsonValue>;
87
88impl Default for ApiRequestErased {
89 fn default() -> Self {
90 Self {
91 auth: None,
92 params: JsonValue::Null,
93 }
94 }
95}
96
97impl ApiRequestErased {
98 pub fn new<T: Serialize>(params: T) -> Self {
99 Self {
100 auth: None,
101 params: serde_json::to_value(params)
102 .expect("parameter serialization error - this should not happen"),
103 }
104 }
105
106 pub fn to_json(&self) -> JsonValue {
107 serde_json::to_value(self).expect("parameter serialization error - this should not happen")
108 }
109
110 pub fn with_auth(self, auth: ApiAuth) -> Self {
111 Self {
112 auth: Some(auth),
113 params: self.params,
114 }
115 }
116
117 pub fn to_typed<T: serde::de::DeserializeOwned>(
118 self,
119 ) -> Result<ApiRequest<T>, serde_json::Error> {
120 Ok(ApiRequest {
121 auth: self.auth,
122 params: serde_json::from_value::<T>(self.params)?,
123 })
124 }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub enum ApiMethod {
129 Core(String),
130 Module(ModuleInstanceId, String),
131}
132
133impl fmt::Display for ApiMethod {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self {
136 Self::Core(s) => f.write_str(s),
137 Self::Module(module_id, s) => f.write_fmt(format_args!("{module_id}-{s}")),
138 }
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct IrohApiRequest {
144 pub method: ApiMethod,
145 pub request: ApiRequestErased,
146}
147
148pub const FEDIMINT_API_ALPN: &[u8] = b"FEDIMINT_API_ALPN";
149
150#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
152pub struct ApiAuth(pub String);
153
154impl Debug for ApiAuth {
155 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
156 write!(f, "ApiAuth(****)")
157 }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct ApiError {
162 pub code: i32,
163 pub message: String,
164}
165
166impl Error for ApiError {}
167
168impl fmt::Display for ApiError {
169 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
170 f.write_fmt(format_args!("{} {}", self.code, self.message))
171 }
172}
173
174pub type ApiResult<T> = Result<T, ApiError>;
175
176impl ApiError {
177 pub fn new(code: i32, message: String) -> Self {
178 Self { code, message }
179 }
180
181 pub fn not_found(message: String) -> Self {
182 Self::new(404, message)
183 }
184
185 pub fn bad_request(message: String) -> Self {
186 Self::new(400, message)
187 }
188
189 pub fn unauthorized() -> Self {
190 Self::new(401, "Invalid authorization".to_string())
191 }
192
193 pub fn server_error(message: String) -> Self {
194 Self::new(500, message)
195 }
196}
197
198pub struct ApiEndpointContext<'dbtx> {
200 db: Database,
201 dbtx: DatabaseTransaction<'dbtx, Committable>,
202 has_auth: bool,
203 request_auth: Option<ApiAuth>,
204}
205
206impl<'a> ApiEndpointContext<'a> {
207 pub fn new(
209 db: Database,
210 dbtx: DatabaseTransaction<'a, Committable>,
211 has_auth: bool,
212 request_auth: Option<ApiAuth>,
213 ) -> Self {
214 Self {
215 db,
216 dbtx,
217 has_auth,
218 request_auth,
219 }
220 }
221
222 pub fn dbtx<'s, 'mtx>(&'s mut self) -> DatabaseTransaction<'mtx, NonCommittable>
224 where
225 'a: 'mtx,
226 's: 'mtx,
227 {
228 self.dbtx.to_ref_nc()
230 }
231
232 pub fn request_auth(&self) -> Option<ApiAuth> {
235 self.request_auth.clone()
236 }
237
238 pub fn has_auth(&self) -> bool {
241 self.has_auth
242 }
243
244 pub fn db(&self) -> Database {
245 self.db.clone()
246 }
247
248 pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value> + use<K>
250 where
251 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
252 {
253 let db = self.db.clone();
254 async move { db.wait_key_exists(&key).await }
257 }
258
259 pub fn wait_value_matches<K>(
261 &self,
262 key: K,
263 matcher: impl Fn(&K::Value) -> bool + Copy,
264 ) -> impl Future<Output = K::Value>
265 where
266 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
267 {
268 let db = self.db.clone();
269 async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 }
270 }
271
272 pub async fn commit_tx_result(self, path: &'static str) -> Result<(), ApiError> {
274 self.dbtx.commit_tx_result().await.map_err(|err| {
275 tracing::warn!(
276 target: fedimint_logging::LOG_NET_API,
277 path,
278 "API server error when writing to database: {:?}",
279 err
280 );
281 ApiError {
282 code: 500,
283 message: "API server error when writing to database".to_string(),
284 }
285 })
286 }
287}
288
289#[apply(async_trait_maybe_send!)]
290pub trait TypedApiEndpoint {
291 type State: Sync;
292
293 const PATH: &'static str;
295
296 type Param: serde::de::DeserializeOwned + Send;
297 type Response: serde::Serialize;
298
299 async fn handle<'state, 'context, 'dbtx>(
300 state: &'state Self::State,
301 context: &'context mut ApiEndpointContext<'dbtx>,
302 request: Self::Param,
303 ) -> Result<Self::Response, ApiError>
304 where
305 'dbtx: 'context;
306}
307
308pub use serde_json;
309
310#[macro_export]
326macro_rules! __api_endpoint {
327 (
328 $path:expr_2021,
329 $version_introduced:expr_2021,
332 async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
333 ) => {{
334 struct Endpoint;
335
336 #[$crate::apply($crate::async_trait_maybe_send!)]
337 impl $crate::module::TypedApiEndpoint for Endpoint {
338 #[allow(deprecated)]
339 const PATH: &'static str = $path;
340 type State = $state_ty;
341 type Param = $param_ty;
342 type Response = $resp_ty;
343
344 async fn handle<'state, 'context, 'dbtx>(
345 $state: &'state Self::State,
346 $context: &'context mut $crate::module::ApiEndpointContext<'dbtx>,
347 $param: Self::Param,
348 ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> {
349 {
350 const __API_VERSION: $crate::module::ApiVersion = $version_introduced;
352 }
353 $body
354 }
355 }
356
357 $crate::module::ApiEndpoint::from_typed::<Endpoint>()
358 }};
359}
360
361pub use __api_endpoint as api_endpoint;
362
363use self::registry::ModuleDecoderRegistry;
364
365type HandlerFnReturn<'a> =
366 Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>;
367type HandlerFn<M> = Box<
368 maybe_add_send_sync!(
369 dyn for<'a> Fn(&'a M, ApiEndpointContext<'a>, ApiRequestErased) -> HandlerFnReturn<'a>
370 ),
371>;
372
373pub struct ApiEndpoint<M> {
375 pub path: &'static str,
380 pub handler: HandlerFn<M>,
384}
385
386static REQ_ID: AtomicU64 = AtomicU64::new(0);
388
389impl ApiEndpoint<()> {
391 pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State>
392 where
393 <E as TypedApiEndpoint>::Response: MaybeSend,
394 E::Param: Debug,
395 E::Response: Debug,
396 {
397 async fn handle_request<'state, 'context, 'dbtx, E>(
398 state: &'state E::State,
399 context: &'context mut ApiEndpointContext<'dbtx>,
400 request: ApiRequest<E::Param>,
401 ) -> Result<E::Response, ApiError>
402 where
403 'dbtx: 'context,
404 E: TypedApiEndpoint,
405 E::Param: Debug,
406 E::Response: Debug,
407 {
408 tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request");
409 let result = E::handle(state, context, request.params).await;
410 match &result {
411 Err(err) => {
412 tracing::warn!(target: LOG_NET_API, path = E::PATH, err = %err.fmt_compact(), "api request error");
413 }
414 _ => {
415 tracing::trace!(target: LOG_NET_API, path = E::PATH, "api request complete");
416 }
417 }
418 result
419 }
420
421 ApiEndpoint {
422 path: E::PATH,
423 handler: Box::new(|m, mut context, request| {
424 Box::pin(async {
425 let request = request
426 .to_typed()
427 .map_err(|e| ApiError::bad_request(e.to_string()))?;
428
429 let span = tracing::info_span!(
430 target: LOG_NET_API,
431 "api_req",
432 id = REQ_ID.fetch_add(1, Ordering::SeqCst),
433 method = E::PATH,
434 );
435 let ret = handle_request::<E>(m, &mut context, request)
436 .instrument(span)
437 .await?;
438
439 context.commit_tx_result(E::PATH).await?;
440
441 Ok(serde_json::to_value(ret).expect("encoding error"))
442 })
443 }),
444 }
445 }
446}
447
448#[apply(async_trait_maybe_send!)]
455pub trait IDynCommonModuleInit: Debug {
456 fn decoder(&self) -> Decoder;
457
458 fn module_kind(&self) -> ModuleKind;
459
460 fn to_dyn_common(&self) -> DynCommonModuleInit;
461
462 async fn dump_database(
463 &self,
464 dbtx: &mut DatabaseTransaction<'_>,
465 prefix_names: Vec<String>,
466 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>;
467}
468
469pub trait ModuleInit: Debug + Clone + Send + Sync + 'static {
471 type Common: CommonModuleInit;
472
473 fn dump_database(
474 &self,
475 dbtx: &mut DatabaseTransaction<'_>,
476 prefix_names: Vec<String>,
477 ) -> maybe_add_send!(
478 impl Future<
479 Output = Box<
480 dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_,
481 >,
482 >
483 );
484}
485
486#[apply(async_trait_maybe_send!)]
487impl<T> IDynCommonModuleInit for T
488where
489 T: ModuleInit,
490{
491 fn decoder(&self) -> Decoder {
492 T::Common::decoder()
493 }
494
495 fn module_kind(&self) -> ModuleKind {
496 T::Common::KIND
497 }
498
499 fn to_dyn_common(&self) -> DynCommonModuleInit {
500 DynCommonModuleInit::from_inner(Arc::new(self.clone()))
501 }
502
503 async fn dump_database(
504 &self,
505 dbtx: &mut DatabaseTransaction<'_>,
506 prefix_names: Vec<String>,
507 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
508 <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await
509 }
510}
511
512dyn_newtype_define!(
513 #[derive(Clone)]
514 pub DynCommonModuleInit(Arc<IDynCommonModuleInit>)
515);
516
517impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit {
518 fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) {
519 self.inner.as_ref()
520 }
521}
522
523impl DynCommonModuleInit {
524 pub fn from_inner(
525 inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
526 ) -> Self {
527 Self { inner }
528 }
529}
530
531#[apply(async_trait_maybe_send!)]
533pub trait CommonModuleInit: Debug + Sized {
534 const CONSENSUS_VERSION: ModuleConsensusVersion;
535 const KIND: ModuleKind;
536
537 type ClientConfig: ClientConfig;
538
539 fn decoder() -> Decoder;
540}
541
542pub trait ModuleCommon {
544 type ClientConfig: ClientConfig;
545 type Input: Input;
546 type Output: Output;
547 type OutputOutcome: OutputOutcome;
548 type ConsensusItem: ModuleConsensusItem;
549 type InputError: InputError;
550 type OutputError: OutputError;
551
552 fn decoder_builder() -> DecoderBuilder {
553 let mut decoder_builder = Decoder::builder();
554 decoder_builder.with_decodable_type::<Self::ClientConfig>();
555 decoder_builder.with_decodable_type::<Self::Input>();
556 decoder_builder.with_decodable_type::<Self::Output>();
557 decoder_builder.with_decodable_type::<Self::OutputOutcome>();
558 decoder_builder.with_decodable_type::<Self::ConsensusItem>();
559 decoder_builder.with_decodable_type::<Self::InputError>();
560 decoder_builder.with_decodable_type::<Self::OutputError>();
561
562 decoder_builder
563 }
564
565 fn decoder() -> Decoder {
566 Self::decoder_builder().build()
567 }
568}
569
570#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
575pub struct SerdeModuleEncoding<T: Encodable + Decodable>(
576 #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>,
577 #[serde(skip)] PhantomData<T>,
578);
579
580#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
582pub struct SerdeModuleEncodingBase64<T: Encodable + Decodable>(
583 #[serde(with = "::fedimint_core::encoding::as_base64")] Vec<u8>,
584 #[serde(skip)] PhantomData<T>,
585);
586
587impl<T> fmt::Debug for SerdeModuleEncoding<T>
588where
589 T: Encodable + Decodable,
590{
591 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
592 f.write_str("SerdeModuleEncoding(")?;
593 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
594 f.write_str(")")?;
595 Ok(())
596 }
597}
598
599impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> {
600 fn from(value: &T) -> Self {
601 let mut bytes = vec![];
602 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
603 .expect("Writing to buffer can never fail");
604 Self(bytes, PhantomData)
605 }
606}
607
608impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> {
609 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
610 Decodable::consensus_decode_whole(&self.0, modules)
611 }
612
613 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
622 let mut reader = std::io::Cursor::new(&self.0);
623 let module_instance = ModuleInstanceId::consensus_decode_partial(
624 &mut reader,
625 &ModuleDecoderRegistry::default(),
626 )?;
627
628 let total_len =
629 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
630
631 decoder.decode_complete(
634 &mut reader,
635 total_len,
636 module_instance,
637 &ModuleRegistry::default(),
638 )
639 }
640}
641
642impl<T> fmt::Debug for SerdeModuleEncodingBase64<T>
643where
644 T: Encodable + Decodable,
645{
646 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
647 f.write_str("SerdeModuleEncoding2(")?;
648 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
649 f.write_str(")")?;
650 Ok(())
651 }
652}
653
654impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncodingBase64<T> {
655 fn from(value: &T) -> Self {
656 let mut bytes = vec![];
657 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
658 .expect("Writing to buffer can never fail");
659 Self(bytes, PhantomData)
660 }
661}
662
663impl<T: Encodable + Decodable + 'static> SerdeModuleEncodingBase64<T> {
664 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
665 Decodable::consensus_decode_whole(&self.0, modules)
666 }
667
668 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
677 let mut reader = std::io::Cursor::new(&self.0);
678 let module_instance = ModuleInstanceId::consensus_decode_partial(
679 &mut reader,
680 &ModuleDecoderRegistry::default(),
681 )?;
682
683 let total_len =
684 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
685
686 decoder.decode_complete(
689 &mut reader,
690 total_len,
691 module_instance,
692 &ModuleRegistry::default(),
693 )
694 }
695}