From 989035e12eb4274bee0fa9d3d681410bcbdada46 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sat, 25 Nov 2023 21:06:32 +0000 Subject: [PATCH] refactor: add keyset_id to blinded_message stop mint from signing if keyset in blinded_message is not the active keyset --- bindings/cashu-ffi/src/cashu.udl | 6 +++--- .../src/nuts/nut00/blinded_message.rs | 5 +++-- .../src/nuts/nut00/blinded_messages.rs | 16 +++++++++----- .../src/nuts/nut00/blinded_message.rs | 4 +++- .../src/nuts/nut00/blinded_messages.rs | 11 ++++++---- bindings/cashu-sdk-ffi/src/cashu_sdk.udl | 6 +++--- crates/cashu-sdk/src/mint.rs | 10 ++++++++- crates/cashu-sdk/src/wallet.rs | 10 ++++----- crates/cashu/src/error.rs | 3 +++ crates/cashu/src/nuts/nut00.rs | 21 +++++++++++++------ crates/cashu/src/nuts/nut02.rs | 2 +- 11 files changed, 63 insertions(+), 31 deletions(-) diff --git a/bindings/cashu-ffi/src/cashu.udl b/bindings/cashu-ffi/src/cashu.udl index e28cefb6..7147d9d7 100644 --- a/bindings/cashu-ffi/src/cashu.udl +++ b/bindings/cashu-ffi/src/cashu.udl @@ -50,7 +50,7 @@ interface SecretKey { }; interface BlindedMessage { - constructor(Amount amount, PublicKey b); + constructor(Id keyset_id, Amount amount, PublicKey b); Amount amount(); PublicKey b(); }; @@ -100,9 +100,9 @@ interface Token { interface BlindedMessages { [Throws=CashuError, Name=random] - constructor(Amount amount); + constructor(Id keyset_id, Amount amount); [Throws=CashuError, Name=blank] - constructor(Amount fee_reserve); + constructor(Id keyset_id, Amount amount); sequence blinded_messages(); sequence secrets(); sequence rs(); diff --git a/bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs b/bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs index c5486e25..b183362a 100644 --- a/bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs +++ b/bindings/cashu-ffi/src/nuts/nut00/blinded_message.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use cashu::nuts::BlindedMessage as BlindedMessageSdk; use crate::nuts::nut01::public_key::PublicKey; -use crate::Amount; +use crate::{Amount, Id}; pub struct BlindedMessage { inner: BlindedMessageSdk, @@ -18,9 +18,10 @@ impl Deref for BlindedMessage { } impl BlindedMessage { - pub fn new(amount: Arc, b: Arc) -> Self { + pub fn new(keyset_id: Arc, amount: Arc, b: Arc) -> Self { Self { inner: BlindedMessageSdk { + keyset_id: *keyset_id.as_ref().deref(), amount: *amount.as_ref().deref(), b: b.as_ref().into(), }, diff --git a/bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs b/bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs index a1a8c59c..0a11bb81 100644 --- a/bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs +++ b/bindings/cashu-ffi/src/nuts/nut00/blinded_messages.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use cashu::nuts::nut00::wallet::BlindedMessages as BlindedMessagesSdk; use crate::error::Result; -use crate::{Amount, BlindedMessage, Secret, SecretKey}; +use crate::{Amount, BlindedMessage, Id, Secret, SecretKey}; pub struct BlindedMessages { inner: BlindedMessagesSdk, @@ -18,15 +18,21 @@ impl Deref for BlindedMessages { } impl BlindedMessages { - pub fn random(amount: Arc) -> Result { + pub fn random(keyset_id: Arc, amount: Arc) -> Result { Ok(Self { - inner: BlindedMessagesSdk::random(*amount.as_ref().deref())?, + inner: BlindedMessagesSdk::random( + *keyset_id.as_ref().deref(), + *amount.as_ref().deref(), + )?, }) } - pub fn blank(fee_reserve: Arc) -> Result { + pub fn blank(keyset_id: Arc, fee_reserve: Arc) -> Result { Ok(Self { - inner: BlindedMessagesSdk::blank(*fee_reserve.as_ref().deref())?, + inner: BlindedMessagesSdk::blank( + *keyset_id.as_ref().deref(), + *fee_reserve.as_ref().deref(), + )?, }) } diff --git a/bindings/cashu-js/src/nuts/nut00/blinded_message.rs b/bindings/cashu-js/src/nuts/nut00/blinded_message.rs index c4a16f94..551e6d19 100644 --- a/bindings/cashu-js/src/nuts/nut00/blinded_message.rs +++ b/bindings/cashu-js/src/nuts/nut00/blinded_message.rs @@ -4,6 +4,7 @@ use cashu::nuts::nut00::BlindedMessage; use wasm_bindgen::prelude::*; use crate::nuts::nut01::JsPublicKey; +use crate::nuts::nut02::JsId; use crate::types::amount::JsAmount; #[wasm_bindgen(js_name = BlindedMessage)] @@ -28,9 +29,10 @@ impl From for JsBlindedMessage { impl JsBlindedMessage { #[allow(clippy::new_without_default)] #[wasm_bindgen(constructor)] - pub fn new(amount: JsAmount, b: JsPublicKey) -> Self { + pub fn new(keyset_id: JsId, amount: JsAmount, b: JsPublicKey) -> Self { Self { inner: BlindedMessage { + keyset_id: *keyset_id.deref(), amount: *amount.deref(), b: b.deref().clone(), }, diff --git a/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs b/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs index 5df4619b..b19599c6 100644 --- a/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs +++ b/bindings/cashu-js/src/nuts/nut00/blinded_messages.rs @@ -4,6 +4,7 @@ use cashu::nuts::nut00::wallet::BlindedMessages; use wasm_bindgen::prelude::*; use crate::error::{into_err, Result}; +use crate::nuts::nut02::JsId; use crate::types::JsAmount; #[wasm_bindgen(js_name = BlindedMessages)] @@ -21,16 +22,18 @@ impl Deref for JsBlindedMessages { #[wasm_bindgen(js_class = BlindedMessages)] impl JsBlindedMessages { #[wasm_bindgen(js_name = random)] - pub fn random(amount: JsAmount) -> Result { + pub fn random(keyset_id: JsId, amount: JsAmount) -> Result { Ok(JsBlindedMessages { - inner: BlindedMessages::random(*amount.deref()).map_err(into_err)?, + inner: BlindedMessages::random(*keyset_id.deref(), *amount.deref()) + .map_err(into_err)?, }) } #[wasm_bindgen(js_name = blank)] - pub fn blank(fee_reserve: JsAmount) -> Result { + pub fn blank(keyset_id: JsId, fee_reserve: JsAmount) -> Result { Ok(JsBlindedMessages { - inner: BlindedMessages::blank(*fee_reserve.deref()).map_err(into_err)?, + inner: BlindedMessages::blank(*keyset_id.deref(), *fee_reserve.deref()) + .map_err(into_err)?, }) } diff --git a/bindings/cashu-sdk-ffi/src/cashu_sdk.udl b/bindings/cashu-sdk-ffi/src/cashu_sdk.udl index de596591..ec4ac1db 100644 --- a/bindings/cashu-sdk-ffi/src/cashu_sdk.udl +++ b/bindings/cashu-sdk-ffi/src/cashu_sdk.udl @@ -53,7 +53,7 @@ interface SecretKey { }; interface BlindedMessage { - constructor(Amount amount, PublicKey b); + constructor(Id keyset_id, Amount amount, PublicKey b); Amount amount(); PublicKey b(); }; @@ -103,9 +103,9 @@ interface Token { interface BlindedMessages { [Throws=CashuError, Name=random] - constructor(Amount amount); + constructor(Id keyset_id, Amount amount); [Throws=CashuError, Name=blank] - constructor(Amount fee_reserve); + constructor(Id keyset_id, Amount amount); sequence blinded_messages(); sequence secrets(); sequence rs(); diff --git a/crates/cashu-sdk/src/mint.rs b/crates/cashu-sdk/src/mint.rs index 338bbd4c..ea747e35 100644 --- a/crates/cashu-sdk/src/mint.rs +++ b/crates/cashu-sdk/src/mint.rs @@ -119,7 +119,15 @@ impl Mint { } fn blind_sign(&self, blinded_message: &BlindedMessage) -> Result { - let BlindedMessage { amount, b } = blinded_message; + let BlindedMessage { + amount, + b, + keyset_id, + } = blinded_message; + + if self.active_keyset.id.ne(keyset_id) { + return Err(Error::InactiveKeyset); + } let Some(key_pair) = self.active_keyset.keys.0.get(amount) else { // No key for amount diff --git a/crates/cashu-sdk/src/wallet.rs b/crates/cashu-sdk/src/wallet.rs index 5249b2bd..fedd01f1 100644 --- a/crates/cashu-sdk/src/wallet.rs +++ b/crates/cashu-sdk/src/wallet.rs @@ -98,7 +98,7 @@ impl Wallet { /// Mint Proofs pub async fn mint(&self, amount: Amount, hash: &str) -> Result { - let blinded_messages = BlindedMessages::random(amount)?; + let blinded_messages = BlindedMessages::random((&self.mint_keys).into(), amount)?; let mint_res = self .client @@ -181,17 +181,17 @@ impl Wallet { // amount first blinded messages are created for the amount let blinded_messages = if let Some(amount) = amount { - let mut desired_messages = BlindedMessages::random(amount)?; + let mut desired_messages = BlindedMessages::random((&self.mint_keys).into(), amount)?; let change_amount = proofs.iter().map(|p| p.amount).sum::() - amount; - let change_messages = BlindedMessages::random(change_amount)?; + let change_messages = BlindedMessages::random((&self.mint_keys).into(), change_amount)?; desired_messages.combine(change_messages); desired_messages } else { let value = proofs.iter().map(|p| p.amount).sum(); - BlindedMessages::random(value)? + BlindedMessages::random((&self.mint_keys).into(), value)? }; let split_payload = SplitRequest::new(proofs, blinded_messages.blinded_messages.clone()); @@ -308,7 +308,7 @@ impl Wallet { proofs: Proofs, fee_reserve: Amount, ) -> Result { - let blinded = BlindedMessages::blank(fee_reserve)?; + let blinded = BlindedMessages::blank((&self.mint_keys).into(), fee_reserve)?; let melt_response = self .client .post_melt( diff --git a/crates/cashu/src/error.rs b/crates/cashu/src/error.rs index 7c29daf1..3a709db5 100644 --- a/crates/cashu/src/error.rs +++ b/crates/cashu/src/error.rs @@ -101,6 +101,9 @@ pub mod mint { /// Duplicate Proofs sent in request #[error("Duplicate proofs")] DuplicateProofs, + /// Keyset id not active + #[error("Keyset id is not active")] + InactiveKeyset, #[error("`{0}`")] CustomError(String), } diff --git a/crates/cashu/src/nuts/nut00.rs b/crates/cashu/src/nuts/nut00.rs index c3958ffe..efadca73 100644 --- a/crates/cashu/src/nuts/nut00.rs +++ b/crates/cashu/src/nuts/nut00.rs @@ -16,6 +16,8 @@ pub struct BlindedMessage { /// encrypted secret message (B_) #[serde(rename = "B_")] pub b: PublicKey, + #[serde(rename = "id")] + pub keyset_id: Id, } #[cfg(feature = "wallet")] @@ -30,7 +32,7 @@ pub mod wallet { use super::MintProofs; use crate::dhke::blind_message; use crate::error::wallet; - use crate::nuts::{BlindedMessage, Proofs, SecretKey}; + use crate::nuts::{BlindedMessage, Id, Proofs, SecretKey}; use crate::secret::Secret; use crate::url::UncheckedUrl; use crate::{error, Amount}; @@ -50,14 +52,18 @@ pub mod wallet { impl BlindedMessages { /// Outputs for speceifed amount with random secret - pub fn random(amount: Amount) -> Result { + pub fn random(keyset_id: Id, amount: Amount) -> Result { let mut blinded_messages = BlindedMessages::default(); for amount in amount.split() { let secret = Secret::new(); let (blinded, r) = blind_message(secret.as_bytes(), None)?; - let blinded_message = BlindedMessage { amount, b: blinded }; + let blinded_message = BlindedMessage { + amount, + b: blinded, + keyset_id, + }; blinded_messages.secrets.push(secret); blinded_messages.blinded_messages.push(blinded_message); @@ -69,7 +75,7 @@ pub mod wallet { } /// Blank Outputs used for NUT-08 change - pub fn blank(fee_reserve: Amount) -> Result { + pub fn blank(keyset_id: Id, fee_reserve: Amount) -> Result { let mut blinded_messages = BlindedMessages::default(); let fee_reserve = bitcoin::Amount::from_sat(fee_reserve.to_sat()); @@ -87,6 +93,7 @@ pub mod wallet { let blinded_message = BlindedMessage { amount: Amount::ZERO, b: blinded, + keyset_id, }; blinded_messages.secrets.push(secret); @@ -322,10 +329,12 @@ mod tests { #[test] fn test_blank_blinded_messages() { - let b = BlindedMessages::blank(Amount::from_sat(1000)).unwrap(); + // TODO: Need to update id to new type in proof + let b = BlindedMessages::blank(Id::from_str("").unwrap(), Amount::from_sat(1000)).unwrap(); assert_eq!(b.blinded_messages.len(), 10); - let b = BlindedMessages::blank(Amount::from_sat(1)).unwrap(); + // TODO: Need to update id to new type in proof + let b = BlindedMessages::blank(Id::from_str("").unwrap(), Amount::from_sat(1)).unwrap(); assert_eq!(b.blinded_messages.len(), 1); } } diff --git a/crates/cashu/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs index e61f131c..7abe65e0 100644 --- a/crates/cashu/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -51,7 +51,7 @@ impl std::fmt::Display for Id { f.write_str(&format!( "{}{}", self.version, - String::from_utf8(self.id.to_vec()).map_err(|_| fmt::Error::default())? + String::from_utf8(self.id.to_vec()).map_err(|_| fmt::Error)? )) } }