mirror of
https://github.com/aljazceru/cdk.git
synced 2026-01-12 09:25:57 +01:00
Drop AmountStr (#612)
* Drop AmountStr Fixes #609 Instead write a customer serializer for Keys to serialize amounts as strings * Add a custom error for invalid amounts
This commit is contained in:
@@ -6,7 +6,7 @@ use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::nuts::CurrencyUnit;
|
||||
@@ -23,6 +23,9 @@ pub enum Error {
|
||||
/// Cannot convert units
|
||||
#[error("Cannot convert units")]
|
||||
CannotConvertUnits,
|
||||
/// Invalid amount
|
||||
#[error("Invalid Amount: {0}")]
|
||||
InvalidAmount(String),
|
||||
}
|
||||
|
||||
/// Amount can be any unit
|
||||
@@ -31,6 +34,17 @@ pub enum Error {
|
||||
#[serde(transparent)]
|
||||
pub struct Amount(u64);
|
||||
|
||||
impl FromStr for Amount {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let value = s
|
||||
.parse::<u64>()
|
||||
.map_err(|_| Error::InvalidAmount(s.to_owned()))?;
|
||||
Ok(Amount(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Amount {
|
||||
/// Amount zero
|
||||
pub const ZERO: Amount = Amount(0);
|
||||
@@ -216,54 +230,6 @@ 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 {
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
//! <https://github.com/cashubtc/nuts/blob/main/01.md>
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bitcoin::secp256k1;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::de::{self, MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{serde_as, VecSkipError};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -16,7 +18,7 @@ mod secret_key;
|
||||
pub use self::public_key::PublicKey;
|
||||
pub use self::secret_key::SecretKey;
|
||||
use super::nut02::KeySet;
|
||||
use crate::amount::{Amount, AmountStr};
|
||||
use crate::amount::Amount;
|
||||
|
||||
/// Nut01 Error
|
||||
#[derive(Debug, Error)]
|
||||
@@ -42,16 +44,59 @@ pub enum Error {
|
||||
/// This is a variation of [MintKeys] that only exposes the public keys.
|
||||
///
|
||||
/// See [NUT-01]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
|
||||
pub struct Keys(BTreeMap<AmountStr, PublicKey>);
|
||||
pub struct Keys(BTreeMap<Amount, PublicKey>);
|
||||
|
||||
impl Serialize for Keys {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let map: BTreeMap<String, _> = self.0.iter().map(|(k, v)| (k.to_string(), v)).collect();
|
||||
map.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Keys {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct KeysVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for KeysVisitor {
|
||||
type Value = Keys;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map with string keys representing u64 values")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut map: M) -> Result<Keys, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut btree_map = BTreeMap::new();
|
||||
|
||||
while let Some((key, value)) = map.next_entry::<String, _>()? {
|
||||
let parsed_key = key.parse::<Amount>().map_err(de::Error::custom)?;
|
||||
btree_map.insert(parsed_key, value);
|
||||
}
|
||||
|
||||
Ok(Keys(btree_map))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(KeysVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MintKeys> for Keys {
|
||||
fn from(keys: MintKeys) -> Self {
|
||||
Self(
|
||||
keys.0
|
||||
.into_iter()
|
||||
.map(|(amount, keypair)| (AmountStr::from(amount), keypair.public_key))
|
||||
.map(|(amount, keypair)| (amount, keypair.public_key))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
@@ -60,25 +105,25 @@ impl From<MintKeys> for Keys {
|
||||
impl Keys {
|
||||
/// Create new [`Keys`]
|
||||
#[inline]
|
||||
pub fn new(keys: BTreeMap<AmountStr, PublicKey>) -> Self {
|
||||
pub fn new(keys: BTreeMap<Amount, PublicKey>) -> Self {
|
||||
Self(keys)
|
||||
}
|
||||
|
||||
/// Get [`Keys`]
|
||||
#[inline]
|
||||
pub fn keys(&self) -> &BTreeMap<AmountStr, PublicKey> {
|
||||
pub fn keys(&self) -> &BTreeMap<Amount, PublicKey> {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get [`PublicKey`] for [`Amount`]
|
||||
#[inline]
|
||||
pub fn amount_key(&self, amount: Amount) -> Option<PublicKey> {
|
||||
self.0.get(&AmountStr::from(amount)).copied()
|
||||
self.0.get(&amount).copied()
|
||||
}
|
||||
|
||||
/// Iterate through the (`Amount`, `PublicKey`) entries in the Map
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&AmountStr, &PublicKey)> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Amount, &PublicKey)> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,8 @@ 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")]
|
||||
use crate::Amount;
|
||||
|
||||
/// NUT02 Error
|
||||
@@ -181,7 +179,7 @@ impl From<&Keys> for Id {
|
||||
/// 4. take the first 14 characters of the hex-encoded hash
|
||||
/// 5. prefix it with a keyset ID version byte
|
||||
fn from(map: &Keys) -> Self {
|
||||
let mut keys: Vec<(&AmountStr, &super::PublicKey)> = map.iter().collect();
|
||||
let mut keys: Vec<(&Amount, &super::PublicKey)> = map.iter().collect();
|
||||
keys.sort_by_key(|(amt, _v)| *amt);
|
||||
|
||||
let pubkeys_concat: Vec<u8> = keys
|
||||
|
||||
Reference in New Issue
Block a user