Skip to main content

fedimint_dummy_client/
output_sm.rs

1use fedimint_client_module::DynGlobalClientContext;
2use fedimint_client_module::sm::{ClientSMDatabaseTransaction, State, StateTransition};
3use fedimint_core::core::OperationId;
4use fedimint_core::db::IDatabaseTransactionOpsCoreTyped;
5use fedimint_core::encoding::{Decodable, Encodable};
6use fedimint_core::module::AmountUnit;
7use fedimint_core::{Amount, OutPoint};
8
9use crate::DummyClientContext;
10use crate::db::DummyClientFundsKey;
11
12/// State machine tracking receiving (outputs).
13///
14/// Balance is NOT added immediately. On acceptance, balance is added.
15#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
16pub struct DummyOutputStateMachine {
17    pub common: DummyOutputSMCommon,
18    pub state: DummyOutputSMState,
19}
20
21#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
22pub struct DummyOutputSMCommon {
23    pub operation_id: OperationId,
24    pub out_point: OutPoint,
25    pub amount: Amount,
26    pub unit: AmountUnit,
27}
28
29#[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
30pub enum DummyOutputSMState {
31    Created,
32    Accepted,
33    Rejected,
34}
35
36impl DummyOutputStateMachine {
37    fn update(&self, state: DummyOutputSMState) -> Self {
38        Self {
39            common: self.common.clone(),
40            state,
41        }
42    }
43}
44
45impl State for DummyOutputStateMachine {
46    type ModuleContext = DummyClientContext;
47
48    fn transitions(
49        &self,
50        context: &Self::ModuleContext,
51        global_context: &DynGlobalClientContext,
52    ) -> Vec<StateTransition<Self>> {
53        match self.state {
54            DummyOutputSMState::Created => {
55                let global = global_context.clone();
56                let txid = self.common.out_point.txid;
57                let balance_update_sender = context.balance_update_sender.clone();
58
59                vec![StateTransition::new(
60                    async move { global.await_tx_accepted(txid).await },
61                    move |dbtx, result, old_state| {
62                        Box::pin(Self::transition_created(
63                            dbtx,
64                            result,
65                            old_state,
66                            balance_update_sender.clone(),
67                        ))
68                    },
69                )]
70            }
71            DummyOutputSMState::Accepted | DummyOutputSMState::Rejected => vec![],
72        }
73    }
74
75    fn operation_id(&self) -> OperationId {
76        self.common.operation_id
77    }
78}
79
80impl DummyOutputStateMachine {
81    async fn transition_created(
82        dbtx: &mut ClientSMDatabaseTransaction<'_, '_>,
83        result: Result<(), String>,
84        old_state: Self,
85        balance_update_sender: tokio::sync::watch::Sender<()>,
86    ) -> Self {
87        if result.is_ok() {
88            let current = dbtx
89                .module_tx()
90                .get_value(&DummyClientFundsKey(old_state.common.unit))
91                .await
92                .unwrap_or(Amount::ZERO);
93
94            dbtx.module_tx()
95                .insert_entry(
96                    &DummyClientFundsKey(old_state.common.unit),
97                    &(current + old_state.common.amount),
98                )
99                .await;
100
101            dbtx.module_tx().on_commit(move || {
102                balance_update_sender.send_replace(());
103            });
104
105            old_state.update(DummyOutputSMState::Accepted)
106        } else {
107            old_state.update(DummyOutputSMState::Rejected)
108        }
109    }
110}