mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-19 13:44:55 +01:00
feat(cdk): add melt quote state transition validation (#1188)
Add state machine validation for melt quote transitions to prevent invalid state changes. Includes new error types and validation logic for Unpaid, Pending, Paid, and Failed states.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! State transition rules
|
||||
|
||||
use cashu::State;
|
||||
use cashu::{MeltQuoteState, State};
|
||||
|
||||
/// State transition Error
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
@@ -14,6 +14,12 @@ pub enum Error {
|
||||
/// Invalid transition
|
||||
#[error("Invalid transition: From {0} to {1}")]
|
||||
InvalidTransition(State, State),
|
||||
/// Already paid
|
||||
#[error("Quote already paid")]
|
||||
AlreadyPaid,
|
||||
/// Invalid transition
|
||||
#[error("Invalid melt quote state transition: From {0} to {1}")]
|
||||
InvalidMeltQuoteTransition(MeltQuoteState, MeltQuoteState),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -37,3 +43,41 @@ pub fn check_state_transition(current_state: State, new_state: State) -> Result<
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Check if the melt quote state transition is allowed
|
||||
///
|
||||
/// Valid transitions:
|
||||
/// - Unpaid -> Pending, Failed
|
||||
/// - Pending -> Unpaid, Paid, Failed
|
||||
/// - Paid -> (no transitions allowed)
|
||||
/// - Failed -> Pending
|
||||
pub fn check_melt_quote_state_transition(
|
||||
current_state: MeltQuoteState,
|
||||
new_state: MeltQuoteState,
|
||||
) -> Result<(), Error> {
|
||||
let is_valid_transition = match current_state {
|
||||
MeltQuoteState::Unpaid => {
|
||||
matches!(new_state, MeltQuoteState::Pending | MeltQuoteState::Failed)
|
||||
}
|
||||
MeltQuoteState::Pending => matches!(
|
||||
new_state,
|
||||
MeltQuoteState::Unpaid | MeltQuoteState::Paid | MeltQuoteState::Failed
|
||||
),
|
||||
MeltQuoteState::Failed => {
|
||||
matches!(new_state, MeltQuoteState::Pending | MeltQuoteState::Unpaid)
|
||||
}
|
||||
MeltQuoteState::Paid => false,
|
||||
MeltQuoteState::Unknown => true,
|
||||
};
|
||||
|
||||
if !is_valid_transition {
|
||||
Err(match current_state {
|
||||
MeltQuoteState::Pending => Error::Pending,
|
||||
MeltQuoteState::Paid => Error::AlreadyPaid,
|
||||
_ => Error::InvalidMeltQuoteTransition(current_state, new_state),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ use cdk_common::nut00::ProofsMethods;
|
||||
use cdk_common::payment::PaymentIdentifier;
|
||||
use cdk_common::quote_id::QuoteId;
|
||||
use cdk_common::secret::Secret;
|
||||
use cdk_common::state::check_state_transition;
|
||||
use cdk_common::state::{check_melt_quote_state_transition, check_state_transition};
|
||||
use cdk_common::util::unix_time;
|
||||
use cdk_common::{
|
||||
Amount, BlindSignature, BlindSignatureDleq, BlindedMessage, CurrencyUnit, Id, MeltQuoteState,
|
||||
@@ -1043,6 +1043,8 @@ VALUES (:quote_id, :amount, :timestamp);
|
||||
.transpose()?
|
||||
.ok_or(Error::QuoteNotFound)?;
|
||||
|
||||
check_melt_quote_state_transition(quote.state, state)?;
|
||||
|
||||
let rec = if state == MeltQuoteState::Paid {
|
||||
let current_time = unix_time();
|
||||
query(r#"UPDATE melt_quote SET state = :state, paid_time = :paid_time, payment_preimage = :payment_preimage WHERE id = :id"#)?
|
||||
|
||||
Reference in New Issue
Block a user