cashu/nuts/
nut10.rs

1//! NUT-10: Spending conditions
2//!
3//! <https://github.com/cashubtc/nuts/blob/main/10.md>
4
5use std::str::FromStr;
6
7use serde::ser::SerializeTuple;
8use serde::{Deserialize, Serialize, Serializer};
9use thiserror::Error;
10
11/// NUT13 Error
12#[derive(Debug, Error)]
13pub enum Error {
14    /// Secret error
15    #[error(transparent)]
16    Secret(#[from] crate::secret::Error),
17    /// Serde Json error
18    #[error(transparent)]
19    SerdeJsonError(#[from] serde_json::Error),
20}
21
22///  NUT10 Secret Kind
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24pub enum Kind {
25    /// NUT-11 P2PK
26    P2PK,
27    /// NUT-14 HTLC
28    HTLC,
29}
30
31/// Secert Date
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
33pub struct SecretData {
34    /// Unique random string
35    pub nonce: String,
36    /// Expresses the spending condition specific to each kind
37    pub data: String,
38    /// Additional data committed to and can be used for feature extensions
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub tags: Option<Vec<Vec<String>>>,
41}
42
43/// NUT10 Secret
44#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize)]
45pub struct Secret {
46    ///  Kind of the spending condition
47    pub kind: Kind,
48    /// Secret Data
49    pub secret_data: SecretData,
50}
51
52impl Secret {
53    /// Create new [`Secret`]
54    pub fn new<S, V>(kind: Kind, data: S, tags: Option<V>) -> Self
55    where
56        S: Into<String>,
57        V: Into<Vec<Vec<String>>>,
58    {
59        let nonce = crate::secret::Secret::generate().to_string();
60
61        let secret_data = SecretData {
62            nonce,
63            data: data.into(),
64            tags: tags.map(|v| v.into()),
65        };
66
67        Self { kind, secret_data }
68    }
69}
70
71impl Serialize for Secret {
72    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73    where
74        S: Serializer,
75    {
76        // Create a tuple representing the struct fields
77        let secret_tuple = (&self.kind, &self.secret_data);
78
79        // Serialize the tuple as a JSON array
80        let mut s = serializer.serialize_tuple(2)?;
81
82        s.serialize_element(&secret_tuple.0)?;
83        s.serialize_element(&secret_tuple.1)?;
84        s.end()
85    }
86}
87
88impl TryFrom<Secret> for crate::secret::Secret {
89    type Error = Error;
90    fn try_from(secret: Secret) -> Result<crate::secret::Secret, Self::Error> {
91        Ok(crate::secret::Secret::from_str(&serde_json::to_string(
92            &secret,
93        )?)?)
94    }
95}
96
97#[cfg(test)]
98mod tests {
99    use std::assert_eq;
100
101    use super::*;
102
103    #[test]
104    fn test_secret_serialize() {
105        let secret = Secret {
106            kind: Kind::P2PK,
107            secret_data: SecretData {
108                nonce: "5d11913ee0f92fefdc82a6764fd2457a".to_string(),
109                data: "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198"
110                    .to_string(),
111                tags: Some(vec![vec![
112                    "key".to_string(),
113                    "value1".to_string(),
114                    "value2".to_string(),
115                ]]),
116            },
117        };
118
119        let secret_str = r#"["P2PK",{"nonce":"5d11913ee0f92fefdc82a6764fd2457a","data":"026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198","tags":[["key","value1","value2"]]}]"#;
120
121        assert_eq!(serde_json::to_string(&secret).unwrap(), secret_str);
122    }
123}