fedimint_dummy_client/
states.rs1use 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::{Amount, TransactionId};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::db::DummyClientFundsKeyV1;
11use crate::{DummyClientContext, get_funds};
12
13#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
15pub enum DummyStateMachine {
16 Input(Amount, TransactionId, OperationId),
17 Output(Amount, TransactionId, OperationId),
18 InputDone(OperationId),
19 OutputDone(Amount, TransactionId, OperationId),
20 Refund(OperationId),
21 Unreachable(OperationId, Amount),
22}
23
24impl State for DummyStateMachine {
25 type ModuleContext = DummyClientContext;
26
27 fn transitions(
28 &self,
29 _context: &Self::ModuleContext,
30 global_context: &DynGlobalClientContext,
31 ) -> Vec<StateTransition<Self>> {
32 match self.clone() {
33 DummyStateMachine::Input(amount, txid, id) => vec![StateTransition::new(
34 await_tx_accepted(global_context.clone(), txid),
35 move |dbtx, res, _state: Self| match res {
36 Ok(()) => Box::pin(async move { DummyStateMachine::InputDone(id) }),
38 Err(_) => Box::pin(async move {
40 add_funds(amount, dbtx.module_tx()).await;
41 DummyStateMachine::Refund(id)
42 }),
43 },
44 )],
45 DummyStateMachine::Output(amount, 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 {
50 add_funds(amount, dbtx.module_tx()).await;
51 DummyStateMachine::OutputDone(amount, txid, id)
52 }),
53 Err(_) => Box::pin(async move { DummyStateMachine::Refund(id) }),
55 },
56 )],
57 DummyStateMachine::InputDone(_)
58 | DummyStateMachine::OutputDone(_, _, _)
59 | DummyStateMachine::Refund(_)
60 | DummyStateMachine::Unreachable(_, _) => vec![],
61 }
62 }
63
64 fn operation_id(&self) -> OperationId {
65 match self {
66 DummyStateMachine::Input(_, _, id)
67 | DummyStateMachine::Output(_, _, id)
68 | DummyStateMachine::InputDone(id)
69 | DummyStateMachine::OutputDone(_, _, id)
70 | DummyStateMachine::Refund(id)
71 | DummyStateMachine::Unreachable(id, _) => *id,
72 }
73 }
74}
75
76async fn add_funds(amount: Amount, mut dbtx: DatabaseTransaction<'_>) {
77 let funds = get_funds(&mut dbtx).await + amount;
78 dbtx.insert_entry(&DummyClientFundsKeyV1, &funds).await;
79}
80
81async fn await_tx_accepted(
83 context: DynGlobalClientContext,
84 txid: TransactionId,
85) -> Result<(), String> {
86 context.await_tx_accepted(txid).await
87}
88
89impl IntoDynInstance for DummyStateMachine {
91 type DynType = DynState;
92
93 fn into_dyn(self, instance_id: ModuleInstanceId) -> Self::DynType {
94 DynState::from_typed(instance_id, self)
95 }
96}
97
98#[derive(Error, Debug, Serialize, Deserialize, Encodable, Decodable, Clone, Eq, PartialEq)]
99pub enum DummyError {
100 #[error("Dummy module had an internal error")]
101 DummyInternalError,
102}