cashu/nuts/
nut07.rs

1//! NUT-07: Spendable Check
2//!
3//! <https://github.com/cashubtc/nuts/blob/main/07.md>
4
5use std::fmt;
6use std::str::FromStr;
7
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11use super::nut01::PublicKey;
12use super::Witness;
13
14/// NUT07 Error
15#[derive(Debug, Error, PartialEq, Eq)]
16pub enum Error {
17    /// Unknown State error
18    #[error("Unknown state")]
19    UnknownState,
20}
21
22/// State of Proof
23#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "UPPERCASE")]
25#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
26pub enum State {
27    /// Spent
28    Spent,
29    /// Unspent
30    Unspent,
31    /// Pending
32    ///
33    /// Currently being used in a transaction i.e. melt in progress
34    Pending,
35    /// Reserved
36    ///
37    /// Proof is reserved for future token creation
38    Reserved,
39    /// Pending spent (i.e., spent but not yet swapped by receiver)
40    PendingSpent,
41}
42
43impl fmt::Display for State {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        let s = match self {
46            Self::Spent => "SPENT",
47            Self::Unspent => "UNSPENT",
48            Self::Pending => "PENDING",
49            Self::Reserved => "RESERVED",
50            Self::PendingSpent => "PENDING_SPENT",
51        };
52
53        write!(f, "{s}")
54    }
55}
56
57impl FromStr for State {
58    type Err = Error;
59
60    fn from_str(state: &str) -> Result<Self, Self::Err> {
61        match state {
62            "SPENT" => Ok(Self::Spent),
63            "UNSPENT" => Ok(Self::Unspent),
64            "PENDING" => Ok(Self::Pending),
65            "RESERVED" => Ok(Self::Reserved),
66            "PENDING_SPENT" => Ok(Self::PendingSpent),
67            _ => Err(Error::UnknownState),
68        }
69    }
70}
71
72/// Check spendable request [NUT-07]
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
75pub struct CheckStateRequest {
76    /// Y's of the proofs to check
77    #[serde(rename = "Ys")]
78    #[cfg_attr(feature = "swagger", schema(value_type = Vec<String>, max_items = 1_000))]
79    pub ys: Vec<PublicKey>,
80}
81
82/// Proof state [NUT-07]
83#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
84#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
85pub struct ProofState {
86    /// Y of proof
87    #[serde(rename = "Y")]
88    #[cfg_attr(feature = "swagger", schema(value_type = String))]
89    pub y: PublicKey,
90    /// State of proof
91    pub state: State,
92    /// Witness data if it is supplied
93    pub witness: Option<Witness>,
94}
95
96impl From<(PublicKey, State)> for ProofState {
97    fn from(value: (PublicKey, State)) -> Self {
98        Self {
99            y: value.0,
100            state: value.1,
101            witness: None,
102        }
103    }
104}
105
106/// Check Spendable Response [NUT-07]
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
109pub struct CheckStateResponse {
110    /// Proof states
111    pub states: Vec<ProofState>,
112}