| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 | //! Types for `cashu-rs`use std::collections::HashMap;use bitcoin::Amount;use lightning_invoice::Invoice;use rand::Rng;use secp256k1::{PublicKey, SecretKey};use serde::{Deserialize, Deserializer, Serialize, Serializer};use crate::{dhke::blind_message, error::Error, utils::split_amount};/// Blinded Message [NUT-00]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct BlindedMessage {    /// Amount in satoshi    #[serde(with = "bitcoin::amount::serde::as_sat")]    pub amount: Amount,    /// encrypted secret message (B_)    #[serde(rename = "B_")]    pub b: PublicKey,}/// Blinded Messages [NUT-00]#[derive(Debug, Default, Clone, PartialEq, Eq)]pub struct BlindedMessages {    /// Blinded messages    pub blinded_messages: Vec<BlindedMessage>,    /// Secrets    pub secrets: Vec<Vec<u8>>,    /// Rs    pub rs: Vec<SecretKey>,    /// Amounts    pub amounts: Vec<Amount>,}impl BlindedMessages {    pub fn random(amount: Amount) -> Result<Self, Error> {        let mut blinded_messages = BlindedMessages::default();        let mut rng = rand::thread_rng();        for amount in split_amount(amount) {            let bytes: [u8; 32] = rng.gen();            let (blinded, r) = blind_message(&bytes, None)?;            let blinded_message = BlindedMessage { amount, b: blinded };            blinded_messages.secrets.push(bytes.to_vec());            blinded_messages.blinded_messages.push(blinded_message);            blinded_messages.rs.push(r);            blinded_messages.amounts.push(amount);        }        Ok(blinded_messages)    }}/// Promise (BlindedSignature) [NIP-00]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct Promise {    pub id: String,    #[serde(with = "bitcoin::amount::serde::as_sat")]    pub amount: Amount,    /// blinded signature (C_) on the secret message `B_` of [BlindedMessage]    #[serde(rename = "C_")]    pub c: String,}/// Proofs [NUT-00]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct Proof {    /// Amount in satoshi    #[serde(with = "bitcoin::amount::serde::as_sat")]    pub amount: Amount,    /// Secret message    pub secret: String,    /// Unblinded signature    #[serde(rename = "C")]    pub c: String,    /// `Keyset id`    pub id: Option<String>,    /// P2SHScript that specifies the spending condition for this Proof    pub script: Option<String>,}/// Mint Keys [NIP-01]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MintKeys(pub HashMap<u64, String>);/// Mint Keysets [NIP-02]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MintKeySets {    /// set of public keys that the mint generates    pub keysets: Vec<String>,}/// Mint request response [NUT-03]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct RequestMintResponse {    /// Bolt11 payment request    pub pr: Invoice,    /// Hash of Invoice    pub hash: String,}/// Post Mint Request [NIP-04]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MintRequest {    pub outputs: Vec<BlindedMessage>,}/// Post Mint Response [NUT-05]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct PostMintResponse {    pub promises: Vec<Promise>,}/// Check Fees Response [NUT-05]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct CheckFeesResponse {    /// Expected Mac Fee in satoshis        #[serde(with = "bitcoin::amount::serde::as_sat")]    pub fee: Amount,}/// Check Fees request [NUT-05]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct CheckFeesRequest {    /// Lighting Invoice    pub pr: Invoice,}/// Melt Request [NUT-05]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MeltRequest {    pub proofs: Vec<Proof>,    /// bollt11    pub pr: Invoice,    /// Blinded Message that can be used to return change [NUT-08]    /// Amount feild of blindedMessages `SHOULD` be set to zero    pub outputs: Option<Vec<BlindedMessage>>,}/// Melt Response [NUT-05]/// Lightning fee return [NUT-08] if change is defined#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MeltResposne {    pub paid: bool,    pub preimage: String,    pub change: Option<Promise>,}/// Split Request [NUT-06]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct SplitRequest {    #[serde(with = "bitcoin::amount::serde::as_sat")]    pub amount: Amount,    pub proofs: Vec<Proof>,    pub outputs: Vec<BlindedMessage>,}/// Split Response [NUT-06]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct SplitResponse {    /// Promises to keep    pub fst: Vec<BlindedMessage>,    /// Promises to send    pub snd: Vec<BlindedMessage>,}/// Check spendabale request [NUT-07]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct CheckSpendableRequest {    pub proofs: Vec<Proof>,}/// Check Spendable Response [NUT-07]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct CheckSpendableResponse {    /// booleans indicating whether the provided Proof is still spendable.    /// In same order as provided proofs    pub spendable: Vec<bool>,}/// Mint Version#[derive(Debug, Clone, PartialEq, Eq)]pub struct MintVersion {    name: String,    version: String,}impl Serialize for MintVersion {    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>    where        S: Serializer,    {        let combined = format!("{}/{}", self.name, self.version);        serializer.serialize_str(&combined)    }}impl<'de> Deserialize<'de> for MintVersion {    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>    where        D: Deserializer<'de>,    {        let combined = String::deserialize(deserializer)?;        let parts: Vec<&str> = combined.split('/').collect();        if parts.len() != 2 {            return Err(serde::de::Error::custom("Invalid input string"));        }        Ok(MintVersion {            name: parts[0].to_string(),            version: parts[1].to_string(),        })    }}/// Mint Info [NIP-09]#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]pub struct MintInfo {    /// name of the mint and should be recognizable    pub name: String,    /// hex pubkey of the mint    pub pubkey: String,    /// implementation name and the version running    pub version: MintVersion,    /// short description of the mint    pub description: String,    /// long description    pub description_long: String,    /// contact methods to reach the mint operator    pub contact: HashMap<String, String>,    /// shows which NUTs the mint supports    pub nuts: Vec<String>,    /// message of the day that the wallet must display to the user    pub motd: String,}
 |