1use fedimint_client_module::DynGlobalClientContext;
2use fedimint_client_module::sm::{DynState, State, StateTransition};
3use fedimint_core::core::{IntoDynInstance, ModuleInstanceId, OperationId};
4use fedimint_core::db::{DatabaseTransaction, IDatabaseTransactionOpsCoreTyped};
5use fedimint_core::encoding::{Decodable, Encodable};
6use fedimint_core::module::AmountUnit;
7use fedimint_core::{Amount, TransactionId};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use crate::db::DummyClientFundsKey;
12use crate::{DummyClientContext, get_funds};
13
14#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
16pub enum DummyStateMachineV1 {
17 Input(Amount, TransactionId, OperationId),
18 Output(Amount, TransactionId, OperationId),
19 InputDone(OperationId),
20 OutputDone(Amount, TransactionId, OperationId),
21 Refund(OperationId),
22 Unreachable(OperationId, Amount),
23}
24
25#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
27pub enum DummyStateMachine {
28 Input(Amount, AmountUnit, TransactionId, OperationId),
29 Output(Amount, AmountUnit, TransactionId, OperationId),
30 InputDone(OperationId),
31 OutputDone(Amount, AmountUnit, TransactionId, OperationId),
32 Refund(OperationId),
33 Unreachable(OperationId, Amount),
34}
35
36impl State for DummyStateMachine {
37 type ModuleContext = DummyClientContext;
38
39 fn transitions(
40 &self,
41 _context: &Self::ModuleContext,
42 global_context: &DynGlobalClientContext,
43 ) -> Vec<StateTransition<Self>> {
44 match self.clone() {
45 DummyStateMachine::Input(amount, unit, txid, id) => vec![StateTransition::new(
46 await_tx_accepted(global_context.clone(), txid),
47 move |dbtx, res, _state: Self| match res {
48 Ok(()) => Box::pin(async move { DummyStateMachine::InputDone(id) }),
50 Err(_) => Box::pin(async move {
52 add_funds(amount, unit, dbtx.module_tx()).await;
53 DummyStateMachine::Refund(id)
54 }),
55 },
56 )],
57 DummyStateMachine::Output(amount, unit, txid, id) => vec![StateTransition::new(
58 await_tx_accepted(global_context.clone(), txid),
59 move |dbtx, res, _state: Self| match res {
60 Ok(()) => Box::pin(async move {
62 add_funds(amount, unit, dbtx.module_tx()).await;
63 DummyStateMachine::OutputDone(amount, unit, txid, id)
64 }),
65 Err(_) => Box::pin(async move { DummyStateMachine::Refund(id) }),
67 },
68 )],
69 DummyStateMachine::InputDone(_)
70 | DummyStateMachine::OutputDone(_, _, _, _)
71 | DummyStateMachine::Refund(_)
72 | DummyStateMachine::Unreachable(_, _) => vec![],
73 }
74 }
75
76 fn operation_id(&self) -> OperationId {
77 match self {
78 DummyStateMachine::Input(_, _, _, id)
79 | DummyStateMachine::Output(_, _, _, id)
80 | DummyStateMachine::InputDone(id)
81 | DummyStateMachine::OutputDone(_, _, _, id)
82 | DummyStateMachine::Refund(id)
83 | DummyStateMachine::Unreachable(id, _) => *id,
84 }
85 }
86}
87
88async fn add_funds(amount: Amount, unit: AmountUnit, mut dbtx: DatabaseTransaction<'_>) {
89 let funds = get_funds(&mut dbtx, unit).await + amount;
90 dbtx.insert_entry(&DummyClientFundsKey { unit }, &funds)
91 .await;
92}
93
94async fn await_tx_accepted(
96 context: DynGlobalClientContext,
97 txid: TransactionId,
98) -> Result<(), String> {
99 context.await_tx_accepted(txid).await
100}
101
102impl IntoDynInstance for DummyStateMachine {
104 type DynType = DynState;
105
106 fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
107 DynState::from_typed(instance_id, self)
108 }
109}
110
111#[derive(Error, Debug, Serialize, Deserialize, Encodable, Decodable, Clone, Eq, PartialEq)]
112pub enum DummyError {
113 #[error("Dummy module had an internal error")]
114 DummyInternalError,
115}