1pub mod audit;
20pub mod registry;
21
22use std::fmt::{self, Debug, Formatter};
23use std::marker::PhantomData;
24use std::pin::Pin;
25use std::sync::atomic::{AtomicU64, Ordering};
26use std::sync::Arc;
27
28use fedimint_logging::LOG_NET_API;
29use futures::Future;
30use jsonrpsee_core::JsonValue;
31use registry::ModuleRegistry;
32use serde::{Deserialize, Serialize};
33use tracing::Instrument;
34
35mod version;
37pub use self::version::*;
38use crate::config::DkgPeerMessage;
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::net::peers::DynP2PConnections;
50use crate::task::MaybeSend;
51use crate::{apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync, Amount, PeerId};
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(Clone, PartialEq, Eq, Serialize, Deserialize)]
129pub struct ApiAuth(pub String);
130
131impl Debug for ApiAuth {
132 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
133 write!(f, "ApiAuth(****)")
134 }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ApiError {
139 pub code: i32,
140 pub message: String,
141}
142
143impl ApiError {
144 pub fn new(code: i32, message: String) -> Self {
145 Self { code, message }
146 }
147
148 pub fn not_found(message: String) -> Self {
149 Self::new(404, message)
150 }
151
152 pub fn bad_request(message: String) -> Self {
153 Self::new(400, message)
154 }
155
156 pub fn unauthorized() -> Self {
157 Self::new(401, "Invalid authorization".to_string())
158 }
159
160 pub fn server_error(message: String) -> Self {
161 Self::new(500, message)
162 }
163}
164
165pub struct ApiEndpointContext<'dbtx> {
167 db: Database,
168 dbtx: DatabaseTransaction<'dbtx, Committable>,
169 has_auth: bool,
170 request_auth: Option<ApiAuth>,
171}
172
173impl<'a> ApiEndpointContext<'a> {
174 pub fn new(
176 db: Database,
177 dbtx: DatabaseTransaction<'a, Committable>,
178 has_auth: bool,
179 request_auth: Option<ApiAuth>,
180 ) -> Self {
181 Self {
182 db,
183 dbtx,
184 has_auth,
185 request_auth,
186 }
187 }
188
189 pub fn dbtx<'s, 'mtx>(&'s mut self) -> DatabaseTransaction<'mtx, NonCommittable>
191 where
192 'a: 'mtx,
193 's: 'mtx,
194 {
195 self.dbtx.to_ref_nc()
197 }
198
199 pub fn request_auth(&self) -> Option<ApiAuth> {
202 self.request_auth.clone()
203 }
204
205 pub fn has_auth(&self) -> bool {
208 self.has_auth
209 }
210
211 pub fn db(&self) -> Database {
212 self.db.clone()
213 }
214
215 pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value>
217 where
218 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
219 {
220 let db = self.db.clone();
221 async move { db.wait_key_exists(&key).await }
224 }
225
226 pub fn wait_value_matches<K>(
228 &self,
229 key: K,
230 matcher: impl Fn(&K::Value) -> bool + Copy,
231 ) -> impl Future<Output = K::Value>
232 where
233 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify,
234 {
235 let db = self.db.clone();
236 async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 }
237 }
238
239 pub async fn commit_tx_result(self, path: &'static str) -> Result<(), ApiError> {
241 self.dbtx.commit_tx_result().await.map_err(|err| {
242 tracing::warn!(
243 target: fedimint_logging::LOG_NET_API,
244 path,
245 "API server error when writing to database: {:?}",
246 err
247 );
248 ApiError {
249 code: 500,
250 message: "API server error when writing to database".to_string(),
251 }
252 })
253 }
254}
255
256#[apply(async_trait_maybe_send!)]
257pub trait TypedApiEndpoint {
258 type State: Sync;
259
260 const PATH: &'static str;
262
263 type Param: serde::de::DeserializeOwned + Send;
264 type Response: serde::Serialize;
265
266 async fn handle<'state, 'context, 'dbtx>(
267 state: &'state Self::State,
268 context: &'context mut ApiEndpointContext<'dbtx>,
269 request: Self::Param,
270 ) -> Result<Self::Response, ApiError>
271 where
272 'dbtx: 'context;
273}
274
275pub use serde_json;
276
277#[macro_export]
293macro_rules! __api_endpoint {
294 (
295 $path:expr,
296 $version_introduced:expr,
299 async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block
300 ) => {{
301 struct Endpoint;
302
303 #[$crate::apply($crate::async_trait_maybe_send!)]
304 impl $crate::module::TypedApiEndpoint for Endpoint {
305 #[allow(deprecated)]
306 const PATH: &'static str = $path;
307 type State = $state_ty;
308 type Param = $param_ty;
309 type Response = $resp_ty;
310
311 async fn handle<'state, 'context, 'dbtx>(
312 $state: &'state Self::State,
313 $context: &'context mut $crate::module::ApiEndpointContext<'dbtx>,
314 $param: Self::Param,
315 ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> {
316 {
317 const __API_VERSION: $crate::module::ApiVersion = $version_introduced;
319 }
320 $body
321 }
322 }
323
324 $crate::module::ApiEndpoint::from_typed::<Endpoint>()
325 }};
326}
327
328pub use __api_endpoint as api_endpoint;
329use fedimint_core::NumPeers;
330
331use self::registry::ModuleDecoderRegistry;
332
333type HandlerFnReturn<'a> =
334 Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>;
335type HandlerFn<M> = Box<
336 maybe_add_send_sync!(
337 dyn for<'a> Fn(&'a M, ApiEndpointContext<'a>, ApiRequestErased) -> HandlerFnReturn<'a>
338 ),
339>;
340
341pub struct ApiEndpoint<M> {
343 pub path: &'static str,
348 pub handler: HandlerFn<M>,
352}
353
354static REQ_ID: AtomicU64 = AtomicU64::new(0);
356
357impl ApiEndpoint<()> {
359 pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State>
360 where
361 <E as TypedApiEndpoint>::Response: MaybeSend,
362 E::Param: Debug,
363 E::Response: Debug,
364 {
365 async fn handle_request<'state, 'context, 'dbtx, E>(
366 state: &'state E::State,
367 context: &'context mut ApiEndpointContext<'dbtx>,
368 request: ApiRequest<E::Param>,
369 ) -> Result<E::Response, ApiError>
370 where
371 'dbtx: 'context,
372 E: TypedApiEndpoint,
373 E::Param: Debug,
374 E::Response: Debug,
375 {
376 tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request");
377 let result = E::handle(state, context, request.params).await;
378 if let Err(error) = &result {
379 tracing::warn!(target: LOG_NET_API, path = E::PATH, ?error, "api request error");
380 } else {
381 tracing::trace!(target: LOG_NET_API, path = E::PATH, "api request complete");
382 }
383 result
384 }
385
386 ApiEndpoint {
387 path: E::PATH,
388 handler: Box::new(|m, mut context, request| {
389 Box::pin(async {
390 let request = request
391 .to_typed()
392 .map_err(|e| ApiError::bad_request(e.to_string()))?;
393
394 let span = tracing::info_span!(
395 target: LOG_NET_API,
396 "api_req",
397 id = REQ_ID.fetch_add(1, Ordering::SeqCst),
398 method = E::PATH,
399 );
400 let ret = handle_request::<E>(m, &mut context, request)
401 .instrument(span)
402 .await?;
403
404 context.commit_tx_result(E::PATH).await?;
405
406 Ok(serde_json::to_value(ret).expect("encoding error"))
407 })
408 }),
409 }
410 }
411}
412
413#[apply(async_trait_maybe_send!)]
420pub trait IDynCommonModuleInit: Debug {
421 fn decoder(&self) -> Decoder;
422
423 fn module_kind(&self) -> ModuleKind;
424
425 fn to_dyn_common(&self) -> DynCommonModuleInit;
426
427 async fn dump_database(
428 &self,
429 dbtx: &mut DatabaseTransaction<'_>,
430 prefix_names: Vec<String>,
431 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>;
432}
433
434pub trait ModuleInit: Debug + Clone + Send + Sync + 'static {
436 type Common: CommonModuleInit;
437
438 fn dump_database(
439 &self,
440 dbtx: &mut DatabaseTransaction<'_>,
441 prefix_names: Vec<String>,
442 ) -> maybe_add_send!(
443 impl Future<
444 Output = Box<
445 dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_,
446 >,
447 >
448 );
449}
450
451#[apply(async_trait_maybe_send!)]
452impl<T> IDynCommonModuleInit for T
453where
454 T: ModuleInit,
455{
456 fn decoder(&self) -> Decoder {
457 T::Common::decoder()
458 }
459
460 fn module_kind(&self) -> ModuleKind {
461 T::Common::KIND
462 }
463
464 fn to_dyn_common(&self) -> DynCommonModuleInit {
465 DynCommonModuleInit::from_inner(Arc::new(self.clone()))
466 }
467
468 async fn dump_database(
469 &self,
470 dbtx: &mut DatabaseTransaction<'_>,
471 prefix_names: Vec<String>,
472 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
473 <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await
474 }
475}
476
477dyn_newtype_define!(
478 #[derive(Clone)]
479 pub DynCommonModuleInit(Arc<IDynCommonModuleInit>)
480);
481
482impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit {
483 fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) {
484 self.inner.as_ref()
485 }
486}
487
488impl DynCommonModuleInit {
489 pub fn from_inner(
490 inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>,
491 ) -> Self {
492 Self { inner }
493 }
494}
495
496#[apply(async_trait_maybe_send!)]
498pub trait CommonModuleInit: Debug + Sized {
499 const CONSENSUS_VERSION: ModuleConsensusVersion;
500 const KIND: ModuleKind;
501
502 type ClientConfig: ClientConfig;
503
504 fn decoder() -> Decoder;
505}
506
507pub trait ModuleCommon {
509 type ClientConfig: ClientConfig;
510 type Input: Input;
511 type Output: Output;
512 type OutputOutcome: OutputOutcome;
513 type ConsensusItem: ModuleConsensusItem;
514 type InputError: InputError;
515 type OutputError: OutputError;
516
517 fn decoder_builder() -> DecoderBuilder {
518 let mut decoder_builder = Decoder::builder();
519 decoder_builder.with_decodable_type::<Self::ClientConfig>();
520 decoder_builder.with_decodable_type::<Self::Input>();
521 decoder_builder.with_decodable_type::<Self::Output>();
522 decoder_builder.with_decodable_type::<Self::OutputOutcome>();
523 decoder_builder.with_decodable_type::<Self::ConsensusItem>();
524 decoder_builder.with_decodable_type::<Self::InputError>();
525 decoder_builder.with_decodable_type::<Self::OutputError>();
526
527 decoder_builder
528 }
529
530 fn decoder() -> Decoder {
531 Self::decoder_builder().build()
532 }
533}
534
535#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
540pub struct SerdeModuleEncoding<T: Encodable + Decodable>(
541 #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>,
542 #[serde(skip)] PhantomData<T>,
543);
544
545#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
547pub struct SerdeModuleEncodingBase64<T: Encodable + Decodable>(
548 #[serde(with = "::fedimint_core::encoding::as_base64")] Vec<u8>,
549 #[serde(skip)] PhantomData<T>,
550);
551
552impl<T> fmt::Debug for SerdeModuleEncoding<T>
553where
554 T: Encodable + Decodable,
555{
556 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
557 f.write_str("SerdeModuleEncoding(")?;
558 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
559 f.write_str(")")?;
560 Ok(())
561 }
562}
563
564impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> {
565 fn from(value: &T) -> Self {
566 let mut bytes = vec![];
567 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
568 .expect("Writing to buffer can never fail");
569 Self(bytes, PhantomData)
570 }
571}
572
573impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> {
574 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
575 Decodable::consensus_decode_whole(&self.0, modules)
576 }
577
578 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
587 let mut reader = std::io::Cursor::new(&self.0);
588 let module_instance = ModuleInstanceId::consensus_decode_partial(
589 &mut reader,
590 &ModuleDecoderRegistry::default(),
591 )?;
592
593 let total_len =
594 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
595
596 decoder.decode_complete(
599 &mut reader,
600 total_len,
601 module_instance,
602 &ModuleRegistry::default(),
603 )
604 }
605}
606
607impl<T> fmt::Debug for SerdeModuleEncodingBase64<T>
608where
609 T: Encodable + Decodable,
610{
611 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
612 f.write_str("SerdeModuleEncoding2(")?;
613 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?;
614 f.write_str(")")?;
615 Ok(())
616 }
617}
618
619impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncodingBase64<T> {
620 fn from(value: &T) -> Self {
621 let mut bytes = vec![];
622 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes)
623 .expect("Writing to buffer can never fail");
624 Self(bytes, PhantomData)
625 }
626}
627
628impl<T: Encodable + Decodable + 'static> SerdeModuleEncodingBase64<T> {
629 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> {
630 Decodable::consensus_decode_whole(&self.0, modules)
631 }
632
633 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> {
642 let mut reader = std::io::Cursor::new(&self.0);
643 let module_instance = ModuleInstanceId::consensus_decode_partial(
644 &mut reader,
645 &ModuleDecoderRegistry::default(),
646 )?;
647
648 let total_len =
649 u64::consensus_decode_partial(&mut reader, &ModuleDecoderRegistry::default())?;
650
651 decoder.decode_complete(
654 &mut reader,
655 total_len,
656 module_instance,
657 &ModuleRegistry::default(),
658 )
659 }
660}
661
662#[non_exhaustive]
668pub struct PeerHandle<'a> {
669 #[doc(hidden)]
672 pub num_peers: NumPeers,
673 #[doc(hidden)]
674 pub identity: PeerId,
675 #[doc(hidden)]
676 pub connections: &'a DynP2PConnections<DkgPeerMessage>,
677}
678
679impl<'a> PeerHandle<'a> {
680 pub fn new(
681 num_peers: NumPeers,
682 identity: PeerId,
683 connections: &'a DynP2PConnections<DkgPeerMessage>,
684 ) -> Self {
685 Self {
686 num_peers,
687 identity,
688 connections,
689 }
690 }
691
692 pub fn num_peers(&self) -> NumPeers {
693 self.num_peers
694 }
695}