diff --git a/bindings/cashu-ffi/src/cashu.udl b/bindings/cashu-ffi/src/cashu.udl index 2172bff1..90303398 100644 --- a/bindings/cashu-ffi/src/cashu.udl +++ b/bindings/cashu-ffi/src/cashu.udl @@ -241,6 +241,11 @@ interface MintInfo { string? motd(); }; +interface KeySetInfo { + constructor(Id id, u64 valid_from, u64? valid_to, string secret, string derivation_path, u8 max_order); + +}; + enum InvoiceStatus { "Unpaid", "Paid", diff --git a/bindings/cashu-ffi/src/lib.rs b/bindings/cashu-ffi/src/lib.rs index e9a0adfb..040f3017 100644 --- a/bindings/cashu-ffi/src/lib.rs +++ b/bindings/cashu-ffi/src/lib.rs @@ -27,6 +27,7 @@ mod ffi { pub use crate::nuts::nut09::{MintInfo, MintVersion}; pub use crate::types::amount::Amount; pub use crate::types::Bolt11Invoice; + pub use crate::types::KeySetInfo; pub use crate::types::Secret; pub use cashu::types::InvoiceStatus; diff --git a/bindings/cashu-ffi/src/types/keyset_info.rs b/bindings/cashu-ffi/src/types/keyset_info.rs new file mode 100644 index 00000000..677dfd94 --- /dev/null +++ b/bindings/cashu-ffi/src/types/keyset_info.rs @@ -0,0 +1,44 @@ +use std::{ops::Deref, sync::Arc}; + +use cashu::types::KeysetInfo as KeySetInfoSdk; + +use crate::Id; + +pub struct KeySetInfo { + inner: KeySetInfoSdk, +} + +impl Deref for KeySetInfo { + type Target = KeySetInfoSdk; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for KeySetInfo { + fn from(inner: KeySetInfoSdk) -> KeySetInfo { + KeySetInfo { inner } + } +} + +impl KeySetInfo { + pub fn new( + id: Arc, + valid_from: u64, + valid_to: Option, + secret: String, + derivation_path: String, + max_order: u8, + ) -> Self { + Self { + inner: KeySetInfoSdk { + id: *id.as_ref().deref(), + valid_from, + valid_to, + secret, + derivation_path, + max_order, + }, + } + } +} diff --git a/bindings/cashu-ffi/src/types/mod.rs b/bindings/cashu-ffi/src/types/mod.rs index 27a825a3..475c4307 100644 --- a/bindings/cashu-ffi/src/types/mod.rs +++ b/bindings/cashu-ffi/src/types/mod.rs @@ -1,6 +1,10 @@ pub mod amount; pub mod bolt11_invoice; +pub mod keyset_info; +pub mod proofs_status; pub mod secret; pub use bolt11_invoice::Bolt11Invoice; +pub use keyset_info::KeySetInfo; +pub use proofs_status::ProofsStatus; pub use secret::Secret; diff --git a/bindings/cashu-js/src/lib.rs b/bindings/cashu-js/src/lib.rs index e5ab5c44..aa3c3f35 100644 --- a/bindings/cashu-js/src/lib.rs +++ b/bindings/cashu-js/src/lib.rs @@ -1,3 +1,3 @@ pub mod error; -mod nuts; +pub mod nuts; pub mod types; diff --git a/bindings/cashu-js/src/nuts/mod.rs b/bindings/cashu-js/src/nuts/mod.rs index 802e1042..72608a54 100644 --- a/bindings/cashu-js/src/nuts/mod.rs +++ b/bindings/cashu-js/src/nuts/mod.rs @@ -1,10 +1,10 @@ -mod nut00; -mod nut01; -mod nut02; -mod nut03; -mod nut04; -mod nut05; -mod nut06; -mod nut07; -mod nut08; -mod nut09; +pub mod nut00; +pub mod nut01; +pub mod nut02; +pub mod nut03; +pub mod nut04; +pub mod nut05; +pub mod nut06; +pub mod nut07; +pub mod nut08; +pub mod nut09; diff --git a/bindings/cashu-js/src/nuts/nut02/keyset.rs b/bindings/cashu-js/src/nuts/nut02/keyset.rs index 362a032f..ceed62c4 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 cashu::nuts::nut02::Id; -use cashu::nuts::nut02::{KeySet, Response}; +use cashu::nuts::nut01::Response as KeysResponse; +use cashu::nuts::nut02::{Id, KeySet, Response as KeySetsResponse}; use wasm_bindgen::prelude::*; use crate::{ @@ -86,36 +86,68 @@ impl JsKeySet { } } -#[wasm_bindgen(js_name = KeySetResponse)] -pub struct JsKeyResponse { - inner: Response, +#[wasm_bindgen(js_name = KeySetsResponse)] +pub struct JsKeySetsResponse { + inner: KeySetsResponse, } -impl Deref for JsKeyResponse { - type Target = Response; +impl Deref for JsKeySetsResponse { + type Target = KeySetsResponse; fn deref(&self) -> &Self::Target { &self.inner } } -impl From for JsKeyResponse { - fn from(inner: Response) -> JsKeyResponse { - JsKeyResponse { inner } +impl From for JsKeySetsResponse { + fn from(inner: KeySetsResponse) -> JsKeySetsResponse { + JsKeySetsResponse { inner } } } -#[wasm_bindgen(js_class = KeyResponse)] -impl JsKeyResponse { - /// From Hex +#[wasm_bindgen(js_class = KeySetsResponse)] +impl JsKeySetsResponse { #[wasm_bindgen(constructor)] - pub fn new(keysets: JsValue) -> Result { + pub fn new(keysets: JsValue) -> Result { let response = serde_wasm_bindgen::from_value(keysets).map_err(into_err)?; Ok(Self { inner: response }) } - /// Get Keysets + /// Get KeySets #[wasm_bindgen(getter)] - pub fn keysets(&self) -> Result { + pub fn keys(&self) -> Result { serde_wasm_bindgen::to_value(&self.inner.keysets).map_err(into_err) } } + +#[wasm_bindgen(js_name = KeysResponse)] +pub struct JsKeysResponse { + inner: KeysResponse, +} + +impl Deref for JsKeysResponse { + type Target = KeysResponse; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsKeysResponse { + fn from(inner: KeysResponse) -> JsKeysResponse { + JsKeysResponse { inner } + } +} + +#[wasm_bindgen(js_class = KeysResponse)] +impl JsKeysResponse { + #[wasm_bindgen(constructor)] + pub fn new(keysets: JsValue) -> Result { + let response = serde_wasm_bindgen::from_value(keysets).map_err(into_err)?; + Ok(Self { inner: response }) + } + + /// Get Keys + #[wasm_bindgen(getter)] + pub fn keys(&self) -> Result { + serde_wasm_bindgen::to_value(&self.inner.keys).map_err(into_err) + } +} diff --git a/bindings/cashu-js/src/nuts/nut02/mod.rs b/bindings/cashu-js/src/nuts/nut02/mod.rs index 3384c97f..8682b32d 100644 --- a/bindings/cashu-js/src/nuts/nut02/mod.rs +++ b/bindings/cashu-js/src/nuts/nut02/mod.rs @@ -3,4 +3,6 @@ mod mint_keyset; pub use keyset::JsId; pub use keyset::JsKeySet; +pub use keyset::JsKeySetsResponse; +pub use keyset::JsKeysResponse; pub use mint_keyset::JsMintKeySet; diff --git a/bindings/cashu-js/src/nuts/nut09.rs b/bindings/cashu-js/src/nuts/nut09.rs index 72a971ce..52014944 100644 --- a/bindings/cashu-js/src/nuts/nut09.rs +++ b/bindings/cashu-js/src/nuts/nut09.rs @@ -40,7 +40,7 @@ impl JsMintVersion { self.inner.name.clone() } - /// Get Name + /// Get Version #[wasm_bindgen(getter)] pub fn version(&self) -> String { self.inner.version.clone() @@ -105,7 +105,7 @@ impl JsMintInfo { self.inner.pubkey.clone().map(|p| p.into()) } - /// Get Name + /// Get Version #[wasm_bindgen(getter)] pub fn version(&self) -> Option { self.inner.version.clone().map(|v| v.into()) diff --git a/bindings/cashu-sdk-ffi/src/cashu_sdk.udl b/bindings/cashu-sdk-ffi/src/cashu_sdk.udl index a705d487..d7203c6e 100644 --- a/bindings/cashu-sdk-ffi/src/cashu_sdk.udl +++ b/bindings/cashu-sdk-ffi/src/cashu_sdk.udl @@ -259,6 +259,12 @@ interface ProofsStatus { sequence spent(); }; + +interface KeySetInfo { + constructor(Id id, u64 valid_from, u64? valid_to, string secret, string derivation_path, u8 max_order); + +}; + // Cashu Sdk @@ -330,7 +336,7 @@ interface Wallet { interface Mint { [Throws=CashuSdkError] - constructor(string secret, string derivation_path, record inactive_keysets, sequence spent_secrets, u8 max_order, Amount min_fee_reserve, f32 percent_fee_reserve); + constructor(string secret, string derivation_path, sequence inactive_keysets, sequence spent_secrets, u8 max_order, Amount min_fee_reserve, f32 percent_fee_reserve); KeysResponse active_keyset_pubkeys(); KeySetResponse keysets(); MintKeySet active_keyset(); diff --git a/bindings/cashu-sdk-ffi/src/lib.rs b/bindings/cashu-sdk-ffi/src/lib.rs index f2d8fce4..ba5804ff 100644 --- a/bindings/cashu-sdk-ffi/src/lib.rs +++ b/bindings/cashu-sdk-ffi/src/lib.rs @@ -8,9 +8,9 @@ mod ffi { pub use cashu_ffi::{ Amount, BlindedMessage, BlindedMessages, BlindedSignature, Bolt11Invoice, CashuError, CheckFeesRequest, CheckFeesResponse, CheckSpendableRequest, CheckSpendableResponse, Id, - InvoiceStatus, KeyPair, KeySet, KeySetResponse, Keys, KeysResponse, MeltRequest, - MeltResponse, MintInfo, MintKeySet, MintProof, MintProofs, MintRequest, MintVersion, - Nut05MeltRequest, Nut05MeltResponse, PostMintResponse, Proof, PublicKey, + InvoiceStatus, KeyPair, KeySet, KeySetInfo, KeySetResponse, Keys, KeysResponse, + MeltRequest, MeltResponse, MintInfo, MintKeySet, MintProof, MintProofs, MintRequest, + MintVersion, Nut05MeltRequest, Nut05MeltResponse, PostMintResponse, Proof, PublicKey, RequestMintResponse, Secret, SecretKey, SplitRequest, SplitResponse, Token, }; diff --git a/bindings/cashu-sdk-ffi/src/mint.rs b/bindings/cashu-sdk-ffi/src/mint.rs index e79ebae1..368d38c5 100644 --- a/bindings/cashu-sdk-ffi/src/mint.rs +++ b/bindings/cashu-sdk-ffi/src/mint.rs @@ -1,16 +1,14 @@ use std::{ - collections::HashMap, ops::Deref, sync::{Arc, RwLock}, }; use cashu_ffi::{ - Amount, CheckSpendableRequest, CheckSpendableResponse, Id, KeySet, KeySetResponse, + Amount, CheckSpendableRequest, CheckSpendableResponse, Id, KeySet, KeySetInfo, KeySetResponse, KeysResponse, MeltRequest, MeltResponse, MintKeySet, MintRequest, PostMintResponse, Secret, SplitRequest, SplitResponse, }; use cashu_sdk::mint::Mint as MintSdk; -use cashu_sdk::nuts::nut02::Id as IdSdk; use crate::error::Result; @@ -22,7 +20,7 @@ impl Mint { pub fn new( secret: String, derivation_path: String, - inactive_keysets: HashMap>, + inactive_keysets: Vec>, spent_secrets: Vec>, max_order: u8, min_fee_reserve: Arc, @@ -35,10 +33,7 @@ impl Mint { let inactive_keysets = inactive_keysets .into_iter() - .flat_map(|(k, v)| { - let id = IdSdk::try_from_base64(&k); - id.map(|id| (id, v.as_ref().deref().clone())) - }) + .map(|ik| ik.as_ref().deref().clone()) .collect(); Ok(Self { diff --git a/bindings/cashu-sdk-js/src/lib.rs b/bindings/cashu-sdk-js/src/lib.rs index 8800f21b..13b147b6 100644 --- a/bindings/cashu-sdk-js/src/lib.rs +++ b/bindings/cashu-sdk-js/src/lib.rs @@ -1,2 +1,3 @@ mod error; +mod mint; mod types; diff --git a/bindings/cashu-sdk-js/src/mint.rs b/bindings/cashu-sdk-js/src/mint.rs new file mode 100644 index 00000000..00fd6112 --- /dev/null +++ b/bindings/cashu-sdk-js/src/mint.rs @@ -0,0 +1,98 @@ +use std::ops::Deref; + +use cashu_js::{ + nuts::{ + nut02::{JsId, JsKeySet, JsKeySetsResponse, JsKeysResponse, JsMintKeySet}, + nut04::{JsMintRequest, JsPostMintResponse}, + }, + types::JsAmount, +}; +use cashu_sdk::{mint::Mint, nuts::nut01, nuts::nut02::KeySet}; +use wasm_bindgen::prelude::*; + +use crate::error::{into_err, Result}; + +#[wasm_bindgen(js_name = Mint)] +pub struct JsMint { + inner: Mint, +} + +impl Deref for JsMint { + type Target = Mint; + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl From for JsMint { + fn from(inner: Mint) -> JsMint { + JsMint { inner } + } +} + +#[wasm_bindgen(js_class = Mint)] +impl JsMint { + #[wasm_bindgen(constructor)] + pub fn new( + secret: String, + derivation_path: String, + inactive_keyset: JsValue, + spent_secrets: JsValue, + max_order: u8, + min_fee_reserve: JsAmount, + percent_fee_reserve: f32, + ) -> Result { + let inactive_keyset = serde_wasm_bindgen::from_value(inactive_keyset).map_err(into_err)?; + let spent_secrets = serde_wasm_bindgen::from_value(spent_secrets).map_err(into_err)?; + Ok(JsMint { + inner: Mint::new( + &secret, + &derivation_path, + inactive_keyset, + spent_secrets, + max_order, + *min_fee_reserve.deref(), + percent_fee_reserve, + ), + }) + } + + /// Get Active Keyset Pubkeys + #[wasm_bindgen(getter)] + pub fn active_keyset_pubkeys(&self) -> Result { + let keyset: KeySet = self.inner.active_keyset.clone().into(); + + Ok(nut01::Response { keys: keyset.keys }.into()) + } + + /// Get Keysets + #[wasm_bindgen(js_name = KeySets)] + pub fn keysets(&self) -> JsKeySetsResponse { + self.inner.keysets().into() + } + + /// Get Active Keyset + #[wasm_bindgen(getter)] + pub fn active_keyset(&self) -> JsMintKeySet { + self.inner.active_keyset.clone().into() + } + + /// Keyset + #[wasm_bindgen(js_name = KeySet)] + pub fn keyset(&self, id: JsId) -> Option { + self.inner.keyset(id.deref()).map(|ks| ks.into()) + } + + /// Process Mint Request + #[wasm_bindgen(js_name = ProcessMintRequest)] + pub fn process_mint_request( + &mut self, + mint_request: JsMintRequest, + ) -> Result { + Ok(self + .inner + .process_mint_request(mint_request.deref().clone()) + .map_err(into_err)? + .into()) + } +} diff --git a/crates/cashu-sdk/src/mint.rs b/crates/cashu-sdk/src/mint.rs index 57975cc1..989a3eac 100644 --- a/crates/cashu-sdk/src/mint.rs +++ b/crates/cashu-sdk/src/mint.rs @@ -16,13 +16,15 @@ use cashu::nuts::nut08::MeltRequest; use cashu::nuts::nut08::MeltResponse; use cashu::nuts::*; use cashu::secret::Secret; +use cashu::types::KeysetInfo; use cashu::Amount; use tracing::debug; pub struct Mint { // pub pubkey: PublicKey, pub active_keyset: nut02::mint::KeySet, - pub inactive_keysets: HashMap, + pub active_keyset_info: KeysetInfo, + pub inactive_keysets: HashMap, pub spent_secrets: HashSet, pub pending_secrets: HashSet, pub fee_reserve: FeeReserve, @@ -32,15 +34,26 @@ impl Mint { pub fn new( secret: &str, derivation_path: &str, - inactive_keysets: HashMap, + inactive_keysets: HashSet, spent_secrets: HashSet, max_order: u8, min_fee_reserve: Amount, percent_fee_reserve: f32, ) -> Self { + let active_keyset = nut02::mint::KeySet::generate(secret, derivation_path, max_order); + let id = active_keyset.id; + Self { - active_keyset: nut02::mint::KeySet::generate(secret, derivation_path, max_order), - inactive_keysets, + active_keyset, + inactive_keysets: inactive_keysets.into_iter().map(|ks| (ks.id, ks)).collect(), + active_keyset_info: KeysetInfo { + id, + valid_from: 0, + valid_to: None, + secret: secret.to_string(), + derivation_path: derivation_path.to_string(), + max_order, + }, spent_secrets, pending_secrets: HashSet::new(), fee_reserve: FeeReserve { @@ -74,7 +87,9 @@ impl Mint { return Some(self.active_keyset.clone().into()); } - self.inactive_keysets.get(id).map(|k| k.clone().into()) + self.inactive_keysets.get(id).map(|k| { + nut02::mint::KeySet::generate(&k.secret, &k.derivation_path, k.max_order).into() + }) } /// Add current keyset to inactive keysets @@ -87,7 +102,7 @@ impl Mint { ) { // Add current set to inactive keysets self.inactive_keysets - .insert(self.active_keyset.id, self.active_keyset.clone()); + .insert(self.active_keyset.id, self.active_keyset_info.clone()); self.active_keyset = KeySet::generate(secret, derivation_path, max_order); } @@ -195,12 +210,16 @@ impl Mint { } let keyset = proof.id.as_ref().map_or_else( - || &self.active_keyset, + || self.active_keyset.clone(), |id| { if let Some(keyset) = self.inactive_keysets.get(id) { - keyset + nut02::mint::KeySet::generate( + &keyset.secret, + &keyset.derivation_path, + keyset.max_order, + ) } else { - &self.active_keyset + self.active_keyset.clone() } }, ); diff --git a/crates/cashu/src/nuts/nut02.rs b/crates/cashu/src/nuts/nut02.rs index a1b5f2f8..1cb41bd6 100644 --- a/crates/cashu/src/nuts/nut02.rs +++ b/crates/cashu/src/nuts/nut02.rs @@ -148,6 +148,7 @@ impl From<&Keys> for Id { } /// Mint Keysets [NUT-02] +/// Ids of mints keyset ids #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Response { /// set of public key ids that the mint generates diff --git a/crates/cashu/src/types.rs b/crates/cashu/src/types.rs index 814ee4cf..7228d195 100644 --- a/crates/cashu/src/types.rs +++ b/crates/cashu/src/types.rs @@ -2,7 +2,10 @@ use serde::{Deserialize, Serialize}; -use crate::nuts::nut00::{mint, Proofs}; +use crate::nuts::{ + nut00::{mint, Proofs}, + nut02::Id, +}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ProofsStatus { @@ -32,3 +35,13 @@ pub enum InvoiceStatus { Expired, InFlight, } + +#[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct KeysetInfo { + pub id: Id, + pub valid_from: u64, + pub valid_to: Option, + pub secret: String, + pub derivation_path: String, + pub max_order: u8, +}