1#![deny(clippy::pedantic)]
2#![allow(clippy::cast_possible_wrap)]
3#![allow(clippy::missing_errors_doc)]
4#![allow(clippy::module_name_repetitions)]
5#![allow(clippy::must_use_candidate)]
6
7use std::collections::BTreeMap;
8
9use anyhow::bail;
10use async_trait::async_trait;
11use fedimint_core::config::{
12 ConfigGenModuleParams, ServerModuleConfig, ServerModuleConsensusConfig,
13 TypedServerModuleConfig, TypedServerModuleConsensusConfig,
14};
15use fedimint_core::core::ModuleInstanceId;
16use fedimint_core::db::{DatabaseTransaction, DatabaseVersion, IDatabaseTransactionOpsCoreTyped};
17use fedimint_core::module::audit::Audit;
18use fedimint_core::module::{
19 ApiEndpoint, CORE_CONSENSUS_VERSION, CoreConsensusVersion, InputMeta, ModuleConsensusVersion,
20 ModuleInit, SupportedModuleApiVersions, TransactionItemAmount,
21};
22use fedimint_core::{Amount, InPoint, OutPoint, PeerId, push_db_pair_items};
23use fedimint_dummy_common::config::{
24 DummyClientConfig, DummyConfig, DummyConfigConsensus, DummyConfigLocal, DummyConfigPrivate,
25 DummyGenParams,
26};
27use fedimint_dummy_common::{
28 DummyCommonInit, DummyConsensusItem, DummyInput, DummyInputError, DummyModuleTypes,
29 DummyOutput, DummyOutputError, DummyOutputOutcome, MODULE_CONSENSUS_VERSION,
30 broken_fed_public_key, fed_public_key,
31};
32use fedimint_server_core::config::PeerHandleOps;
33use fedimint_server_core::migration::ServerModuleDbMigrationFn;
34use fedimint_server_core::{ServerModule, ServerModuleInit, ServerModuleInitArgs};
35use futures::{FutureExt, StreamExt};
36use strum::IntoEnumIterator;
37
38use crate::db::{
39 DbKeyPrefix, DummyFundsKeyV1, DummyFundsPrefixV1, DummyOutcomeKey, DummyOutcomePrefix,
40 migrate_to_v1,
41};
42
43pub mod db;
44
45#[derive(Debug, Clone)]
47pub struct DummyInit;
48
49impl ModuleInit for DummyInit {
51 type Common = DummyCommonInit;
52
53 async fn dump_database(
55 &self,
56 dbtx: &mut DatabaseTransaction<'_>,
57 prefix_names: Vec<String>,
58 ) -> Box<dyn Iterator<Item = (String, Box<dyn erased_serde::Serialize + Send>)> + '_> {
59 let mut items: BTreeMap<String, Box<dyn erased_serde::Serialize + Send>> = BTreeMap::new();
61 let filtered_prefixes = DbKeyPrefix::iter().filter(|f| {
62 prefix_names.is_empty() || prefix_names.contains(&f.to_string().to_lowercase())
63 });
64
65 for table in filtered_prefixes {
66 match table {
67 DbKeyPrefix::Funds => {
68 push_db_pair_items!(
69 dbtx,
70 DummyFundsPrefixV1,
71 DummyFundsKeyV1,
72 Amount,
73 items,
74 "Dummy Funds"
75 );
76 }
77 DbKeyPrefix::Outcome => {
78 push_db_pair_items!(
79 dbtx,
80 DummyOutcomePrefix,
81 DummyOutcomeKey,
82 DummyOutputOutcome,
83 items,
84 "Dummy Outputs"
85 );
86 }
87 }
88 }
89
90 Box::new(items.into_iter())
91 }
92}
93
94#[async_trait]
96impl ServerModuleInit for DummyInit {
97 type Module = Dummy;
98 type Params = DummyGenParams;
99
100 fn versions(&self, _core: CoreConsensusVersion) -> &[ModuleConsensusVersion] {
102 &[MODULE_CONSENSUS_VERSION]
103 }
104
105 fn supported_api_versions(&self) -> SupportedModuleApiVersions {
106 SupportedModuleApiVersions::from_raw(
107 (CORE_CONSENSUS_VERSION.major, CORE_CONSENSUS_VERSION.minor),
108 (
109 MODULE_CONSENSUS_VERSION.major,
110 MODULE_CONSENSUS_VERSION.minor,
111 ),
112 &[(0, 0)],
113 )
114 }
115
116 async fn init(&self, args: &ServerModuleInitArgs<Self>) -> anyhow::Result<Self::Module> {
118 Ok(Dummy::new(args.cfg().to_typed()?))
119 }
120
121 fn trusted_dealer_gen(
123 &self,
124 peers: &[PeerId],
125 params: &ConfigGenModuleParams,
126 ) -> BTreeMap<PeerId, ServerModuleConfig> {
127 let params = self.parse_params(params).unwrap();
128 peers
130 .iter()
131 .map(|&peer| {
132 let config = DummyConfig {
133 local: DummyConfigLocal {},
134 private: DummyConfigPrivate,
135 consensus: DummyConfigConsensus {
136 tx_fee: params.consensus.tx_fee,
137 },
138 };
139 (peer, config.to_erased())
140 })
141 .collect()
142 }
143
144 async fn distributed_gen(
146 &self,
147 _peers: &(dyn PeerHandleOps + Send + Sync),
148 params: &ConfigGenModuleParams,
149 ) -> anyhow::Result<ServerModuleConfig> {
150 let params = self.parse_params(params).unwrap();
151
152 Ok(DummyConfig {
153 local: DummyConfigLocal {},
154 private: DummyConfigPrivate,
155 consensus: DummyConfigConsensus {
156 tx_fee: params.consensus.tx_fee,
157 },
158 }
159 .to_erased())
160 }
161
162 fn get_client_config(
164 &self,
165 config: &ServerModuleConsensusConfig,
166 ) -> anyhow::Result<DummyClientConfig> {
167 let config = DummyConfigConsensus::from_erased(config)?;
168 Ok(DummyClientConfig {
169 tx_fee: config.tx_fee,
170 })
171 }
172
173 fn validate_config(
174 &self,
175 _identity: &PeerId,
176 _config: ServerModuleConfig,
177 ) -> anyhow::Result<()> {
178 Ok(())
179 }
180
181 fn get_database_migrations(
183 &self,
184 ) -> BTreeMap<DatabaseVersion, ServerModuleDbMigrationFn<Dummy>> {
185 let mut migrations: BTreeMap<DatabaseVersion, ServerModuleDbMigrationFn<Dummy>> =
186 BTreeMap::new();
187 migrations.insert(
188 DatabaseVersion(0),
189 Box::new(|ctx| migrate_to_v1(ctx).boxed()),
190 );
191 migrations
192 }
193}
194
195#[derive(Debug)]
197pub struct Dummy {
198 pub cfg: DummyConfig,
199}
200
201#[async_trait]
203impl ServerModule for Dummy {
204 type Common = DummyModuleTypes;
206 type Init = DummyInit;
207
208 async fn consensus_proposal(
209 &self,
210 _dbtx: &mut DatabaseTransaction<'_>,
211 ) -> Vec<DummyConsensusItem> {
212 Vec::new()
213 }
214
215 async fn process_consensus_item<'a, 'b>(
216 &'a self,
217 _dbtx: &mut DatabaseTransaction<'b>,
218 _consensus_item: DummyConsensusItem,
219 _peer_id: PeerId,
220 ) -> anyhow::Result<()> {
221 bail!("The dummy module does not use consensus items");
227 }
228
229 async fn process_input<'a, 'b, 'c>(
230 &'a self,
231 dbtx: &mut DatabaseTransaction<'c>,
232 input: &'b DummyInput,
233 _in_point: InPoint,
234 ) -> Result<InputMeta, DummyInputError> {
235 let current_funds = dbtx
236 .get_value(&DummyFundsKeyV1(input.account))
237 .await
238 .unwrap_or(Amount::ZERO);
239
240 if input.amount > current_funds
242 && fed_public_key() != input.account
243 && broken_fed_public_key() != input.account
244 {
245 return Err(DummyInputError::NotEnoughFunds);
246 }
247
248 let updated_funds = if fed_public_key() == input.account {
250 current_funds + input.amount
251 } else if broken_fed_public_key() == input.account {
252 current_funds
254 } else {
255 current_funds.saturating_sub(input.amount)
256 };
257
258 dbtx.insert_entry(&DummyFundsKeyV1(input.account), &updated_funds)
259 .await;
260
261 Ok(InputMeta {
262 amount: TransactionItemAmount {
263 amount: input.amount,
264 fee: self.cfg.consensus.tx_fee,
265 },
266 pub_key: input.account,
268 })
269 }
270
271 async fn process_output<'a, 'b>(
272 &'a self,
273 dbtx: &mut DatabaseTransaction<'b>,
274 output: &'a DummyOutput,
275 out_point: OutPoint,
276 ) -> Result<TransactionItemAmount, DummyOutputError> {
277 let current_funds = dbtx.get_value(&DummyFundsKeyV1(output.account)).await;
279 let updated_funds = current_funds.unwrap_or(Amount::ZERO) + output.amount;
280 dbtx.insert_entry(&DummyFundsKeyV1(output.account), &updated_funds)
281 .await;
282
283 let outcome = DummyOutputOutcome(updated_funds, output.account);
285 dbtx.insert_entry(&DummyOutcomeKey(out_point), &outcome)
286 .await;
287
288 Ok(TransactionItemAmount {
289 amount: output.amount,
290 fee: self.cfg.consensus.tx_fee,
291 })
292 }
293
294 async fn output_status(
295 &self,
296 dbtx: &mut DatabaseTransaction<'_>,
297 out_point: OutPoint,
298 ) -> Option<DummyOutputOutcome> {
299 dbtx.get_value(&DummyOutcomeKey(out_point)).await
301 }
302
303 async fn audit(
304 &self,
305 dbtx: &mut DatabaseTransaction<'_>,
306 audit: &mut Audit,
307 module_instance_id: ModuleInstanceId,
308 ) {
309 audit
310 .add_items(
311 dbtx,
312 module_instance_id,
313 &DummyFundsPrefixV1,
314 |k, v| match k {
315 DummyFundsKeyV1(key)
318 if key == fed_public_key() || key == broken_fed_public_key() =>
319 {
320 v.msats as i64
321 }
322 DummyFundsKeyV1(_) => -(v.msats as i64),
324 },
325 )
326 .await;
327 }
328
329 fn api_endpoints(&self) -> Vec<ApiEndpoint<Self>> {
330 Vec::new()
331 }
332}
333
334impl Dummy {
335 pub fn new(cfg: DummyConfig) -> Dummy {
337 Dummy { cfg }
338 }
339}