fedimint_empty_server/
lib.rs

1#![deny(clippy::pedantic)]
2#![allow(clippy::module_name_repetitions)]
3#![allow(clippy::must_use_candidate)]
4
5use std::collections::BTreeMap;
6
7use anyhow::bail;
8use async_trait::async_trait;
9use fedimint_core::config::{
10    ConfigGenModuleParams, ServerModuleConfig, ServerModuleConsensusConfig,
11    TypedServerModuleConfig, TypedServerModuleConsensusConfig,
12};
13use fedimint_core::core::ModuleInstanceId;
14use fedimint_core::db::{DatabaseTransaction, DatabaseVersion};
15use fedimint_core::module::audit::Audit;
16use fedimint_core::module::{
17    ApiEndpoint, CORE_CONSENSUS_VERSION, CoreConsensusVersion, InputMeta, ModuleConsensusVersion,
18    ModuleInit, SupportedModuleApiVersions, TransactionItemAmount,
19};
20use fedimint_core::{InPoint, OutPoint, PeerId, push_db_pair_items};
21use fedimint_empty_common::config::{
22    EmptyClientConfig, EmptyConfig, EmptyConfigConsensus, EmptyConfigPrivate, EmptyGenParams,
23};
24use fedimint_empty_common::{
25    EmptyCommonInit, EmptyConsensusItem, EmptyInput, EmptyInputError, EmptyModuleTypes,
26    EmptyOutput, EmptyOutputError, EmptyOutputOutcome, MODULE_CONSENSUS_VERSION,
27};
28use fedimint_server_core::config::PeerHandleOps;
29use fedimint_server_core::migration::ServerModuleDbMigrationFn;
30use fedimint_server_core::{ServerModule, ServerModuleInit, ServerModuleInitArgs};
31use futures::StreamExt;
32use strum::IntoEnumIterator;
33
34use crate::db::{DbKeyPrefix, EmptyExampleKeyPrefix};
35
36pub mod db;
37
38/// Generates the module
39#[derive(Debug, Clone)]
40pub struct EmptyInit;
41
42// TODO: Boilerplate-code
43impl ModuleInit for EmptyInit {
44    type Common = EmptyCommonInit;
45
46    /// Dumps all database items for debugging
47    async fn dump_database(
48        &self,
49        dbtx: &mut DatabaseTransaction<'_>,
50        prefix_names: Vec<String>,
51    ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
52        // TODO: Boilerplate-code
53        let mut items: BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> = BTreeMap::new();
54        let filtered_prefixes = DbKeyPrefix::iter().filter(|f| {
55            prefix_names.is_empty() || prefix_names.contains(&f.to_string().to_lowercase())
56        });
57
58        for table in filtered_prefixes {
59            match table {
60                DbKeyPrefix::Example => {
61                    push_db_pair_items!(
62                        dbtx,
63                        EmptyExampleKeyPrefix,
64                        EmptyExampleKey,
65                        Vec<u8>,
66                        items,
67                        "Empty Example"
68                    );
69                }
70            }
71        }
72
73        Box::new(items.into_iter())
74    }
75}
76
77/// Implementation of server module non-consensus functions
78#[async_trait]
79impl ServerModuleInit for EmptyInit {
80    type Module = Empty;
81    type Params = EmptyGenParams;
82
83    /// Returns the version of this module
84    fn versions(&self, _core: CoreConsensusVersion) -> &[ModuleConsensusVersion] {
85        &[MODULE_CONSENSUS_VERSION]
86    }
87
88    fn supported_api_versions(&self) -> SupportedModuleApiVersions {
89        SupportedModuleApiVersions::from_raw(
90            (CORE_CONSENSUS_VERSION.major, CORE_CONSENSUS_VERSION.minor),
91            (
92                MODULE_CONSENSUS_VERSION.major,
93                MODULE_CONSENSUS_VERSION.minor,
94            ),
95            &[(0, 0)],
96        )
97    }
98
99    /// Initialize the module
100    async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<Self::Module> {
101        Ok(Empty::new(args.cfg().to_typed()?))
102    }
103
104    /// Generates configs for all peers in a trusted manner for testing
105    fn trusted_dealer_gen(
106        &self,
107        peers: &[PeerId],
108        params: &ConfigGenModuleParams,
109        _disable_base_fees: bool,
110    ) -> BTreeMap<PeerId, ServerModuleConfig> {
111        let _params = self.parse_params(params).unwrap();
112        // Generate a config for each peer
113        peers
114            .iter()
115            .map(|&peer| {
116                let config = EmptyConfig {
117                    private: EmptyConfigPrivate,
118                    consensus: EmptyConfigConsensus {},
119                };
120                (peer, config.to_erased())
121            })
122            .collect()
123    }
124
125    /// Generates configs for all peers in an untrusted manner
126    async fn distributed_gen(
127        &self,
128        _peers: &(dyn PeerHandleOps + Send + Sync),
129        params: &ConfigGenModuleParams,
130        _disable_base_fees: bool,
131    ) -> anyhow::Result<ServerModuleConfig> {
132        let _params = self.parse_params(params).unwrap();
133
134        Ok(EmptyConfig {
135            private: EmptyConfigPrivate,
136            consensus: EmptyConfigConsensus {},
137        }
138        .to_erased())
139    }
140
141    /// Converts the consensus config into the client config
142    fn get_client_config(
143        &self,
144        config: &ServerModuleConsensusConfig,
145    ) -> anyhow::Result<EmptyClientConfig> {
146        let _config = EmptyConfigConsensus::from_erased(config)?;
147        Ok(EmptyClientConfig {})
148    }
149
150    fn validate_config(
151        &self,
152        _identity: &PeerId,
153        _config: ServerModuleConfig,
154    ) -> anyhow::Result<()> {
155        Ok(())
156    }
157
158    /// DB migrations to move from old to newer versions
159    fn get_database_migrations(
160        &self,
161    ) -> BTreeMap<DatabaseVersion, ServerModuleDbMigrationFn<Empty>> {
162        BTreeMap::new()
163    }
164}
165
166/// Empty module
167#[derive(Debug)]
168pub struct Empty {
169    pub cfg: EmptyConfig,
170}
171
172/// Implementation of consensus for the server module
173#[async_trait]
174impl ServerModule for Empty {
175    /// Define the consensus types
176    type Common = EmptyModuleTypes;
177    type Init = EmptyInit;
178
179    async fn consensus_proposal(
180        &self,
181        _dbtx: &mut DatabaseTransaction<'_>,
182    ) -> Vec<EmptyConsensusItem> {
183        Vec::new()
184    }
185
186    async fn process_consensus_item<'a, 'b>(
187        &'a self,
188        _dbtx: &mut DatabaseTransaction<'b>,
189        _consensus_item: EmptyConsensusItem,
190        _peer_id: PeerId,
191    ) -> anyhow::Result<()> {
192        // WARNING: `process_consensus_item` should return an `Err` for items that do
193        // not change any internal consensus state. Failure to do so, will result in an
194        // (potentially significantly) increased consensus history size.
195        // If you are using this code as a template,
196        // make sure to read the [`ServerModule::process_consensus_item`] documentation,
197        bail!("The empty module does not use consensus items");
198    }
199
200    async fn process_input<'a, 'b, 'c>(
201        &'a self,
202        _dbtx: &mut DatabaseTransaction<'c>,
203        _input: &'b EmptyInput,
204        _in_point: InPoint,
205    ) -> Result<InputMeta, EmptyInputError> {
206        Err(EmptyInputError::NotSupported)
207    }
208
209    async fn process_output<'a, 'b>(
210        &'a self,
211        _dbtx: &mut DatabaseTransaction<'b>,
212        _output: &'a EmptyOutput,
213        _out_point: OutPoint,
214    ) -> Result<TransactionItemAmount, EmptyOutputError> {
215        Err(EmptyOutputError::NotSupported)
216    }
217
218    async fn output_status(
219        &self,
220        _dbtx: &mut DatabaseTransaction<'_>,
221        _out_point: OutPoint,
222    ) -> Option<EmptyOutputOutcome> {
223        None
224    }
225
226    async fn audit(
227        &self,
228        _dbtx: &mut DatabaseTransaction<'_>,
229        _audit: &mut Audit,
230        _module_instance_id: ModuleInstanceId,
231    ) {
232    }
233
234    fn api_endpoints(&self) -> Vec<ApiEndpoint<Self>> {
235        Vec::new()
236    }
237}
238
239impl Empty {
240    /// Create new module instance
241    pub fn new(cfg: EmptyConfig) -> Empty {
242        Empty { cfg }
243    }
244}