mod.rs
1 //! Fedimint supports modules to allow extending it's functionality. 2 //! Some of the standard functionality is implemented in form of modules as 3 //! well. 4 //! 5 //! The top level server-side types are: 6 //! 7 //! * [`fedimint_core::module::ModuleInit`] 8 //! * [`fedimint_core::module::ServerModule`] 9 //! 10 //! Top level client-side types are: 11 //! 12 //! * `ClientModuleInit` (in `fedimint_client`) 13 //! * `ClientModule` (in `fedimint_client`) 14 pub mod audit; 15 pub mod registry; 16 17 use std::collections::BTreeMap; 18 use std::fmt::{self, Debug, Formatter}; 19 use std::marker::{self, PhantomData}; 20 use std::pin::Pin; 21 use std::sync::atomic::{AtomicU64, Ordering}; 22 use std::sync::Arc; 23 24 use fedimint_logging::LOG_NET_API; 25 use futures::Future; 26 use jsonrpsee_core::JsonValue; 27 use serde::{Deserialize, Serialize}; 28 use tracing::Instrument; 29 30 // TODO: Make this module public and remove the wildcard `pub use` below 31 mod version; 32 pub use self::version::*; 33 use crate::config::{ 34 ClientModuleConfig, ConfigGenModuleParams, DkgPeerMsg, ModuleInitParams, ServerModuleConfig, 35 ServerModuleConsensusConfig, 36 }; 37 use crate::core::{ 38 ClientConfig, Decoder, DecoderBuilder, Input, InputError, ModuleConsensusItem, 39 ModuleInstanceId, ModuleKind, Output, OutputError, OutputOutcome, 40 }; 41 use crate::db::{ 42 Committable, Database, DatabaseKey, DatabaseKeyWithNotify, DatabaseRecord, DatabaseTransaction, 43 DatabaseVersion, ServerMigrationFn, 44 }; 45 use crate::encoding::{Decodable, DecodeError, Encodable}; 46 use crate::fmt_utils::AbbreviateHexBytes; 47 use crate::module::audit::Audit; 48 use crate::net::peers::MuxPeerConnections; 49 use crate::server::DynServerModule; 50 use crate::task::{MaybeSend, TaskGroup}; 51 use crate::{ 52 apply, async_trait_maybe_send, maybe_add_send, maybe_add_send_sync, Amount, NumPeers, OutPoint, 53 PeerId, 54 }; 55 56 #[derive(Debug, PartialEq)] 57 pub struct InputMeta { 58 pub amount: TransactionItemAmount, 59 pub pub_key: secp256k1_zkp::PublicKey, 60 } 61 62 /// Information about the amount represented by an input or output. 63 /// 64 /// * For **inputs** the amount is funding the transaction while the fee is 65 /// consuming funding 66 /// * For **outputs** the amount and the fee consume funding 67 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] 68 pub struct TransactionItemAmount { 69 pub amount: Amount, 70 pub fee: Amount, 71 } 72 73 impl TransactionItemAmount { 74 pub const ZERO: TransactionItemAmount = TransactionItemAmount { 75 amount: Amount::ZERO, 76 fee: Amount::ZERO, 77 }; 78 } 79 80 /// All requests from client to server contain these fields 81 #[derive(Debug, Serialize, Deserialize, Clone)] 82 pub struct ApiRequest<T> { 83 /// Hashed user password if the API requires authentication 84 pub auth: Option<ApiAuth>, 85 /// Parameters required by the API 86 pub params: T, 87 } 88 89 pub type ApiRequestErased = ApiRequest<JsonValue>; 90 91 impl Default for ApiRequestErased { 92 fn default() -> Self { 93 Self { 94 auth: None, 95 params: JsonValue::Null, 96 } 97 } 98 } 99 100 impl ApiRequestErased { 101 pub fn new<T: Serialize>(params: T) -> ApiRequestErased { 102 Self { 103 auth: None, 104 params: serde_json::to_value(params) 105 .expect("parameter serialization error - this should not happen"), 106 } 107 } 108 109 pub fn to_json(&self) -> JsonValue { 110 serde_json::to_value(self).expect("parameter serialization error - this should not happen") 111 } 112 113 pub fn with_auth(self, auth: ApiAuth) -> Self { 114 Self { 115 auth: Some(auth), 116 params: self.params, 117 } 118 } 119 120 pub fn to_typed<T: serde::de::DeserializeOwned>( 121 self, 122 ) -> Result<ApiRequest<T>, serde_json::Error> { 123 Ok(ApiRequest { 124 auth: self.auth, 125 params: serde_json::from_value::<T>(self.params)?, 126 }) 127 } 128 } 129 130 /// Authentication uses the hashed user password in PHC format 131 #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] 132 pub struct ApiAuth(pub String); 133 134 impl Debug for ApiAuth { 135 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 136 write!(f, "ApiAuth(****)") 137 } 138 } 139 140 #[derive(Debug, Clone)] 141 pub struct ApiError { 142 pub code: i32, 143 pub message: String, 144 } 145 146 impl ApiError { 147 pub fn new(code: i32, message: String) -> Self { 148 Self { code, message } 149 } 150 151 pub fn not_found(message: String) -> Self { 152 Self::new(404, message) 153 } 154 155 pub fn bad_request(message: String) -> Self { 156 Self::new(400, message) 157 } 158 159 pub fn unauthorized() -> Self { 160 Self::new(401, "Invalid authorization".to_string()) 161 } 162 163 pub fn server_error(message: String) -> Self { 164 Self::new(500, message) 165 } 166 } 167 168 /// State made available to all API endpoints for handling a request 169 pub struct ApiEndpointContext<'dbtx> { 170 db: Database, 171 dbtx: DatabaseTransaction<'dbtx, Committable>, 172 has_auth: bool, 173 request_auth: Option<ApiAuth>, 174 } 175 176 impl<'a> ApiEndpointContext<'a> { 177 /// `db` and `dbtx` should be isolated. 178 pub fn new( 179 db: Database, 180 dbtx: DatabaseTransaction<'a, Committable>, 181 has_auth: bool, 182 request_auth: Option<ApiAuth>, 183 ) -> Self { 184 Self { 185 db, 186 dbtx, 187 has_auth, 188 request_auth, 189 } 190 } 191 192 /// Database tx handle, will be committed 193 pub fn dbtx<'s, 'mtx>(&'s mut self) -> DatabaseTransaction<'mtx, Committable> 194 where 195 'a: 'mtx, 196 's: 'mtx, 197 { 198 // dbtx is already isolated. 199 self.dbtx.to_ref() 200 } 201 202 /// Returns the auth set on the request (regardless of whether it was 203 /// correct) 204 pub fn request_auth(&self) -> Option<ApiAuth> { 205 self.request_auth.clone() 206 } 207 208 /// Whether the request was authenticated as the guardian who controls this 209 /// fedimint server 210 pub fn has_auth(&self) -> bool { 211 self.has_auth 212 } 213 214 pub fn db(&self) -> Database { 215 self.db.clone() 216 } 217 218 /// Waits for key to be present in database. 219 pub fn wait_key_exists<K>(&self, key: K) -> impl Future<Output = K::Value> 220 where 221 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify, 222 { 223 let db = self.db.clone(); 224 // self contains dbtx which is !Send 225 // try removing this and see the error. 226 async move { db.wait_key_exists(&key).await } 227 } 228 229 /// Waits for key to have a value that matches. 230 pub fn wait_value_matches<K>( 231 &self, 232 key: K, 233 matcher: impl Fn(&K::Value) -> bool + Copy, 234 ) -> impl Future<Output = K::Value> 235 where 236 K: DatabaseKey + DatabaseRecord + DatabaseKeyWithNotify, 237 { 238 let db = self.db.clone(); 239 async move { db.wait_key_check(&key, |v| v.filter(matcher)).await.0 } 240 } 241 242 /// Attempts to commit the dbtx or returns an ApiError 243 pub async fn commit_tx_result(self, path: &'static str) -> Result<(), ApiError> { 244 self.dbtx.commit_tx_result().await.map_err(|_err| { 245 tracing::warn!( 246 target: fedimint_logging::LOG_NET_API, 247 path, 248 "API server error when writing to database: {:?}", 249 _err 250 ); 251 ApiError { 252 code: 500, 253 message: "API server error when writing to database".to_string(), 254 } 255 }) 256 } 257 } 258 259 #[apply(async_trait_maybe_send!)] 260 pub trait TypedApiEndpoint { 261 type State: Sync; 262 263 /// example: /transaction 264 const PATH: &'static str; 265 266 type Param: serde::de::DeserializeOwned + Send; 267 type Response: serde::Serialize; 268 269 async fn handle<'state, 'context, 'dbtx>( 270 state: &'state Self::State, 271 context: &'context mut ApiEndpointContext<'dbtx>, 272 request: Self::Param, 273 ) -> Result<Self::Response, ApiError> 274 where 275 'dbtx: 'context; 276 } 277 278 #[doc(hidden)] 279 pub mod __reexports { 280 pub use serde_json; 281 } 282 283 /// # Example 284 /// 285 /// ```rust 286 /// # use fedimint_core::module::ApiVersion; 287 /// # use fedimint_core::module::{api_endpoint, ApiEndpoint, registry::ModuleInstanceId}; 288 /// struct State; 289 /// 290 /// let _: ApiEndpoint<State> = api_endpoint! { 291 /// "/foobar", 292 /// ApiVersion::new(0, 3), 293 /// async |state: &State, _dbtx, params: ()| -> i32 { 294 /// Ok(0) 295 /// } 296 /// }; 297 /// ``` 298 #[macro_export] 299 macro_rules! __api_endpoint { 300 ( 301 $path:expr, 302 // Api Version this endpoint was introduced in, at the current consensus level 303 // Currently for documentation purposes only. 304 $version_introduced:expr, 305 async |$state:ident: &$state_ty:ty, $context:ident, $param:ident: $param_ty:ty| -> $resp_ty:ty $body:block 306 ) => {{ 307 struct Endpoint; 308 309 #[$crate::apply($crate::async_trait_maybe_send!)] 310 impl $crate::module::TypedApiEndpoint for Endpoint { 311 const PATH: &'static str = $path; 312 type State = $state_ty; 313 type Param = $param_ty; 314 type Response = $resp_ty; 315 316 async fn handle<'state, 'context, 'dbtx>( 317 $state: &'state Self::State, 318 $context: &'context mut $crate::module::ApiEndpointContext<'dbtx>, 319 $param: Self::Param, 320 ) -> ::std::result::Result<Self::Response, $crate::module::ApiError> { 321 { 322 // just to enforce the correct type 323 const __API_VERSION: $crate::module::ApiVersion = $version_introduced; 324 } 325 $body 326 } 327 } 328 329 $crate::module::ApiEndpoint::from_typed::<Endpoint>() 330 }}; 331 } 332 333 pub use __api_endpoint as api_endpoint; 334 use fedimint_core::config::DkgResult; 335 336 use self::registry::ModuleDecoderRegistry; 337 338 type HandlerFnReturn<'a> = 339 Pin<Box<maybe_add_send!(dyn Future<Output = Result<serde_json::Value, ApiError>> + 'a)>>; 340 type HandlerFn<M> = Box< 341 maybe_add_send_sync!( 342 dyn for<'a> Fn(&'a M, ApiEndpointContext<'a>, ApiRequestErased) -> HandlerFnReturn<'a> 343 ), 344 >; 345 346 /// Definition of an API endpoint defined by a module `M`. 347 pub struct ApiEndpoint<M> { 348 /// Path under which the API endpoint can be reached. It should start with a 349 /// `/` e.g. `/transaction`. E.g. this API endpoint would be reachable 350 /// under `module_module_instance_id_transaction` depending on the 351 /// module name returned by `[FedertionModule::api_base_name]`. 352 pub path: &'static str, 353 /// Handler for the API call that takes the following arguments: 354 /// * Reference to the module which defined it 355 /// * Request parameters parsed into JSON `[Value](serde_json::Value)` 356 pub handler: HandlerFn<M>, 357 } 358 359 /// Global request ID used for logging 360 static REQ_ID: AtomicU64 = AtomicU64::new(0); 361 362 // <()> is used to avoid specify state. 363 impl ApiEndpoint<()> { 364 pub fn from_typed<E: TypedApiEndpoint>() -> ApiEndpoint<E::State> 365 where 366 <E as TypedApiEndpoint>::Response: MaybeSend, 367 E::Param: Debug, 368 E::Response: Debug, 369 { 370 async fn handle_request<'state, 'context, 'dbtx, E>( 371 state: &'state E::State, 372 context: &'context mut ApiEndpointContext<'dbtx>, 373 request: ApiRequest<E::Param>, 374 ) -> Result<E::Response, ApiError> 375 where 376 'dbtx: 'context, 377 E: TypedApiEndpoint, 378 E::Param: Debug, 379 E::Response: Debug, 380 { 381 tracing::debug!(target: LOG_NET_API, path = E::PATH, ?request, "received api request"); 382 let result = E::handle(state, context, request.params).await; 383 if let Err(error) = &result { 384 tracing::warn!(target: LOG_NET_API, path = E::PATH, ?error, "api request error"); 385 } else { 386 tracing::debug!(target: LOG_NET_API, path = E::PATH, "api request complete"); 387 } 388 result 389 } 390 391 ApiEndpoint { 392 path: E::PATH, 393 handler: Box::new(|m, mut context, request| { 394 Box::pin(async move { 395 let request = request 396 .to_typed() 397 .map_err(|e| ApiError::bad_request(e.to_string()))?; 398 399 let span = tracing::info_span!( 400 target: LOG_NET_API, 401 "api_req", 402 id = REQ_ID.fetch_add(1, Ordering::SeqCst), 403 method = E::PATH, 404 ); 405 let ret = handle_request::<E>(m, &mut context, request) 406 .instrument(span) 407 .await?; 408 409 context.commit_tx_result(E::PATH).await?; 410 411 Ok(serde_json::to_value(ret).expect("encoding error")) 412 }) 413 }), 414 } 415 } 416 } 417 418 /// Operations common to Server and Client side module gen dyn newtypes 419 /// 420 /// Due to conflict of `impl Trait for T` for both `ServerModuleInit` and 421 /// `ClientModuleInit`, we can't really have a `ICommonModuleInit`, so to unify 422 /// them in `ModuleInitRegistry` we move the common functionality to be an 423 /// interface over their dyn newtype wrappers. A bit weird, but works. 424 #[apply(async_trait_maybe_send!)] 425 pub trait IDynCommonModuleInit: Debug { 426 fn decoder(&self) -> Decoder; 427 428 fn module_kind(&self) -> ModuleKind; 429 430 fn to_dyn_common(&self) -> DynCommonModuleInit; 431 432 fn database_version(&self) -> DatabaseVersion; 433 434 async fn dump_database( 435 &self, 436 dbtx: &mut DatabaseTransaction<'_>, 437 prefix_names: Vec<String>, 438 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_>; 439 } 440 441 /// Trait implemented by every `*ModuleInit` (server or client side) 442 pub trait ModuleInit: Debug + Clone + Send + Sync + 'static { 443 type Common: CommonModuleInit; 444 445 /// This represents the module's database version that the current code is 446 /// compatible with. It is important to increment this value whenever a 447 /// key or a value that is persisted to the database within the module 448 /// changes. It is also important to add the corresponding 449 /// migration function in `get_database_migrations` which should define how 450 /// to move from the previous database version to the current version. 451 const DATABASE_VERSION: DatabaseVersion; 452 453 fn dump_database( 454 &self, 455 dbtx: &mut DatabaseTransaction<'_>, 456 prefix_names: Vec<String>, 457 ) -> maybe_add_send!( 458 impl Future< 459 Output = Box< 460 dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_, 461 >, 462 > 463 ); 464 } 465 466 #[apply(async_trait_maybe_send!)] 467 impl<T> IDynCommonModuleInit for T 468 where 469 T: ModuleInit, 470 { 471 fn decoder(&self) -> Decoder { 472 T::Common::decoder() 473 } 474 475 fn module_kind(&self) -> ModuleKind { 476 T::Common::KIND 477 } 478 479 fn to_dyn_common(&self) -> DynCommonModuleInit { 480 DynCommonModuleInit::from_inner(Arc::new(self.clone())) 481 } 482 483 fn database_version(&self) -> DatabaseVersion { 484 <Self as ModuleInit>::DATABASE_VERSION 485 } 486 487 async fn dump_database( 488 &self, 489 dbtx: &mut DatabaseTransaction<'_>, 490 prefix_names: Vec<String>, 491 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> { 492 <Self as ModuleInit>::dump_database(self, dbtx, prefix_names).await 493 } 494 } 495 496 /// Interface for Module Generation 497 /// 498 /// This trait contains the methods responsible for the module's 499 /// - initialization 500 /// - config generation 501 /// - config validation 502 /// 503 /// Once the module configuration is ready, the module can be instantiated via 504 /// `[Self::init]`. 505 #[apply(async_trait_maybe_send!)] 506 pub trait IServerModuleInit: IDynCommonModuleInit { 507 fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static); 508 509 fn supported_api_versions(&self) -> SupportedModuleApiVersions; 510 511 /// Initialize the [`DynServerModule`] instance from its config 512 async fn init( 513 &self, 514 peer_num: NumPeers, 515 cfg: ServerModuleConfig, 516 db: Database, 517 task_group: &TaskGroup, 518 our_peer_id: PeerId, 519 ) -> anyhow::Result<DynServerModule>; 520 521 fn validate_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<()>; 522 523 fn trusted_dealer_gen( 524 &self, 525 peers: &[PeerId], 526 params: &ConfigGenModuleParams, 527 ) -> BTreeMap<PeerId, ServerModuleConfig>; 528 529 async fn distributed_gen( 530 &self, 531 peers: &PeerHandle, 532 params: &ConfigGenModuleParams, 533 ) -> DkgResult<ServerModuleConfig>; 534 535 fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>; 536 537 fn get_client_config( 538 &self, 539 module_instance_id: ModuleInstanceId, 540 config: &ServerModuleConsensusConfig, 541 ) -> anyhow::Result<ClientModuleConfig>; 542 543 /// Retrieves the migrations map from the server module to be applied to the 544 /// database before the module is initialized. The migrations map is 545 /// indexed on the from version. 546 fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ServerMigrationFn>; 547 } 548 549 dyn_newtype_define!( 550 #[derive(Clone)] 551 pub DynCommonModuleInit(Arc<IDynCommonModuleInit>) 552 ); 553 554 impl AsRef<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)> for DynCommonModuleInit { 555 fn as_ref(&self) -> &(maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)) { 556 self.inner.as_ref() 557 } 558 } 559 560 impl DynCommonModuleInit { 561 pub fn from_inner( 562 inner: Arc<maybe_add_send_sync!(dyn IDynCommonModuleInit + 'static)>, 563 ) -> Self { 564 DynCommonModuleInit { inner } 565 } 566 } 567 568 dyn_newtype_define!( 569 #[derive(Clone)] 570 pub DynServerModuleInit(Arc<IServerModuleInit>) 571 ); 572 573 impl AsRef<dyn IDynCommonModuleInit + Send + Sync + 'static> for DynServerModuleInit { 574 fn as_ref(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) { 575 self.inner.as_common() 576 } 577 } 578 579 /// Logic and constant common between server side and client side modules 580 #[apply(async_trait_maybe_send!)] 581 pub trait CommonModuleInit: Debug + Sized { 582 const CONSENSUS_VERSION: ModuleConsensusVersion; 583 const KIND: ModuleKind; 584 585 type ClientConfig: ClientConfig; 586 587 fn decoder() -> Decoder; 588 } 589 590 pub struct ServerModuleInitArgs<S> 591 where 592 S: ServerModuleInit, 593 { 594 cfg: ServerModuleConfig, 595 db: Database, 596 task_group: TaskGroup, 597 our_peer_id: PeerId, 598 num_peers: NumPeers, 599 // ClientModuleInitArgs needs a bound because sometimes we need 600 // to pass associated-types data, so let's just put it here right away 601 _marker: marker::PhantomData<S>, 602 } 603 604 impl<S> ServerModuleInitArgs<S> 605 where 606 S: ServerModuleInit, 607 { 608 pub fn cfg(&self) -> &ServerModuleConfig { 609 &self.cfg 610 } 611 612 pub fn db(&self) -> &Database { 613 &self.db 614 } 615 616 pub fn num_peers(&self) -> NumPeers { 617 self.num_peers 618 } 619 620 pub fn task_group(&self) -> &TaskGroup { 621 &self.task_group 622 } 623 624 pub fn our_peer_id(&self) -> PeerId { 625 self.our_peer_id 626 } 627 } 628 /// Module Generation trait with associated types 629 /// 630 /// Needs to be implemented by module generation type 631 /// 632 /// For examples, take a look at one of the `MintConfigGenerator`, 633 /// `WalletConfigGenerator`, or `LightningConfigGenerator` structs. 634 #[apply(async_trait_maybe_send!)] 635 pub trait ServerModuleInit: ModuleInit + Sized { 636 type Params: ModuleInitParams; 637 638 /// Version of the module consensus supported by this implementation given a 639 /// certain [`CoreConsensusVersion`]. 640 /// 641 /// Refer to [`ModuleConsensusVersion`] for more information about 642 /// versioning. 643 /// 644 /// One module implementation ([`ServerModuleInit`] of a given 645 /// [`ModuleKind`]) can potentially implement multiple versions of the 646 /// consensus, and depending on the config module instance config, 647 /// instantiate the desired one. This method should expose all the 648 /// available versions, purely for information, setup UI and sanity 649 /// checking purposes. 650 fn versions(&self, core: CoreConsensusVersion) -> &[ModuleConsensusVersion]; 651 652 fn supported_api_versions(&self) -> SupportedModuleApiVersions; 653 654 fn kind() -> ModuleKind { 655 <Self as ModuleInit>::Common::KIND 656 } 657 658 /// Initialize the [`DynServerModule`] instance from its config 659 async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<DynServerModule>; 660 661 fn parse_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<Self::Params> { 662 params.to_typed::<Self::Params>() 663 } 664 665 fn trusted_dealer_gen( 666 &self, 667 peers: &[PeerId], 668 params: &ConfigGenModuleParams, 669 ) -> BTreeMap<PeerId, ServerModuleConfig>; 670 671 async fn distributed_gen( 672 &self, 673 peer: &PeerHandle, 674 params: &ConfigGenModuleParams, 675 ) -> DkgResult<ServerModuleConfig>; 676 677 fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()>; 678 679 /// Converts the consensus config into the client config 680 fn get_client_config( 681 &self, 682 config: &ServerModuleConsensusConfig, 683 ) -> anyhow::Result<<<Self as ModuleInit>::Common as CommonModuleInit>::ClientConfig>; 684 685 /// Retrieves the migrations map from the server module to be applied to the 686 /// database before the module is initialized. The migrations map is 687 /// indexed on the from version. 688 fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ServerMigrationFn> { 689 BTreeMap::new() 690 } 691 } 692 693 #[apply(async_trait_maybe_send!)] 694 impl<T> IServerModuleInit for T 695 where 696 T: ServerModuleInit + 'static + Sync, 697 { 698 fn as_common(&self) -> &(dyn IDynCommonModuleInit + Send + Sync + 'static) { 699 self 700 } 701 702 fn supported_api_versions(&self) -> SupportedModuleApiVersions { 703 <Self as ServerModuleInit>::supported_api_versions(self) 704 } 705 706 async fn init( 707 &self, 708 num_peers: NumPeers, 709 cfg: ServerModuleConfig, 710 db: Database, 711 task_group: &TaskGroup, 712 our_peer_id: PeerId, 713 ) -> anyhow::Result<DynServerModule> { 714 <Self as ServerModuleInit>::init( 715 self, 716 &ServerModuleInitArgs { 717 num_peers, 718 cfg, 719 db, 720 task_group: task_group.clone(), 721 our_peer_id, 722 _marker: Default::default(), 723 }, 724 ) 725 .await 726 } 727 728 fn validate_params(&self, params: &ConfigGenModuleParams) -> anyhow::Result<()> { 729 <Self as ServerModuleInit>::parse_params(self, params)?; 730 Ok(()) 731 } 732 733 fn trusted_dealer_gen( 734 &self, 735 peers: &[PeerId], 736 params: &ConfigGenModuleParams, 737 ) -> BTreeMap<PeerId, ServerModuleConfig> { 738 <Self as ServerModuleInit>::trusted_dealer_gen(self, peers, params) 739 } 740 741 async fn distributed_gen( 742 &self, 743 peers: &PeerHandle, 744 params: &ConfigGenModuleParams, 745 ) -> DkgResult<ServerModuleConfig> { 746 <Self as ServerModuleInit>::distributed_gen(self, peers, params).await 747 } 748 749 fn validate_config(&self, identity: &PeerId, config: ServerModuleConfig) -> anyhow::Result<()> { 750 <Self as ServerModuleInit>::validate_config(self, identity, config) 751 } 752 753 fn get_client_config( 754 &self, 755 module_instance_id: ModuleInstanceId, 756 config: &ServerModuleConsensusConfig, 757 ) -> anyhow::Result<ClientModuleConfig> { 758 ClientModuleConfig::from_typed( 759 module_instance_id, 760 <Self as ServerModuleInit>::kind(), 761 config.version, 762 <Self as ServerModuleInit>::get_client_config(self, config)?, 763 ) 764 } 765 766 fn get_database_migrations(&self) -> BTreeMap<DatabaseVersion, ServerMigrationFn> { 767 <Self as ServerModuleInit>::get_database_migrations(self) 768 } 769 } 770 771 /// Module associated types required by both client and server 772 pub trait ModuleCommon { 773 type ClientConfig: ClientConfig; 774 type Input: Input; 775 type Output: Output; 776 type OutputOutcome: OutputOutcome; 777 type ConsensusItem: ModuleConsensusItem; 778 type InputError: InputError; 779 type OutputError: OutputError; 780 781 fn decoder_builder() -> DecoderBuilder { 782 let mut decoder_builder = Decoder::builder(); 783 decoder_builder.with_decodable_type::<Self::ClientConfig>(); 784 decoder_builder.with_decodable_type::<Self::Input>(); 785 decoder_builder.with_decodable_type::<Self::Output>(); 786 decoder_builder.with_decodable_type::<Self::OutputOutcome>(); 787 decoder_builder.with_decodable_type::<Self::ConsensusItem>(); 788 decoder_builder.with_decodable_type::<Self::InputError>(); 789 decoder_builder.with_decodable_type::<Self::OutputError>(); 790 791 decoder_builder 792 } 793 794 fn decoder() -> Decoder { 795 Self::decoder_builder().build() 796 } 797 } 798 799 #[apply(async_trait_maybe_send!)] 800 pub trait ServerModule: Debug + Sized { 801 type Common: ModuleCommon; 802 803 type Init: ServerModuleInit; 804 805 fn module_kind() -> ModuleKind { 806 // Note: All modules should define kinds as &'static str, so this doesn't 807 // allocate 808 <Self::Init as ModuleInit>::Common::KIND 809 } 810 811 /// Returns a decoder for the following associated types of this module: 812 /// * `ClientConfig` 813 /// * `Input` 814 /// * `Output` 815 /// * `OutputOutcome` 816 /// * `ConsensusItem` 817 /// * `InputError` 818 /// * `OutputError` 819 fn decoder() -> Decoder { 820 Self::Common::decoder_builder().build() 821 } 822 823 /// This module's contribution to the next consensus proposal. This method 824 /// is only guaranteed to be called once every few seconds. Consensus items 825 /// are not meant to be latency critical; do not create them as 826 /// a response to a processed transaction. Only use consensus items to 827 /// establish consensus on a value that is required to verify 828 /// transactions, like unix time, block heights and feerates, and model all 829 /// other state changes trough transactions. The intention for this method 830 /// is to always return all available consensus items even if they are 831 /// redundant while process_consensus_item returns an error for the 832 /// redundant proposals. 833 /// 834 /// If you think you actually do require latency critical consensus items or 835 /// have trouble designing your module in order to avoid them please contact 836 /// the Fedimint developers. 837 async fn consensus_proposal<'a>( 838 &'a self, 839 dbtx: &mut DatabaseTransaction<'_>, 840 ) -> Vec<<Self::Common as ModuleCommon>::ConsensusItem>; 841 842 /// This function is called once for every consensus item. The function 843 /// should return Ok if and only if the consensus item changes 844 /// the system state. *Therefore this method should return an error in case 845 /// of merely redundant consensus items such that they will be purged from 846 /// the history of the federation.* This enables consensus_proposal to 847 /// return all available consensus item without wasting disk 848 /// space with redundant consensus items. 849 async fn process_consensus_item<'a, 'b>( 850 &'a self, 851 dbtx: &mut DatabaseTransaction<'b>, 852 consensus_item: <Self::Common as ModuleCommon>::ConsensusItem, 853 peer_id: PeerId, 854 ) -> anyhow::Result<()>; 855 856 /// Try to spend a transaction input. On success all necessary updates will 857 /// be part of the database transaction. On failure (e.g. double spend) 858 /// the database transaction is rolled back and the operation will take 859 /// no effect. 860 async fn process_input<'a, 'b, 'c>( 861 &'a self, 862 dbtx: &mut DatabaseTransaction<'c>, 863 input: &'b <Self::Common as ModuleCommon>::Input, 864 ) -> Result<InputMeta, <Self::Common as ModuleCommon>::InputError>; 865 866 /// Try to create an output (e.g. issue notes, peg-out BTC, …). On success 867 /// all necessary updates to the database will be part of the database 868 /// transaction. On failure (e.g. double spend) the database transaction 869 /// is rolled back and the operation will take no effect. 870 /// 871 /// The supplied `out_point` identifies the operation (e.g. a peg-out or 872 /// note issuance) and can be used to retrieve its outcome later using 873 /// `output_status`. 874 async fn process_output<'a, 'b>( 875 &'a self, 876 dbtx: &mut DatabaseTransaction<'b>, 877 output: &'a <Self::Common as ModuleCommon>::Output, 878 out_point: OutPoint, 879 ) -> Result<TransactionItemAmount, <Self::Common as ModuleCommon>::OutputError>; 880 881 /// Retrieve the current status of the output. Depending on the module this 882 /// might contain data needed by the client to access funds or give an 883 /// estimate of when funds will be available. Returns `None` if the 884 /// output is unknown, **NOT** if it is just not ready yet. 885 async fn output_status( 886 &self, 887 dbtx: &mut DatabaseTransaction<'_>, 888 out_point: OutPoint, 889 ) -> Option<<Self::Common as ModuleCommon>::OutputOutcome>; 890 891 /// Queries the database and returns all assets and liabilities of the 892 /// module. 893 /// 894 /// Summing over all modules, if liabilities > assets then an error has 895 /// occurred in the database and consensus should halt. 896 async fn audit( 897 &self, 898 dbtx: &mut DatabaseTransaction<'_>, 899 audit: &mut Audit, 900 module_instance_id: ModuleInstanceId, 901 ); 902 903 /// Returns a list of custom API endpoints defined by the module. These are 904 /// made available both to users as well as to other modules. They thus 905 /// should be deterministic, only dependant on their input and the 906 /// current epoch. 907 fn api_endpoints(&self) -> Vec<ApiEndpoint<Self>>; 908 } 909 910 /// Creates a struct that can be used to make our module-decodable structs 911 /// interact with `serde`-based APIs (AlephBFT, jsonrpsee). It creates a wrapper 912 /// that holds the data as serialized 913 // bytes internally. 914 #[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] 915 pub struct SerdeModuleEncoding<T: Encodable + Decodable>( 916 #[serde(with = "::fedimint_core::encoding::as_hex")] Vec<u8>, 917 #[serde(skip)] PhantomData<T>, 918 ); 919 920 impl<T> fmt::Debug for SerdeModuleEncoding<T> 921 where 922 T: Encodable + Decodable, 923 { 924 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 925 f.write_str("SerdeModuleEncoding(")?; 926 fmt::Debug::fmt(&AbbreviateHexBytes(&self.0), f)?; 927 f.write_str(")")?; 928 Ok(()) 929 } 930 } 931 932 impl<T: Encodable + Decodable> From<&T> for SerdeModuleEncoding<T> { 933 fn from(value: &T) -> Self { 934 let mut bytes = vec![]; 935 fedimint_core::encoding::Encodable::consensus_encode(value, &mut bytes) 936 .expect("Writing to buffer can never fail"); 937 Self(bytes, PhantomData) 938 } 939 } 940 941 impl<T: Encodable + Decodable + 'static> SerdeModuleEncoding<T> { 942 pub fn try_into_inner(&self, modules: &ModuleDecoderRegistry) -> Result<T, DecodeError> { 943 let mut reader = std::io::Cursor::new(&self.0); 944 Decodable::consensus_decode(&mut reader, modules) 945 } 946 947 /// In cases where we know exactly which module kind we expect but don't 948 /// have access to all decoders this function can be used instead. 949 /// 950 /// Note that it just assumes the decoded module instance id to be valid 951 /// since it cannot validate against the decoder registry. The lack of 952 /// access to a decoder registry also makes decoding structs impossible that 953 /// themselves contain module dyn-types (e.g. a module output containing a 954 /// fedimint transaction). 955 pub fn try_into_inner_known_module_kind(&self, decoder: &Decoder) -> Result<T, DecodeError> { 956 let mut reader = std::io::Cursor::new(&self.0); 957 let module_instance = 958 ModuleInstanceId::consensus_decode(&mut reader, &ModuleDecoderRegistry::default())?; 959 960 let total_len = u64::consensus_decode(&mut reader, &ModuleDecoderRegistry::default())?; 961 962 // No recursive module decoding is supported since we give an empty decoder 963 // registry to the decode function 964 decoder.decode_complete(&mut reader, total_len, module_instance, &Default::default()) 965 } 966 } 967 968 /// A handle passed to [`ServerModuleInit::distributed_gen`] 969 /// 970 /// This struct encapsulates dkg data that the module should not have a direct 971 /// access to, and implements higher level dkg operations available to the 972 /// module to complete its distributed initialization inside the federation. 973 #[non_exhaustive] 974 pub struct PeerHandle<'a> { 975 // TODO: this whole type should be a part of a `fedimint-server` and fields here inaccessible 976 // to outside crates, but until `ServerModule` is not in `fedimint-server` this is impossible 977 #[doc(hidden)] 978 pub connections: &'a MuxPeerConnections<(ModuleInstanceId, String), DkgPeerMsg>, 979 #[doc(hidden)] 980 pub module_instance_id: ModuleInstanceId, 981 #[doc(hidden)] 982 pub our_id: PeerId, 983 #[doc(hidden)] 984 pub peers: Vec<PeerId>, 985 } 986 987 impl<'a> PeerHandle<'a> { 988 pub fn new( 989 connections: &'a MuxPeerConnections<(ModuleInstanceId, String), DkgPeerMsg>, 990 module_instance_id: ModuleInstanceId, 991 our_id: PeerId, 992 peers: Vec<PeerId>, 993 ) -> Self { 994 Self { 995 connections, 996 module_instance_id, 997 our_id, 998 peers, 999 } 1000 } 1001 1002 pub fn peer_ids(&self) -> &[PeerId] { 1003 self.peers.as_slice() 1004 } 1005 }