diff --git a/bindings/cashu-ffi/src/nuts/nut02/key_set.rs b/bindings/cashu-ffi/src/nuts/nut02/key_set.rs index d179d19e..5b84cfb7 100644 --- a/bindings/cashu-ffi/src/nuts/nut02/key_set.rs +++ b/bindings/cashu-ffi/src/nuts/nut02/key_set.rs @@ -2,7 +2,9 @@ use std::ops::Deref; use std::str::FromStr; use std::sync::Arc; -use cashu::nuts::{Id as IdSdk, KeySet as KeySetSdk, KeysetResponse as KeysetResponseSdk}; +use cashu::nuts::{ + CurrencyUnit, Id as IdSdk, KeySet as KeySetSdk, KeysetResponse as KeysetResponseSdk, +}; use crate::error::Result; use crate::nuts::nut01::keys::Keys; @@ -54,7 +56,7 @@ impl KeySet { Self { inner: KeySetSdk { id: *id.as_ref().deref(), - unit, + unit: CurrencyUnit::from_str(&unit).unwrap(), keys: keys.as_ref().deref().clone(), }, } @@ -65,7 +67,7 @@ impl KeySet { } pub fn unit(&self) -> String { - self.inner.unit.clone() + self.inner.unit.clone().to_string() } pub fn keys(&self) -> Arc { diff --git a/bindings/cashu-ffi/src/nuts/nut02/mint_keyset.rs b/bindings/cashu-ffi/src/nuts/nut02/mint_keyset.rs index fab9286a..c83d5317 100644 --- a/bindings/cashu-ffi/src/nuts/nut02/mint_keyset.rs +++ b/bindings/cashu-ffi/src/nuts/nut02/mint_keyset.rs @@ -1,6 +1,8 @@ use std::ops::Deref; +use std::str::FromStr; use cashu::nuts::nut02::mint::KeySet as KeySetSdk; +use cashu::nuts::CurrencyUnit; pub struct MintKeySet { inner: KeySetSdk, @@ -16,7 +18,12 @@ impl Deref for MintKeySet { impl MintKeySet { pub fn generate(secret: String, unit: String, derivation_path: String, max_order: u8) -> Self { Self { - inner: KeySetSdk::generate(secret, unit, derivation_path, max_order), + inner: KeySetSdk::generate( + secret.as_bytes(), + CurrencyUnit::from_str(&unit).unwrap(), + &derivation_path, + max_order, + ), } } } diff --git a/bindings/cashu-ffi/src/types/keyset_info.rs b/bindings/cashu-ffi/src/types/keyset_info.rs index 5b4b602d..fbcce8f5 100644 --- a/bindings/cashu-ffi/src/types/keyset_info.rs +++ b/bindings/cashu-ffi/src/types/keyset_info.rs @@ -1,7 +1,8 @@ use std::ops::Deref; +use std::str::FromStr; use std::sync::Arc; -use cashu::nuts::KeySetInfo as KeySetInfoSdk; +use cashu::nuts::{CurrencyUnit, KeySetInfo as KeySetInfoSdk}; use crate::Id; @@ -27,7 +28,7 @@ impl KeySetInfo { Self { inner: KeySetInfoSdk { id: *id.as_ref().deref(), - unit, + unit: CurrencyUnit::from_str(&unit).unwrap(), }, } } diff --git a/bindings/cashu-js/src/nuts/nut02/keyset.rs b/bindings/cashu-js/src/nuts/nut02/keyset.rs index 30a25679..77fbe78f 100644 --- a/bindings/cashu-js/src/nuts/nut02/keyset.rs +++ b/bindings/cashu-js/src/nuts/nut02/keyset.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use std::str::FromStr; -use cashu::nuts::{Id, KeySet, KeysResponse, KeysetResponse}; +use cashu::nuts::{CurrencyUnit, Id, KeySet, KeysResponse, KeysetResponse}; use wasm_bindgen::prelude::*; use crate::error::{into_err, Result}; @@ -68,7 +68,7 @@ impl JsKeySet { Self { inner: KeySet { id: *id.deref(), - unit, + unit: CurrencyUnit::from_str(&unit).unwrap(), keys: keys.deref().clone(), }, } diff --git a/bindings/cashu-js/src/nuts/nut02/mint_keyset.rs b/bindings/cashu-js/src/nuts/nut02/mint_keyset.rs index 1cd453c9..19823dfd 100644 --- a/bindings/cashu-js/src/nuts/nut02/mint_keyset.rs +++ b/bindings/cashu-js/src/nuts/nut02/mint_keyset.rs @@ -1,6 +1,8 @@ use std::ops::Deref; +use std::str::FromStr; use cashu::nuts::nut02::mint::KeySet; +use cashu::nuts::CurrencyUnit; use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name = MintKeySet)] @@ -32,7 +34,12 @@ impl JsMintKeySet { max_order: u8, ) -> JsMintKeySet { Self { - inner: KeySet::generate(secret, unit, derivation_path, max_order), + inner: KeySet::generate( + secret.as_bytes(), + CurrencyUnit::from_str(&unit).unwrap(), + &derivation_path, + max_order, + ), } } } diff --git a/bindings/cashu-sdk-ffi/src/mint.rs b/bindings/cashu-sdk-ffi/src/mint.rs index 4740250b..cb0cede0 100644 --- a/bindings/cashu-sdk-ffi/src/mint.rs +++ b/bindings/cashu-sdk-ffi/src/mint.rs @@ -1,4 +1,5 @@ use std::ops::Deref; +use std::str::FromStr; use std::sync::{Arc, RwLock}; use cashu_ffi::{ @@ -7,6 +8,7 @@ use cashu_ffi::{ Secret, SwapRequest, SwapResponse, }; use cashu_sdk::mint::Mint as MintSdk; +use cashu_sdk::Mnemonic; use crate::error::Result; use crate::types::MintKeySetInfo; @@ -35,7 +37,7 @@ impl Mint { Ok(Self { inner: MintSdk::new( - &secret, + Mnemonic::from_str(&secret).unwrap(), keysets, spent_secrets, // TODO: quotes diff --git a/bindings/cashu-sdk-ffi/src/types/keyset_info.rs b/bindings/cashu-sdk-ffi/src/types/keyset_info.rs index 810a992b..27546273 100644 --- a/bindings/cashu-sdk-ffi/src/types/keyset_info.rs +++ b/bindings/cashu-sdk-ffi/src/types/keyset_info.rs @@ -1,7 +1,9 @@ use std::ops::Deref; +use std::str::FromStr; use std::sync::Arc; use cashu_sdk::mint::MintKeySetInfo as MintKeySetInfoSdk; +use cashu_sdk::nuts::CurrencyUnit; use crate::Id; @@ -36,7 +38,7 @@ impl MintKeySetInfo { inner: MintKeySetInfoSdk { id: *id.as_ref().deref(), active, - unit, + unit: CurrencyUnit::from_str(&unit).unwrap(), valid_from, valid_to, derivation_path, diff --git a/bindings/cashu-sdk-js/src/mint.rs b/bindings/cashu-sdk-js/src/mint.rs index dc380fba..9b3145c0 100644 --- a/bindings/cashu-sdk-js/src/mint.rs +++ b/bindings/cashu-sdk-js/src/mint.rs @@ -1,4 +1,5 @@ use std::ops::Deref; +use std::str::FromStr; #[cfg(feature = "nut07")] use cashu_js::nuts::{JsCheckSpendableRequest, JsCheckSpendableResponse}; @@ -9,6 +10,7 @@ use cashu_js::nuts::{ use cashu_js::JsAmount; use cashu_sdk::mint::Mint; use cashu_sdk::nuts::{KeySet, KeysResponse}; +use cashu_sdk::Mnemonic; use wasm_bindgen::prelude::*; use crate::error::{into_err, Result}; @@ -48,7 +50,7 @@ impl JsMint { let quotes = serde_wasm_bindgen::from_value(quotes).map_err(into_err)?; Ok(JsMint { inner: Mint::new( - &secret, + Mnemonic::from_str(&secret).unwrap(), keyset_info, spent_secrets, quotes, diff --git a/crates/cashu-sdk/Cargo.toml b/crates/cashu-sdk/Cargo.toml index e5cd8145..61f2d629 100644 --- a/crates/cashu-sdk/Cargo.toml +++ b/crates/cashu-sdk/Cargo.toml @@ -20,6 +20,7 @@ nut08 = ["cashu/nut08"] [dependencies] +bip39 = "2.0.0" cashu = { path = "../cashu" } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/cashu-sdk/src/lib.rs b/crates/cashu-sdk/src/lib.rs index 5736b265..79083942 100644 --- a/crates/cashu-sdk/src/lib.rs +++ b/crates/cashu-sdk/src/lib.rs @@ -16,6 +16,8 @@ pub mod mint; pub mod utils; #[cfg(feature = "wallet")] pub mod wallet; + +pub use bip39::Mnemonic; pub use cashu::{self, *}; #[cfg(feature = "blocking")] diff --git a/crates/cashu-sdk/src/mint.rs b/crates/cashu-sdk/src/mint.rs index fad6dd62..db9c04e6 100644 --- a/crates/cashu-sdk/src/mint.rs +++ b/crates/cashu-sdk/src/mint.rs @@ -14,12 +14,14 @@ use serde::{Deserialize, Serialize}; use tracing::{debug, info}; use crate::types::MeltQuote; +use crate::Mnemonic; pub struct Mint { // pub pubkey: PublicKey - _secret: String, pub keysets: HashMap, pub keysets_info: HashMap, + // pub pubkey: PublicKey, + mnemonic: Mnemonic, pub spent_secrets: HashSet, pub pending_secrets: HashSet, pub fee_reserve: FeeReserve, @@ -28,7 +30,7 @@ pub struct Mint { impl Mint { pub fn new( - secret: &str, + mnemonic: Mnemonic, keysets_info: HashSet, spent_secrets: HashSet, melt_quotes: Vec, @@ -38,7 +40,7 @@ impl Mint { let mut keysets = HashMap::default(); let mut info = HashMap::default(); - let mut active_units: HashSet = HashSet::default(); + let mut active_units: HashSet = HashSet::default(); let melt_quotes = melt_quotes.into_iter().map(|q| (q.id.clone(), q)).collect(); @@ -50,9 +52,9 @@ impl Mint { } let keyset = nut02::mint::KeySet::generate( - secret, + &mnemonic.to_seed_normalized(""), keyset_info.unit.clone(), - keyset_info.derivation_path.clone(), + &keyset_info.derivation_path.clone(), keyset_info.max_order, ); @@ -62,7 +64,7 @@ impl Mint { } Self { - _secret: secret.to_string(), + mnemonic, keysets, melt_quotes, keysets_info: info, @@ -105,6 +107,21 @@ impl Mint { self.keysets.get(id).map(|ks| ks.clone().into()) } + /// Add current keyset to inactive keysets + /// Generate new keyset + pub fn rotate_keyset(&mut self, unit: CurrencyUnit, derivation_path: &str, max_order: u8) { + // TODO: Set old keyset as inactive + + let new_keyset = MintKeySet::generate( + &self.mnemonic.to_seed_normalized(""), + unit, + derivation_path, + max_order, + ); + + self.keysets.insert(new_keyset.id, new_keyset); + } + pub fn process_mint_request( &mut self, mint_request: nut04::MintBolt11Request, @@ -327,7 +344,7 @@ pub struct FeeReserve { #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct MintKeySetInfo { pub id: Id, - pub unit: String, + pub unit: CurrencyUnit, pub active: bool, pub valid_from: u64, pub valid_to: Option, diff --git a/crates/cashu/src/nuts/nut00.rs b/crates/cashu/src/nuts/nut00.rs index eae1ff9b..2fdb5527 100644 --- a/crates/cashu/src/nuts/nut00.rs +++ b/crates/cashu/src/nuts/nut00.rs @@ -25,7 +25,7 @@ pub struct BlindedMessage { pub b: PublicKey, } -#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, Hash)] #[serde(rename_all = "lowercase")] pub enum CurrencyUnit { #[default] diff --git a/crates/cashu/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs index bd0c493d..78147015 100644 --- a/crates/cashu/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use super::nut01::Keys; +use super::CurrencyUnit; #[derive(Debug, Error, PartialEq)] pub enum Error { @@ -164,7 +165,7 @@ impl KeysetResponse { #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct KeySet { pub id: Id, - pub unit: String, + pub unit: CurrencyUnit, pub keys: Keys, } @@ -181,7 +182,7 @@ impl From for KeySet { #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize)] pub struct KeySetInfo { pub id: Id, - pub unit: String, + pub unit: CurrencyUnit, } impl From for KeySetInfo { @@ -203,20 +204,21 @@ pub mod mint { use super::Id; use crate::nuts::nut01::mint::{KeyPair, Keys}; + use crate::nuts::CurrencyUnit; use crate::Amount; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct KeySet { pub id: Id, - pub unit: String, + pub unit: CurrencyUnit, pub keys: Keys, } impl KeySet { pub fn generate( - secret: impl Into, - unit: impl Into, - derivation_path: impl Into, + secret: &[u8], + unit: CurrencyUnit, + derivation_path: &str, max_order: u8, ) -> Self { // Elliptic curve math context @@ -230,8 +232,8 @@ pub mod mint { // SHA-256 midstate, for quicker hashing let mut engine = Sha256::engine(); - engine.input(secret.into().as_bytes()); - engine.input(derivation_path.into().as_bytes()); + engine.input(secret); + engine.input(derivation_path.as_bytes()); for i in 0..max_order { let amount = Amount::from(2_u64.pow(i as u32)); @@ -249,7 +251,7 @@ pub mod mint { Self { id: (&keys).into(), - unit: unit.into(), + unit, keys, } } diff --git a/crates/cashu/src/types.rs b/crates/cashu/src/types.rs index fbb8cdc5..4010b329 100644 --- a/crates/cashu/src/types.rs +++ b/crates/cashu/src/types.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -use crate::nuts::{CurrencyUnit, Proofs}; +use crate::nuts::{CurrencyUnit, Id, Proofs}; use crate::{Amount, Bolt11Invoice}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -56,3 +56,13 @@ pub struct MeltQuote { pub paid: bool, pub expiry: u64, } + +/// Keyset id +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct KeysetInfo { + pub id: Id, + pub valid_from: u64, + pub valid_to: Option, + pub derivation_path: String, + pub max_order: u8, +}