state.rs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. //! State transition rules
  2. use cashu::{MeltQuoteState, State};
  3. /// State transition Error
  4. #[derive(thiserror::Error, Debug)]
  5. pub enum Error {
  6. /// Pending Token
  7. #[error("Token already pending for another update")]
  8. Pending,
  9. /// Already spent
  10. #[error("Token already spent")]
  11. AlreadySpent,
  12. /// Invalid transition
  13. #[error("Invalid transition: From {0} to {1}")]
  14. InvalidTransition(State, State),
  15. /// Already paid
  16. #[error("Quote already paid")]
  17. AlreadyPaid,
  18. /// Invalid transition
  19. #[error("Invalid melt quote state transition: From {0} to {1}")]
  20. InvalidMeltQuoteTransition(MeltQuoteState, MeltQuoteState),
  21. }
  22. #[inline]
  23. /// Check if the state transition is allowed
  24. pub fn check_state_transition(current_state: State, new_state: State) -> Result<(), Error> {
  25. let is_valid_transition = match current_state {
  26. State::Unspent => matches!(new_state, State::Pending | State::Spent),
  27. State::Pending => matches!(new_state, State::Unspent | State::Spent),
  28. // Any other state shouldn't be updated by the mint, and the wallet does not use this
  29. // function
  30. _ => false,
  31. };
  32. if !is_valid_transition {
  33. Err(match current_state {
  34. State::Pending => Error::Pending,
  35. State::Spent => Error::AlreadySpent,
  36. _ => Error::InvalidTransition(current_state, new_state),
  37. })
  38. } else {
  39. Ok(())
  40. }
  41. }
  42. #[inline]
  43. /// Check if the melt quote state transition is allowed
  44. ///
  45. /// Valid transitions:
  46. /// - Unpaid -> Pending, Failed
  47. /// - Pending -> Unpaid, Paid, Failed
  48. /// - Paid -> (no transitions allowed)
  49. /// - Failed -> Pending
  50. pub fn check_melt_quote_state_transition(
  51. current_state: MeltQuoteState,
  52. new_state: MeltQuoteState,
  53. ) -> Result<(), Error> {
  54. let is_valid_transition = match current_state {
  55. MeltQuoteState::Unpaid => {
  56. matches!(new_state, MeltQuoteState::Pending | MeltQuoteState::Failed)
  57. }
  58. MeltQuoteState::Pending => matches!(
  59. new_state,
  60. MeltQuoteState::Unpaid | MeltQuoteState::Paid | MeltQuoteState::Failed
  61. ),
  62. MeltQuoteState::Failed => {
  63. matches!(new_state, MeltQuoteState::Pending | MeltQuoteState::Unpaid)
  64. }
  65. MeltQuoteState::Paid => false,
  66. MeltQuoteState::Unknown => true,
  67. };
  68. if !is_valid_transition {
  69. Err(match current_state {
  70. MeltQuoteState::Pending => Error::Pending,
  71. MeltQuoteState::Paid => Error::AlreadyPaid,
  72. _ => Error::InvalidMeltQuoteTransition(current_state, new_state),
  73. })
  74. } else {
  75. Ok(())
  76. }
  77. }