cashu/nuts/
nut03.rs

1//! NUT-03: Swap
2//!
3//! <https://github.com/cashubtc/nuts/blob/main/03.md>
4
5use serde::{Deserialize, Serialize};
6use thiserror::Error;
7
8#[cfg(feature = "wallet")]
9use super::nut00::PreMintSecrets;
10use super::nut00::{BlindSignature, BlindedMessage, Proofs};
11use super::ProofsMethods;
12use crate::Amount;
13
14/// NUT03 Error
15#[derive(Debug, Error)]
16pub enum Error {
17    /// DHKE error
18    #[error(transparent)]
19    DHKE(#[from] crate::dhke::Error),
20    /// Amount Error
21    #[error(transparent)]
22    Amount(#[from] crate::amount::Error),
23}
24
25/// Preswap information
26#[cfg(feature = "wallet")]
27#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
28pub struct PreSwap {
29    /// Preswap mint secrets
30    pub pre_mint_secrets: PreMintSecrets,
31    /// Swap request
32    pub swap_request: SwapRequest,
33    /// Amount to increment keyset counter by
34    pub derived_secret_count: u32,
35    /// Fee amount
36    pub fee: Amount,
37}
38
39/// Swap Request [NUT-03]
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
42pub struct SwapRequest {
43    /// Proofs that are to be spent in a `Swap`
44    #[cfg_attr(feature = "swagger", schema(value_type = Vec<crate::Proof>))]
45    inputs: Proofs,
46    /// Blinded Messages for Mint to sign
47    outputs: Vec<BlindedMessage>,
48}
49
50impl SwapRequest {
51    /// Create new [`SwapRequest`]
52    pub fn new(inputs: Proofs, outputs: Vec<BlindedMessage>) -> Self {
53        Self {
54            inputs: inputs.without_dleqs(),
55            outputs,
56        }
57    }
58
59    /// Get inputs (proofs)
60    pub fn inputs(&self) -> &Proofs {
61        &self.inputs
62    }
63
64    /// Get outputs (blinded messages)
65    pub fn outputs(&self) -> &Vec<BlindedMessage> {
66        &self.outputs
67    }
68
69    /// Get mutable reference to outputs (blinded messages)
70    pub fn outputs_mut(&mut self) -> &mut Vec<BlindedMessage> {
71        &mut self.outputs
72    }
73
74    /// Total value of proofs in [`SwapRequest`]
75    pub fn input_amount(&self) -> Result<Amount, Error> {
76        Ok(Amount::try_sum(
77            self.inputs.iter().map(|proof| proof.amount),
78        )?)
79    }
80
81    /// Total value of outputs in [`SwapRequest`]
82    pub fn output_amount(&self) -> Result<Amount, Error> {
83        Ok(Amount::try_sum(
84            self.outputs.iter().map(|proof| proof.amount),
85        )?)
86    }
87}
88
89/// Split Response [NUT-06]
90#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
91#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
92pub struct SwapResponse {
93    /// Promises
94    pub signatures: Vec<BlindSignature>,
95}
96
97impl SwapResponse {
98    /// Create new [`SwapResponse`]
99    pub fn new(promises: Vec<BlindSignature>) -> Self {
100        Self {
101            signatures: promises,
102        }
103    }
104
105    /// Total [`Amount`] of promises
106    pub fn promises_amount(&self) -> Result<Amount, Error> {
107        Ok(Amount::try_sum(
108            self.signatures
109                .iter()
110                .map(|BlindSignature { amount, .. }| *amount),
111        )?)
112    }
113}