Browse Source

mintd: sort pks by amount in /v1/keys

ok300 5 months ago
parent
commit
4efeb7d1c5
3 changed files with 67 additions and 13 deletions
  1. 50 1
      crates/cdk/src/amount.rs
  2. 14 10
      crates/cdk/src/nuts/nut01/mod.rs
  3. 3 2
      crates/cdk/src/nuts/nut02.rs

+ 50 - 1
crates/cdk/src/amount.rs

@@ -4,8 +4,9 @@
 
 use std::cmp::Ordering;
 use std::fmt;
+use std::str::FromStr;
 
-use serde::{Deserialize, Serialize};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use thiserror::Error;
 
 use crate::nuts::CurrencyUnit;
@@ -214,6 +215,54 @@ impl std::ops::Div for Amount {
     }
 }
 
+/// String wrapper for an [Amount].
+///
+/// It ser-/deserializes the inner [Amount] to a string, while at the same time using the [u64]
+/// value of the [Amount] for comparison and ordering. This helps automatically sort the keys of
+/// a [BTreeMap] when [AmountStr] is used as key.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct AmountStr(Amount);
+
+impl AmountStr {
+    pub(crate) fn from(amt: Amount) -> Self {
+        Self(amt)
+    }
+}
+
+impl PartialOrd<Self> for AmountStr {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for AmountStr {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.0.cmp(&other.0)
+    }
+}
+
+impl<'de> Deserialize<'de> for AmountStr {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let s = String::deserialize(deserializer)?;
+        u64::from_str(&s)
+            .map(Amount)
+            .map(Self)
+            .map_err(serde::de::Error::custom)
+    }
+}
+
+impl Serialize for AmountStr {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.0.to_string())
+    }
+}
+
 /// Kinds of targeting that are supported
 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)]
 pub enum SplitTarget {

+ 14 - 10
crates/cdk/src/nuts/nut01/mod.rs

@@ -16,7 +16,7 @@ mod secret_key;
 pub use self::public_key::PublicKey;
 pub use self::secret_key::SecretKey;
 use super::nut02::KeySet;
-use crate::amount::Amount;
+use crate::amount::{Amount, AmountStr};
 
 /// Nut01 Error
 #[derive(Debug, Error)]
@@ -37,16 +37,20 @@ pub enum Error {
     },
 }
 
-/// Mint Keys [NUT-01]
+/// Mint public keys per amount.
+///
+/// This is a variation of [MintKeys] that only exposes the public keys.
+///
+/// See [NUT-01]
 #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
-pub struct Keys(BTreeMap<String, PublicKey>);
+pub struct Keys(BTreeMap<AmountStr, PublicKey>);
 
 impl From<MintKeys> for Keys {
     fn from(keys: MintKeys) -> Self {
         Self(
             keys.0
-                .iter()
-                .map(|(amount, keypair)| (amount.to_string(), keypair.public_key))
+                .into_iter()
+                .map(|(amount, keypair)| (AmountStr::from(amount), keypair.public_key))
                 .collect(),
         )
     }
@@ -55,25 +59,25 @@ impl From<MintKeys> for Keys {
 impl Keys {
     /// Create new [`Keys`]
     #[inline]
-    pub fn new(keys: BTreeMap<String, PublicKey>) -> Self {
+    pub fn new(keys: BTreeMap<AmountStr, PublicKey>) -> Self {
         Self(keys)
     }
 
     /// Get [`Keys`]
     #[inline]
-    pub fn keys(&self) -> &BTreeMap<String, PublicKey> {
+    pub fn keys(&self) -> &BTreeMap<AmountStr, PublicKey> {
         &self.0
     }
 
     /// Get [`PublicKey`] for [`Amount`]
     #[inline]
     pub fn amount_key(&self, amount: Amount) -> Option<PublicKey> {
-        self.0.get(&amount.to_string()).copied()
+        self.0.get(&AmountStr::from(amount)).copied()
     }
 
     /// Iterate through the (`Amount`, `PublicKey`) entries in the Map
     #[inline]
-    pub fn iter(&self) -> impl Iterator<Item = (&String, &PublicKey)> {
+    pub fn iter(&self) -> impl Iterator<Item = (&AmountStr, &PublicKey)> {
         self.0.iter()
     }
 }
@@ -87,7 +91,7 @@ pub struct KeysResponse {
     pub keysets: Vec<KeySet>,
 }
 
-/// Mint keys
+/// Mint key pairs per amount
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct MintKeys(BTreeMap<Amount, MintKeyPair>);
 

+ 3 - 2
crates/cdk/src/nuts/nut02.rs

@@ -25,6 +25,7 @@ use thiserror::Error;
 use super::nut01::Keys;
 #[cfg(feature = "mint")]
 use super::nut01::{MintKeyPair, MintKeys};
+use crate::amount::AmountStr;
 use crate::nuts::nut00::CurrencyUnit;
 use crate::util::hex;
 #[cfg(feature = "mint")]
@@ -197,9 +198,9 @@ impl From<&Keys> for Id {
             5 - prefix it with a keyset ID version byte
         */
 
-        let mut keys: Vec<(&String, &super::PublicKey)> = map.iter().collect();
+        let mut keys: Vec<(&AmountStr, &super::PublicKey)> = map.iter().collect();
 
-        keys.sort_by_key(|(k, _v)| u64::from_str(k).unwrap());
+        keys.sort_by_key(|(amt, _v)| *amt);
 
         let pubkeys_concat: Vec<u8> = keys
             .iter()