From 09a8bd3fc04eb781e9976bdad03d3ecb7b79798a Mon Sep 17 00:00:00 2001 From: vnprc Date: Tue, 10 Sep 2024 22:56:25 -0400 Subject: [PATCH 1/9] feat: functions to convert keyset ID to and from u64 --- crates/cdk/src/nuts/nut02.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cdk/src/nuts/nut02.rs index 0de24c984..13f327a5f 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cdk/src/nuts/nut02.rs @@ -112,6 +112,20 @@ impl Id { id: bytes[1..].try_into()?, }) } + + /// [`Id`] to u64 + pub fn to_u64(&self) -> u64 { + let bytes = self.to_bytes(); + let mut array = [0u8; 8]; + array[..bytes.len()].copy_from_slice(&bytes); + u64::from_be_bytes(array) + } + + /// [`Id`] from u64 + pub fn from_u64(value: u64) -> Result { + let bytes = value.to_be_bytes(); + Self::from_bytes(&bytes) + } } impl TryFrom for u64 { From 9f0951b105ca93013371ca51c03fd340999efdf9 Mon Sep 17 00:00:00 2001 From: vnprc Date: Sat, 9 Nov 2024 22:27:19 -0500 Subject: [PATCH 2/9] test: ID::to_u64 and ID::from_u64 --- .pre-commit-config.yaml | 2 +- crates/cdk/src/nuts/nut02.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e7bdaccfc..8ff7ac2d4 120000 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1 +1 @@ -/nix/store/9bf8g8scpkrma0rwv05b4bd1qc81gihg-pre-commit-config.json \ No newline at end of file +/nix/store/v35hg96mpn8sa4i4vk9qm1f4jdyb59yj-pre-commit-config.json \ No newline at end of file diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cdk/src/nuts/nut02.rs index 13f327a5f..f2ca74fe6 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cdk/src/nuts/nut02.rs @@ -561,4 +561,15 @@ mod test { let id_from_uppercase = Id::from_str(&SHORT_KEYSET_ID.to_uppercase()); assert!(id_from_uppercase.is_ok()); } + + #[test] + fn test_id_u64_conversion() { + let id = generate_random_id(); + let u64_value = id.to_u64(); + let converted_id = Id::from_u64(u64_value).unwrap(); + + assert_eq!(id, converted_id); + assert_eq!(id.version, converted_id.version); + assert_eq!(id.id, converted_id.id); + } } From a34d93be53c135f31728eb2c849dbd242e66a284 Mon Sep 17 00:00:00 2001 From: vnprc Date: Mon, 11 Nov 2024 22:01:24 -0500 Subject: [PATCH 3/9] fix: return u32 from existing Id::TryFrom and add lossless u64 versions --- crates/cdk/src/nuts/nut02.rs | 62 +++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cdk/src/nuts/nut02.rs index f2ca74fe6..f4da54b63 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cdk/src/nuts/nut02.rs @@ -112,17 +112,23 @@ impl Id { id: bytes[1..].try_into()?, }) } +} + +impl TryFrom for u32 { + type Error = Error; + fn try_from(value: Id) -> Result { + let hex_bytes: [u8; 8] = value.to_bytes().try_into().map_err(|_| Error::Length)?; + + let int = u64::from_be_bytes(hex_bytes); - /// [`Id`] to u64 - pub fn to_u64(&self) -> u64 { - let bytes = self.to_bytes(); - let mut array = [0u8; 8]; - array[..bytes.len()].copy_from_slice(&bytes); - u64::from_be_bytes(array) + let result = (int % (2_u64.pow(31) - 1)) as u32; + Ok(result) } +} - /// [`Id`] from u64 - pub fn from_u64(value: u64) -> Result { +impl TryFrom for Id { + type Error = Error; + fn try_from(value: u64) -> Result { let bytes = value.to_be_bytes(); Self::from_bytes(&bytes) } @@ -130,12 +136,11 @@ impl Id { impl TryFrom for u64 { type Error = Error; - fn try_from(value: Id) -> Result { - let hex_bytes: [u8; 8] = value.to_bytes().try_into().map_err(|_| Error::Length)?; - let int = u64::from_be_bytes(hex_bytes); - - Ok(int % (2_u64.pow(31) - 1)) + fn try_from(value: Id) -> Result { + let bytes = value.to_bytes(); + let byte_array: [u8; 8] = bytes.try_into().map_err(|_| Error::Length)?; + Ok(u64::from_be_bytes(byte_array)) } } @@ -504,10 +509,28 @@ mod test { fn test_to_int() { let id = Id::from_str("009a1f293253e41e").unwrap(); - let id_int = u64::try_from(id).unwrap(); + let id_int = u32::try_from(id).unwrap(); assert_eq!(864559728, id_int) } + #[test] + fn test_u64_to_id_and_back_conversion() { + let id = Id::from_str("009a1f293253e41e").unwrap(); + + let id_long = u64::try_from(id).unwrap(); + assert_eq!(43381408211919902, id_long); + + let new_id = Id::try_from(id_long).unwrap(); + assert_eq!(id, new_id); + } + + #[test] + fn test_id_from_invalid_byte_length() { + let three_bytes = [0x01, 0x02, 0x03]; + let result = Id::from_bytes(&three_bytes); + assert!(result.is_err(), "Expected an invalid byte length error"); + } + #[test] fn test_keyset_bytes() { let id = Id::from_str("009a1f293253e41e").unwrap(); @@ -561,15 +584,4 @@ mod test { let id_from_uppercase = Id::from_str(&SHORT_KEYSET_ID.to_uppercase()); assert!(id_from_uppercase.is_ok()); } - - #[test] - fn test_id_u64_conversion() { - let id = generate_random_id(); - let u64_value = id.to_u64(); - let converted_id = Id::from_u64(u64_value).unwrap(); - - assert_eq!(id, converted_id); - assert_eq!(id.version, converted_id.version); - assert_eq!(id.id, converted_id.id); - } } From 91208e601266aff8b5501dc373e4d726f85f0274 Mon Sep 17 00:00:00 2001 From: vnprc Date: Mon, 11 Nov 2024 22:09:56 -0500 Subject: [PATCH 4/9] fix: revert change to .pre-commit-config.yaml --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ff7ac2d4..e7bdaccfc 120000 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1 +1 @@ -/nix/store/v35hg96mpn8sa4i4vk9qm1f4jdyb59yj-pre-commit-config.json \ No newline at end of file +/nix/store/9bf8g8scpkrma0rwv05b4bd1qc81gihg-pre-commit-config.json \ No newline at end of file From d23733c39fac75778778499b94f1f069a877bd68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20D=2E=20Rodas?= Date: Sun, 10 Nov 2024 09:08:44 -0300 Subject: [PATCH 5/9] fix: Send current state of the subscriptions (#444) --- crates/cdk-axum/src/ws/subscribe.rs | 20 +++- crates/cdk-integration-tests/tests/regtest.rs | 14 ++- crates/cdk/Cargo.toml | 2 +- crates/cdk/src/mint/mod.rs | 2 +- crates/cdk/src/nuts/mod.rs | 2 + crates/cdk/src/nuts/nut06.rs | 5 +- .../cdk/src/nuts/{nut17.rs => nut17/mod.rs} | 31 +++-- crates/cdk/src/nuts/nut17/on_subscription.rs | 110 ++++++++++++++++++ crates/cdk/src/pub_sub/mod.rs | 69 ++++++++++- 9 files changed, 236 insertions(+), 19 deletions(-) rename crates/cdk/src/nuts/{nut17.rs => nut17/mod.rs} (94%) create mode 100644 crates/cdk/src/nuts/nut17/on_subscription.rs diff --git a/crates/cdk-axum/src/ws/subscribe.rs b/crates/cdk-axum/src/ws/subscribe.rs index 7b755eda2..0a7de1589 100644 --- a/crates/cdk-axum/src/ws/subscribe.rs +++ b/crates/cdk-axum/src/ws/subscribe.rs @@ -11,17 +11,26 @@ use cdk::{ pub struct Method(Params); #[derive(Debug, Clone, serde::Serialize)] +/// The response to a subscription request pub struct Response { + /// Status status: String, + /// Subscription ID #[serde(rename = "subId")] sub_id: SubId, } #[derive(Debug, Clone, serde::Serialize)] +/// The notification +/// +/// This is the notification that is sent to the client when an event matches a +/// subscription pub struct Notification { + /// The subscription ID #[serde(rename = "subId")] pub sub_id: SubId, + /// The notification payload pub payload: NotificationPayload, } @@ -39,12 +48,21 @@ impl From<(SubId, NotificationPayload)> for WsNotification { impl WsHandle for Method { type Response = Response; + /// The `handle` method is called when a client sends a subscription request async fn handle(self, context: &mut WsContext) -> Result { let sub_id = self.0.id.clone(); if context.subscriptions.contains_key(&sub_id) { + // Subscription ID already exits. Returns an error instead of + // replacing the other subscription or avoiding it. return Err(WsError::InvalidParams); } - let mut subscription = context.state.mint.pubsub_manager.subscribe(self.0).await; + + let mut subscription = context + .state + .mint + .pubsub_manager + .subscribe(self.0.clone()) + .await; let publisher = context.publisher.clone(); context.subscriptions.insert( sub_id.clone(), diff --git a/crates/cdk-integration-tests/tests/regtest.rs b/crates/cdk-integration-tests/tests/regtest.rs index 2ba545951..24bbe89b2 100644 --- a/crates/cdk-integration-tests/tests/regtest.rs +++ b/crates/cdk-integration-tests/tests/regtest.rs @@ -35,7 +35,7 @@ async fn get_notification> + Unpin, E: De .unwrap(); let mut response: serde_json::Value = - serde_json::from_str(&msg.to_text().unwrap()).expect("valid json"); + serde_json::from_str(msg.to_text().unwrap()).expect("valid json"); let mut params_raw = response .as_object_mut() @@ -112,6 +112,18 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> { assert!(melt_response.preimage.is_some()); assert!(melt_response.state == MeltQuoteState::Paid); + let (sub_id, payload) = get_notification(&mut reader, Duration::from_millis(15000)).await; + // first message is the current state + assert_eq!("test-sub", sub_id); + let payload = match payload { + NotificationPayload::MeltQuoteBolt11Response(melt) => melt, + _ => panic!("Wrong payload"), + }; + assert_eq!(payload.amount + payload.fee_reserve, 100.into()); + assert_eq!(payload.quote, melt.id); + assert_eq!(payload.state, MeltQuoteState::Unpaid); + + // get current state let (sub_id, payload) = get_notification(&mut reader, Duration::from_millis(15000)).await; assert_eq!("test-sub", sub_id); let payload = match payload { diff --git a/crates/cdk/Cargo.toml b/crates/cdk/Cargo.toml index 816738614..4d72da318 100644 --- a/crates/cdk/Cargo.toml +++ b/crates/cdk/Cargo.toml @@ -39,7 +39,7 @@ serde_json = "1" serde_with = "3" tracing = { version = "0.1", default-features = false, features = ["attributes", "log"] } thiserror = "1" -futures = { version = "0.3.28", default-features = false, optional = true } +futures = { version = "0.3.28", default-features = false, optional = true, features = ["alloc"] } url = "2.3" utoipa = { version = "4", optional = true } uuid = { version = "1", features = ["v4"] } diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index 5c8e28e1d..dbbdc59b6 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -185,7 +185,7 @@ impl Mint { Ok(Self { mint_url: MintUrl::from_str(mint_url)?, keysets: Arc::new(RwLock::new(active_keysets)), - pubsub_manager: Default::default(), + pubsub_manager: Arc::new(localstore.clone().into()), secp_ctx, quote_ttl, xpriv, diff --git a/crates/cdk/src/nuts/mod.rs b/crates/cdk/src/nuts/mod.rs index 79bfb0d82..eb1f81707 100644 --- a/crates/cdk/src/nuts/mod.rs +++ b/crates/cdk/src/nuts/mod.rs @@ -18,6 +18,7 @@ pub mod nut12; pub mod nut13; pub mod nut14; pub mod nut15; +#[cfg(feature = "mint")] pub mod nut17; pub mod nut18; @@ -48,5 +49,6 @@ pub use nut11::{Conditions, P2PKWitness, SigFlag, SpendingConditions}; pub use nut12::{BlindSignatureDleq, ProofDleq}; pub use nut14::HTLCWitness; pub use nut15::{Mpp, MppMethodSettings, Settings as NUT15Settings}; +#[cfg(feature = "mint")] pub use nut17::{NotificationPayload, PubSubManager}; pub use nut18::{PaymentRequest, PaymentRequestPayload, Transport}; diff --git a/crates/cdk/src/nuts/nut06.rs b/crates/cdk/src/nuts/nut06.rs index 9ecabe876..17ba18b6d 100644 --- a/crates/cdk/src/nuts/nut06.rs +++ b/crates/cdk/src/nuts/nut06.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::nut01::PublicKey; -use super::{nut04, nut05, nut15, nut17, MppMethodSettings}; +use super::{nut04, nut05, nut15, MppMethodSettings}; /// Mint Version #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -238,7 +238,8 @@ pub struct Nuts { /// NUT17 Settings #[serde(default)] #[serde(rename = "17")] - pub nut17: nut17::SupportedSettings, + #[cfg(feature = "mint")] + pub nut17: super::nut17::SupportedSettings, } impl Nuts { diff --git a/crates/cdk/src/nuts/nut17.rs b/crates/cdk/src/nuts/nut17/mod.rs similarity index 94% rename from crates/cdk/src/nuts/nut17.rs rename to crates/cdk/src/nuts/nut17/mod.rs index 3d64fad9f..d186cc634 100644 --- a/crates/cdk/src/nuts/nut17.rs +++ b/crates/cdk/src/nuts/nut17/mod.rs @@ -1,5 +1,8 @@ //! Specific Subscription for the cdk crate +use super::{BlindSignature, CurrencyUnit, PaymentMethod}; +use crate::cdk_database::{self, MintDatabase}; +pub use crate::pub_sub::SubId; use crate::{ nuts::{ MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, MintQuoteState, @@ -8,7 +11,11 @@ use crate::{ pub_sub::{self, Index, Indexable, SubscriptionGlobalId}, }; use serde::{Deserialize, Serialize}; -use std::ops::Deref; +use std::{ops::Deref, sync::Arc}; + +mod on_subscription; + +pub use on_subscription::OnSubscription; /// Subscription Parameter according to the standard #[derive(Debug, Clone, Serialize, Deserialize)] @@ -57,10 +64,6 @@ impl Default for SupportedMethods { } } -pub use crate::pub_sub::SubId; - -use super::{BlindSignature, CurrencyUnit, PaymentMethod}; - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] /// Subscription response @@ -145,15 +148,27 @@ impl From for Vec> { } /// Manager -#[derive(Default)] /// Publish–subscribe manager /// /// Nut-17 implementation is system-wide and not only through the WebSocket, so /// it is possible for another part of the system to subscribe to events. -pub struct PubSubManager(pub_sub::Manager); +pub struct PubSubManager(pub_sub::Manager); + +#[allow(clippy::default_constructed_unit_structs)] +impl Default for PubSubManager { + fn default() -> Self { + PubSubManager(OnSubscription::default().into()) + } +} + +impl From + Send + Sync>> for PubSubManager { + fn from(val: Arc + Send + Sync>) -> Self { + PubSubManager(OnSubscription(Some(val)).into()) + } +} impl Deref for PubSubManager { - type Target = pub_sub::Manager; + type Target = pub_sub::Manager; fn deref(&self) -> &Self::Target { &self.0 diff --git a/crates/cdk/src/nuts/nut17/on_subscription.rs b/crates/cdk/src/nuts/nut17/on_subscription.rs new file mode 100644 index 000000000..b2347e600 --- /dev/null +++ b/crates/cdk/src/nuts/nut17/on_subscription.rs @@ -0,0 +1,110 @@ +//! On Subscription +//! +//! This module contains the code that is triggered when a new subscription is created. +use super::{Kind, NotificationPayload}; +use crate::{ + cdk_database::{self, MintDatabase}, + nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}, + pub_sub::OnNewSubscription, +}; +use std::{collections::HashMap, sync::Arc}; + +#[derive(Default)] +/// Subscription Init +/// +/// This struct triggers code when a new subscription is created. +/// +/// It is used to send the initial state of the subscription to the client. +pub struct OnSubscription( + pub(crate) Option + Send + Sync>>, +); + +#[async_trait::async_trait] +impl OnNewSubscription for OnSubscription { + type Event = NotificationPayload; + type Index = (String, Kind); + + async fn on_new_subscription( + &self, + request: &[&Self::Index], + ) -> Result, String> { + let datastore = if let Some(localstore) = self.0.as_ref() { + localstore + } else { + return Ok(vec![]); + }; + + let mut to_return = vec![]; + + for (kind, values) in request.iter().fold( + HashMap::new(), + |mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| { + acc.entry(kind).or_default().push(data); + acc + }, + ) { + match kind { + Kind::Bolt11MeltQuote => { + let queries = values + .iter() + .map(|id| datastore.get_melt_quote(id)) + .collect::>(); + + to_return.extend( + futures::future::try_join_all(queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MeltQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + } + Kind::Bolt11MintQuote => { + let queries = values + .iter() + .map(|id| datastore.get_mint_quote(id)) + .collect::>(); + + to_return.extend( + futures::future::try_join_all(queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MintQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + } + Kind::ProofState => { + let public_keys = values + .iter() + .map(PublicKey::from_hex) + .collect::, _>>() + .map_err(|e| e.to_string())?; + + to_return.extend( + datastore + .get_proofs_states(&public_keys) + .await + .map_err(|e| e.to_string())? + .into_iter() + .enumerate() + .filter_map(|(idx, state)| { + state.map(|state| (public_keys[idx], state).into()) + }) + .map(|state: ProofState| state.into()), + ); + } + } + } + + Ok(to_return) + } +} diff --git a/crates/cdk/src/pub_sub/mod.rs b/crates/cdk/src/pub_sub/mod.rs index f9347c784..c511c8037 100644 --- a/crates/cdk/src/pub_sub/mod.rs +++ b/crates/cdk/src/pub_sub/mod.rs @@ -37,6 +37,25 @@ pub const DEFAULT_REMOVE_SIZE: usize = 10_000; /// Default channel size for subscription buffering pub const DEFAULT_CHANNEL_SIZE: usize = 10; +#[async_trait::async_trait] +/// On New Subscription trait +/// +/// This trait is optional and it is used to notify the application when a new +/// subscription is created. This is useful when the application needs to send +/// the initial state to the subscriber upon subscription +pub trait OnNewSubscription { + /// Index type + type Index; + /// Subscription event type + type Event; + + /// Called when a new subscription is created + async fn on_new_subscription( + &self, + request: &[&Self::Index], + ) -> Result, String>; +} + /// Subscription manager /// /// This object keep track of all subscription listener and it is also @@ -45,21 +64,24 @@ pub const DEFAULT_CHANNEL_SIZE: usize = 10; /// The content of the notification is not relevant to this scope and it is up /// to the application, therefore the generic T is used instead of a specific /// type -pub struct Manager +pub struct Manager where T: Indexable + Clone + Send + Sync + 'static, I: PartialOrd + Clone + Debug + Ord + Send + Sync + 'static, + F: OnNewSubscription + 'static, { indexes: IndexTree, + on_new_subscription: Option, unsubscription_sender: mpsc::Sender<(SubId, Vec>)>, active_subscriptions: Arc, background_subscription_remover: Option>, } -impl Default for Manager +impl Default for Manager where T: Indexable + Clone + Send + Sync + 'static, I: PartialOrd + Clone + Debug + Ord + Send + Sync + 'static, + F: OnNewSubscription + 'static, { fn default() -> Self { let (sender, receiver) = mpsc::channel(DEFAULT_REMOVE_SIZE); @@ -72,6 +94,7 @@ where storage.clone(), active_subscriptions.clone(), ))), + on_new_subscription: None, unsubscription_sender: sender, active_subscriptions, indexes: storage, @@ -79,10 +102,24 @@ where } } -impl Manager +impl From for Manager where T: Indexable + Clone + Send + Sync + 'static, - I: Clone + Debug + PartialOrd + Ord + Send + Sync + 'static, + I: PartialOrd + Clone + Debug + Ord + Send + Sync + 'static, + F: OnNewSubscription + 'static, +{ + fn from(value: F) -> Self { + let mut manager: Self = Default::default(); + manager.on_new_subscription = Some(value); + manager + } +} + +impl Manager +where + T: Indexable + Clone + Send + Sync + 'static, + I: PartialOrd + Clone + Debug + Ord + Send + Sync + 'static, + F: OnNewSubscription + 'static, { #[inline] /// Broadcast an event to all listeners @@ -132,8 +169,29 @@ where ) -> ActiveSubscription { let (sender, receiver) = mpsc::channel(10); let sub_id: SubId = params.as_ref().clone(); + let indexes: Vec> = params.into(); + if let Some(on_new_subscription) = self.on_new_subscription.as_ref() { + match on_new_subscription + .on_new_subscription(&indexes.iter().map(|x| x.deref()).collect::>()) + .await + { + Ok(events) => { + for event in events { + let _ = sender.try_send((sub_id.clone(), event)); + } + } + Err(err) => { + tracing::info!( + "Failed to get initial state for subscription: {:?}, {}", + sub_id, + err + ); + } + } + } + let mut index_storage = self.indexes.write().await; for index in indexes.clone() { index_storage.insert(index, sender.clone()); @@ -180,10 +238,11 @@ where } /// Manager goes out of scope, stop all background tasks -impl Drop for Manager +impl Drop for Manager where T: Indexable + Clone + Send + Sync + 'static, I: Clone + Debug + PartialOrd + Ord + Send + Sync + 'static, + F: OnNewSubscription + 'static, { fn drop(&mut self) { if let Some(handler) = self.background_subscription_remover.take() { From 072dd71a0d0a403add2a0570119b51af37fe679a Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 10 Nov 2024 12:33:27 +0000 Subject: [PATCH 6/9] chore: rust fmt --- rustfmt.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rustfmt.toml b/rustfmt.toml index 9b155f2e6..547421e78 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,5 +1,9 @@ tab_spaces = 4 -max_width = 100 newline_style = "Auto" reorder_imports = true reorder_modules = true +reorder_impl_items = false +indent_style = "Block" +normalize_comments = false +imports_granularity = "Module" +group_imports = "StdExternalCrate" From 980174146f686936f79c6abcea48936fc3a59b4c Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 10 Nov 2024 12:12:42 +0000 Subject: [PATCH 7/9] chore: nut17 import oordering --- crates/cdk/src/nuts/nut17/mod.rs | 14 ++++++++------ crates/cdk/src/nuts/nut17/on_subscription.rs | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cdk/src/nuts/nut17/mod.rs b/crates/cdk/src/nuts/nut17/mod.rs index d186cc634..7d2a3c4a9 100644 --- a/crates/cdk/src/nuts/nut17/mod.rs +++ b/crates/cdk/src/nuts/nut17/mod.rs @@ -1,8 +1,13 @@ //! Specific Subscription for the cdk crate -use super::{BlindSignature, CurrencyUnit, PaymentMethod}; +use std::{ops::Deref, sync::Arc}; + +use serde::{Deserialize, Serialize}; + +mod on_subscription; + use crate::cdk_database::{self, MintDatabase}; -pub use crate::pub_sub::SubId; +use crate::nuts::{BlindSignature, CurrencyUnit, PaymentMethod}; use crate::{ nuts::{ MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, MintQuoteState, @@ -10,11 +15,8 @@ use crate::{ }, pub_sub::{self, Index, Indexable, SubscriptionGlobalId}, }; -use serde::{Deserialize, Serialize}; -use std::{ops::Deref, sync::Arc}; - -mod on_subscription; +pub use crate::pub_sub::SubId; pub use on_subscription::OnSubscription; /// Subscription Parameter according to the standard diff --git a/crates/cdk/src/nuts/nut17/on_subscription.rs b/crates/cdk/src/nuts/nut17/on_subscription.rs index b2347e600..9aaf8c18b 100644 --- a/crates/cdk/src/nuts/nut17/on_subscription.rs +++ b/crates/cdk/src/nuts/nut17/on_subscription.rs @@ -1,13 +1,14 @@ //! On Subscription //! //! This module contains the code that is triggered when a new subscription is created. +use std::{collections::HashMap, sync::Arc}; + use super::{Kind, NotificationPayload}; use crate::{ cdk_database::{self, MintDatabase}, nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}, pub_sub::OnNewSubscription, }; -use std::{collections::HashMap, sync::Arc}; #[derive(Default)] /// Subscription Init From 1012f4681d337e415353770c19556a331c2da363 Mon Sep 17 00:00:00 2001 From: Mubarak Muhammad Aminu Date: Tue, 12 Nov 2024 14:18:58 +0100 Subject: [PATCH 8/9] multiple active keysets and return active keyset with lowest fee (#448) --- crates/cdk/src/wallet/keysets.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/cdk/src/wallet/keysets.rs b/crates/cdk/src/wallet/keysets.rs index 9e5babc51..546c17a51 100644 --- a/crates/cdk/src/wallet/keysets.rs +++ b/crates/cdk/src/wallet/keysets.rs @@ -50,7 +50,7 @@ impl Wallet { /// Queries mint for current keysets then gets [`Keys`] for any unknown /// keysets #[instrument(skip(self))] - pub async fn get_active_mint_keyset(&self) -> Result { + pub async fn get_active_mint_keysets(&self) -> Result, Error> { let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?; let keysets = keysets.keysets; @@ -86,6 +86,21 @@ impl Wallet { } } - active_keysets.first().ok_or(Error::NoActiveKeyset).cloned() + Ok(active_keysets) + } + + /// Get active keyset for mint with the lowest fees + /// + /// Queries mint for current keysets then gets [`Keys`] for any unknown + /// keysets + #[instrument(skip(self))] + pub async fn get_active_mint_keyset(&self) -> Result { + let active_keysets = self.get_active_mint_keysets().await?; + + let keyset_with_lowest_fee = active_keysets + .into_iter() + .min_by_key(|key| key.input_fee_ppk) + .ok_or(Error::NoActiveKeyset)?; + Ok(keyset_with_lowest_fee) } } From efda2aabd1d880e22ac0dd72b9a9019fe4582cfc Mon Sep 17 00:00:00 2001 From: vnprc Date: Tue, 12 Nov 2024 21:31:12 -0500 Subject: [PATCH 9/9] rename test function to be more concise and accurate --- crates/cdk/src/nuts/nut02.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cdk/src/nuts/nut02.rs b/crates/cdk/src/nuts/nut02.rs index f4da54b63..57393980a 100644 --- a/crates/cdk/src/nuts/nut02.rs +++ b/crates/cdk/src/nuts/nut02.rs @@ -514,7 +514,7 @@ mod test { } #[test] - fn test_u64_to_id_and_back_conversion() { + fn test_to_u64_and_back() { let id = Id::from_str("009a1f293253e41e").unwrap(); let id_long = u64::try_from(id).unwrap();