From 548bbf1b40a133c74fdefb8692de8b7cc67ff939 Mon Sep 17 00:00:00 2001 From: asmo Date: Thu, 5 Jun 2025 13:25:56 +0200 Subject: [PATCH] Secret remove pub properties (#782) * refactor: add getters to Secret and SecretData * refactor: use new getters for Secret and SecretData in wallet receive * refactor: using SecretData constructor --------- Co-authored-by: thesimplekid --- crates/cashu/src/nuts/nut10.rs | 61 +++++++++++++++---- crates/cashu/src/nuts/nut11/mod.rs | 36 +++++++---- crates/cashu/src/nuts/nut14/mod.rs | 10 +-- .../cashu/src/nuts/nut18/payment_request.rs | 8 +-- crates/cashu/src/nuts/nut18/secret.rs | 6 +- crates/cashu/src/secret.rs | 2 +- crates/cdk/src/mint/mod.rs | 2 +- crates/cdk/src/wallet/receive.rs | 14 +++-- 8 files changed, 95 insertions(+), 44 deletions(-) diff --git a/crates/cashu/src/nuts/nut10.rs b/crates/cashu/src/nuts/nut10.rs index 86b1919c..e6de748e 100644 --- a/crates/cashu/src/nuts/nut10.rs +++ b/crates/cashu/src/nuts/nut10.rs @@ -34,21 +34,53 @@ pub enum Kind { #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct SecretData { /// Unique random string - pub nonce: String, + nonce: String, /// Expresses the spending condition specific to each kind - pub data: String, + data: String, /// Additional data committed to and can be used for feature extensions #[serde(skip_serializing_if = "Option::is_none")] - pub tags: Option>>, + tags: Option>>, +} + +impl SecretData { + /// Create new [`SecretData`] + pub fn new(data: S, tags: Option) -> Self + where + S: Into, + V: Into>>, + { + let nonce = crate::secret::Secret::generate().to_string(); + + Self { + nonce, + data: data.into(), + tags: tags.map(|v| v.into()), + } + } + + /// Get the nonce + pub fn nonce(&self) -> &str { + &self.nonce + } + + /// Get the data + pub fn data(&self) -> &str { + &self.data + } + + /// Get the tags + pub fn tags(&self) -> Option<&Vec>> { + self.tags.as_ref() + } } /// NUT10 Secret #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Secret { /// Kind of the spending condition - pub kind: Kind, + kind: Kind, /// Secret Data - pub secret_data: SecretData, + secret_data: SecretData, } impl Secret { @@ -58,16 +90,19 @@ impl Secret { S: Into, V: Into>>, { - let nonce = crate::secret::Secret::generate().to_string(); - - let secret_data = SecretData { - nonce, - data: data.into(), - tags: tags.map(|v| v.into()), - }; - + let secret_data = SecretData::new(data, tags); Self { kind, secret_data } } + + /// Get the kind + pub fn kind(&self) -> Kind { + self.kind + } + + /// Get the secret data + pub fn secret_data(&self) -> &SecretData { + &self.secret_data + } } impl Serialize for Secret { diff --git a/crates/cashu/src/nuts/nut11/mod.rs b/crates/cashu/src/nuts/nut11/mod.rs index e3ad7c4a..bc83cde7 100644 --- a/crates/cashu/src/nuts/nut11/mod.rs +++ b/crates/cashu/src/nuts/nut11/mod.rs @@ -127,8 +127,12 @@ impl Proof { /// Verify P2PK signature on [Proof] pub fn verify_p2pk(&self) -> Result<(), Error> { let secret: Nut10Secret = self.secret.clone().try_into()?; - let spending_conditions: Conditions = - secret.secret_data.tags.unwrap_or_default().try_into()?; + let spending_conditions: Conditions = secret + .secret_data() + .tags() + .cloned() + .unwrap_or_default() + .try_into()?; let msg: &[u8] = self.secret.as_bytes(); let mut verified_pubkeys = HashSet::new(); @@ -142,8 +146,8 @@ impl Proof { let mut pubkeys = spending_conditions.pubkeys.clone().unwrap_or_default(); - if secret.kind.eq(&Kind::P2PK) { - pubkeys.push(PublicKey::from_str(&secret.secret_data.data)?); + if secret.kind().eq(&Kind::P2PK) { + pubkeys.push(PublicKey::from_str(secret.secret_data().data())?); } for signature in witness_signatures.iter() { @@ -394,15 +398,21 @@ impl TryFrom<&Secret> for SpendingConditions { impl TryFrom for SpendingConditions { type Error = Error; fn try_from(secret: Nut10Secret) -> Result { - match secret.kind { + match secret.kind() { Kind::P2PK => Ok(SpendingConditions::P2PKConditions { - data: PublicKey::from_str(&secret.secret_data.data)?, - conditions: secret.secret_data.tags.and_then(|t| t.try_into().ok()), + data: PublicKey::from_str(secret.secret_data().data())?, + conditions: secret + .secret_data() + .tags() + .and_then(|t| t.clone().try_into().ok()), }), Kind::HTLC => Ok(Self::HTLCConditions { - data: Sha256Hash::from_str(&secret.secret_data.data) + data: Sha256Hash::from_str(secret.secret_data().data()) .map_err(|_| Error::InvalidHash)?, - conditions: secret.secret_data.tags.and_then(|t| t.try_into().ok()), + conditions: secret + .secret_data() + .tags() + .and_then(|t| t.clone().try_into().ok()), }), } } @@ -650,14 +660,14 @@ pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag { let mut sigs_required = 1; for proof in proofs { if let Ok(secret) = Nut10Secret::try_from(proof.secret) { - if secret.kind.eq(&Kind::P2PK) { - if let Ok(verifying_key) = PublicKey::from_str(&secret.secret_data.data) { + if secret.kind().eq(&Kind::P2PK) { + if let Ok(verifying_key) = PublicKey::from_str(secret.secret_data().data()) { pubkeys.insert(verifying_key); } } - if let Some(tags) = secret.secret_data.tags { - if let Ok(conditions) = Conditions::try_from(tags) { + if let Some(tags) = secret.secret_data().tags() { + if let Ok(conditions) = Conditions::try_from(tags.clone()) { if conditions.sig_flag.eq(&SigFlag::SigAll) { sig_flag = SigFlag::SigAll; } diff --git a/crates/cashu/src/nuts/nut14/mod.rs b/crates/cashu/src/nuts/nut14/mod.rs index 725fbb32..fd52d902 100644 --- a/crates/cashu/src/nuts/nut14/mod.rs +++ b/crates/cashu/src/nuts/nut14/mod.rs @@ -66,8 +66,10 @@ impl Proof { /// Verify HTLC pub fn verify_htlc(&self) -> Result<(), Error> { let secret: Secret = self.secret.clone().try_into()?; - let conditions: Option = - secret.secret_data.tags.and_then(|c| c.try_into().ok()); + let conditions: Option = secret + .secret_data() + .tags() + .and_then(|c| c.clone().try_into().ok()); let htlc_witness = match &self.witness { Some(Witness::HTLCWitness(witness)) => witness, @@ -118,12 +120,12 @@ impl Proof { } } - if secret.kind.ne(&super::Kind::HTLC) { + if secret.kind().ne(&super::Kind::HTLC) { return Err(Error::IncorrectSecretKind); } let hash_lock = - Sha256Hash::from_str(&secret.secret_data.data).map_err(|_| Error::InvalidHash)?; + Sha256Hash::from_str(secret.secret_data().data()).map_err(|_| Error::InvalidHash)?; let preimage_hash = Sha256Hash::hash(htlc_witness.preimage.as_bytes()); diff --git a/crates/cashu/src/nuts/nut18/payment_request.rs b/crates/cashu/src/nuts/nut18/payment_request.rs index 17988029..8c690efe 100644 --- a/crates/cashu/src/nuts/nut18/payment_request.rs +++ b/crates/cashu/src/nuts/nut18/payment_request.rs @@ -343,14 +343,14 @@ mod tests { let full_secret: crate::nuts::Nut10Secret = secret_request.clone().into(); // Check conversion - assert_eq!(full_secret.kind, Kind::P2PK); + assert_eq!(full_secret.kind(), Kind::P2PK); assert_eq!( - full_secret.secret_data.data, + full_secret.secret_data().data(), "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198" ); assert_eq!( - full_secret.secret_data.tags, - Some(vec![vec!["key".to_string(), "value".to_string()]]) + full_secret.secret_data().tags().clone(), + Some(vec![vec!["key".to_string(), "value".to_string()]]).as_ref() ); // Convert back to Nut10SecretRequest diff --git a/crates/cashu/src/nuts/nut18/secret.rs b/crates/cashu/src/nuts/nut18/secret.rs index 0ae6b604..24b16f16 100644 --- a/crates/cashu/src/nuts/nut18/secret.rs +++ b/crates/cashu/src/nuts/nut18/secret.rs @@ -47,12 +47,12 @@ impl Nut10SecretRequest { impl From for Nut10SecretRequest { fn from(secret: Nut10Secret) -> Self { let secret_data = SecretDataRequest { - data: secret.secret_data.data, - tags: secret.secret_data.tags, + data: secret.secret_data().data().to_string(), + tags: secret.secret_data().tags().cloned(), }; Self { - kind: secret.kind, + kind: secret.kind(), secret_data, } } diff --git a/crates/cashu/src/secret.rs b/crates/cashu/src/secret.rs index ef388393..08198d19 100644 --- a/crates/cashu/src/secret.rs +++ b/crates/cashu/src/secret.rs @@ -78,7 +78,7 @@ impl Secret { serde_json::from_str(&self.0); if let Ok(secret) = secret { - if secret.kind.eq(&Kind::P2PK) { + if secret.kind().eq(&Kind::P2PK) { return true; } } diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 5b7c8580..f87a30ca 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -387,7 +387,7 @@ impl Mint { // only supported secret kinds are used as there is no way for the mint to // enforce only signing supported secrets as they are blinded at // that point. - match secret.kind { + match secret.kind() { Kind::P2PK => { proof.verify_p2pk()?; } diff --git a/crates/cdk/src/wallet/receive.rs b/crates/cdk/src/wallet/receive.rs index 0461fe98..7a26720e 100644 --- a/crates/cdk/src/wallet/receive.rs +++ b/crates/cdk/src/wallet/receive.rs @@ -80,19 +80,23 @@ impl Wallet { proof.secret.clone(), ) { - let conditions: Result = - secret.secret_data.tags.unwrap_or_default().try_into(); + let conditions: Result = secret + .secret_data() + .tags() + .cloned() + .unwrap_or_default() + .try_into(); if let Ok(conditions) = conditions { let mut pubkeys = conditions.pubkeys.unwrap_or_default(); - match secret.kind { + match secret.kind() { Kind::P2PK => { - let data_key = PublicKey::from_str(&secret.secret_data.data)?; + let data_key = PublicKey::from_str(secret.secret_data().data())?; pubkeys.push(data_key); } Kind::HTLC => { - let hashed_preimage = &secret.secret_data.data; + let hashed_preimage = secret.secret_data().data(); let preimage = hashed_to_preimage .get(hashed_preimage) .ok_or(Error::PreimageNotProvided)?;