states.rs
  1  use fedimint_client::sm::{DynState, State, StateTransition};
  2  use fedimint_client::DynGlobalClientContext;
  3  use fedimint_core::core::{IntoDynInstance, ModuleInstanceId, OperationId};
  4  use fedimint_core::db::{DatabaseTransaction, IDatabaseTransactionOpsCoreTyped};
  5  use fedimint_core::encoding::{Decodable, Encodable};
  6  use fedimint_core::{Amount, TransactionId};
  7  use serde::{Deserialize, Serialize};
  8  use thiserror::Error;
  9  
 10  use crate::db::DummyClientFundsKeyV1;
 11  use crate::{get_funds, DummyClientContext};
 12  
 13  /// Tracks a transaction
 14  #[derive(Debug, Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
 15  pub 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  
 24  impl 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                      // accepted, we are done
 37                      Ok(()) => Box::pin(async move { DummyStateMachine::InputDone(id) }),
 38                      // tx rejected, we refund ourselves
 39                      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                      // output accepted, add funds
 49                      Ok(()) => Box::pin(async move {
 50                          add_funds(amount, dbtx.module_tx()).await;
 51                          DummyStateMachine::OutputDone(amount, txid, id)
 52                      }),
 53                      // output rejected, do not add funds
 54                      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  
 76  async 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  
 81  // TODO: Boiler-plate, should return OutputOutcome
 82  async fn await_tx_accepted(
 83      context: DynGlobalClientContext,
 84      txid: TransactionId,
 85  ) -> Result<(), String> {
 86      context.await_tx_accepted(txid).await
 87  }
 88  
 89  // TODO: Boiler-plate
 90  impl 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)]
 99  pub enum DummyError {
100      #[error("Dummy module had an internal error")]
101      DummyInternalError,
102  }