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