mint.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. use std::collections::{HashMap, HashSet};
  2. use cashu::dhke::sign_message;
  3. use cashu::dhke::verify_message;
  4. pub use cashu::error::mint::Error;
  5. use cashu::nuts::nut00::BlindedMessage;
  6. use cashu::nuts::nut00::BlindedSignature;
  7. use cashu::nuts::nut00::Proof;
  8. use cashu::nuts::nut02::mint::KeySet;
  9. use cashu::nuts::nut06::SplitRequest;
  10. use cashu::nuts::nut06::SplitResponse;
  11. use cashu::nuts::nut07::CheckSpendableRequest;
  12. use cashu::nuts::nut07::CheckSpendableResponse;
  13. use cashu::nuts::nut08::MeltRequest;
  14. use cashu::nuts::nut08::MeltResponse;
  15. use cashu::nuts::*;
  16. use cashu::secret::Secret;
  17. use cashu::Amount;
  18. pub struct Mint {
  19. // pub pubkey: PublicKey,
  20. pub active_keyset: nut02::mint::KeySet,
  21. pub inactive_keysets: HashMap<String, nut02::mint::KeySet>,
  22. pub spent_secrets: HashSet<Secret>,
  23. pub pending_secrets: HashSet<Secret>,
  24. }
  25. impl Mint {
  26. pub fn new(
  27. secret: &str,
  28. derivation_path: &str,
  29. inactive_keysets: HashMap<String, nut02::mint::KeySet>,
  30. spent_secrets: HashSet<Secret>,
  31. max_order: u8,
  32. ) -> Self {
  33. Self {
  34. active_keyset: nut02::mint::KeySet::generate(secret, derivation_path, max_order),
  35. inactive_keysets,
  36. spent_secrets,
  37. pending_secrets: HashSet::new(),
  38. }
  39. }
  40. /// Retrieve the public keys of the active keyset for distribution to
  41. /// wallet clients
  42. pub fn active_keyset_pubkeys(&self) -> nut02::KeySet {
  43. nut02::KeySet::from(self.active_keyset.clone())
  44. }
  45. /// Return a list of all supported keysets
  46. pub fn keysets(&self) -> nut02::Response {
  47. let mut keysets: HashSet<_> = self.inactive_keysets.keys().cloned().collect();
  48. keysets.insert(self.active_keyset.id.clone());
  49. nut02::Response { keysets }
  50. }
  51. pub fn active_keyset(&self) -> nut02::mint::KeySet {
  52. self.active_keyset.clone()
  53. }
  54. pub fn keyset(&self, id: &str) -> Option<nut02::KeySet> {
  55. if self.active_keyset.id == id {
  56. return Some(self.active_keyset.clone().into());
  57. }
  58. self.inactive_keysets.get(id).map(|k| k.clone().into())
  59. }
  60. /// Add current keyset to inactive keysets
  61. /// Generate new keyset
  62. pub fn rotate_keyset(
  63. &mut self,
  64. secret: impl Into<String>,
  65. derivation_path: impl Into<String>,
  66. max_order: u8,
  67. ) {
  68. // Add current set to inactive keysets
  69. self.inactive_keysets
  70. .insert(self.active_keyset.id.clone(), self.active_keyset.clone());
  71. self.active_keyset = KeySet::generate(secret, derivation_path, max_order);
  72. }
  73. pub fn process_mint_request(
  74. &mut self,
  75. mint_request: nut04::MintRequest,
  76. ) -> Result<nut04::PostMintResponse, Error> {
  77. let mut blind_signatures = Vec::with_capacity(mint_request.outputs.len());
  78. for blinded_message in mint_request.outputs {
  79. blind_signatures.push(self.blind_sign(&blinded_message)?);
  80. }
  81. Ok(nut04::PostMintResponse {
  82. promises: blind_signatures,
  83. })
  84. }
  85. fn blind_sign(&self, blinded_message: &BlindedMessage) -> Result<BlindedSignature, Error> {
  86. let BlindedMessage { amount, b } = blinded_message;
  87. let Some(key_pair) = self.active_keyset.keys.0.get(&amount.to_sat()) else {
  88. // No key for amount
  89. return Err(Error::AmountKey);
  90. };
  91. let c = sign_message(key_pair.secret_key.clone().into(), b.clone().into())?;
  92. Ok(BlindedSignature {
  93. amount: *amount,
  94. c: c.into(),
  95. id: self.active_keyset.id.clone(),
  96. })
  97. }
  98. pub fn process_split_request(
  99. &mut self,
  100. split_request: SplitRequest,
  101. ) -> Result<SplitResponse, Error> {
  102. let proofs_total = split_request.proofs_amount();
  103. let output_total = split_request.output_amount();
  104. if proofs_total != output_total {
  105. return Err(Error::Amount);
  106. }
  107. let proof_count = split_request.proofs.len();
  108. let secrets: HashSet<Secret> = split_request.proofs.into_iter().map(|p| p.secret).collect();
  109. // Check that there are no duplicate proofs in request
  110. if secrets.len().ne(&proof_count) {
  111. return Err(Error::DuplicateProofs);
  112. }
  113. for secret in secrets {
  114. self.spent_secrets.insert(secret);
  115. }
  116. match &split_request.amount {
  117. None => {
  118. let promises: Vec<BlindedSignature> = split_request
  119. .outputs
  120. .iter()
  121. .map(|b| self.blind_sign(b).unwrap())
  122. .collect();
  123. Ok(SplitResponse::new(promises))
  124. }
  125. Some(amount) => {
  126. let outs_fst = (proofs_total.to_owned() - amount.to_owned()).split();
  127. // Blinded change messages
  128. let b_fst = split_request.outputs[0..outs_fst.len()].to_vec();
  129. let b_snd = split_request.outputs[outs_fst.len()..].to_vec();
  130. let fst: Vec<BlindedSignature> =
  131. b_fst.iter().map(|b| self.blind_sign(b).unwrap()).collect();
  132. let snd: Vec<BlindedSignature> =
  133. b_snd.iter().map(|b| self.blind_sign(b).unwrap()).collect();
  134. let split_response = SplitResponse::new_from_amount(fst, snd);
  135. if split_response.target_amount() != split_request.amount {
  136. return Err(Error::CustomError("Output order".to_string()));
  137. }
  138. Ok(split_response)
  139. }
  140. }
  141. }
  142. pub fn verify_proof(&self, proof: &Proof) -> Result<(), Error> {
  143. if self.spent_secrets.contains(&proof.secret) {
  144. return Err(Error::TokenSpent);
  145. }
  146. let keyset = proof.id.as_ref().map_or_else(
  147. || &self.active_keyset,
  148. |id| {
  149. if let Some(keyset) = self.inactive_keysets.get(id) {
  150. keyset
  151. } else {
  152. &self.active_keyset
  153. }
  154. },
  155. );
  156. let Some(keypair) = keyset.keys.0.get(&proof.amount.to_sat()) else {
  157. return Err(Error::AmountKey);
  158. };
  159. verify_message(
  160. keypair.secret_key.clone().into(),
  161. proof.c.clone().into(),
  162. &proof.secret,
  163. )?;
  164. Ok(())
  165. }
  166. pub fn check_spendable(
  167. &self,
  168. check_spendable: &CheckSpendableRequest,
  169. ) -> Result<CheckSpendableResponse, Error> {
  170. let mut spendable = Vec::with_capacity(check_spendable.proofs.len());
  171. let mut pending = Vec::with_capacity(check_spendable.proofs.len());
  172. for proof in &check_spendable.proofs {
  173. spendable.push(!self.spent_secrets.contains(&proof.secret));
  174. pending.push(!self.pending_secrets.contains(&proof.secret));
  175. }
  176. Ok(CheckSpendableResponse { spendable, pending })
  177. }
  178. pub fn verify_melt_request(&mut self, melt_request: &MeltRequest) -> Result<(), Error> {
  179. let proofs_total = melt_request.proofs_amount();
  180. // TODO: Fee reserve
  181. if proofs_total
  182. < melt_request
  183. .invoice_amount()
  184. .map_err(|_| Error::InvoiceAmountUndefined)?
  185. {
  186. return Err(Error::Amount);
  187. }
  188. let secrets: HashSet<&Secret> = melt_request.proofs.iter().map(|p| &p.secret).collect();
  189. // Ensure proofs are unique and not being double spent
  190. if melt_request.proofs.len().ne(&secrets.len()) {
  191. return Err(Error::DuplicateProofs);
  192. }
  193. Ok(())
  194. }
  195. pub fn process_melt_request(
  196. &mut self,
  197. melt_request: &MeltRequest,
  198. preimage: &str,
  199. total_spent: Amount,
  200. ) -> Result<MeltResponse, Error> {
  201. let secrets = Vec::with_capacity(melt_request.proofs.len());
  202. for secret in secrets {
  203. self.spent_secrets.insert(secret);
  204. }
  205. let change_target = melt_request.proofs_amount() - total_spent;
  206. let amounts = change_target.split();
  207. let mut change = Vec::with_capacity(amounts.len());
  208. if let Some(outputs) = &melt_request.outputs {
  209. for (i, amount) in amounts.iter().enumerate() {
  210. let mut message = outputs[i].clone();
  211. message.amount = *amount;
  212. let signature = self.blind_sign(&message)?;
  213. change.push(signature)
  214. }
  215. }
  216. Ok(MeltResponse {
  217. paid: true,
  218. preimage: Some(preimage.to_string()),
  219. change: Some(change),
  220. })
  221. }
  222. }