mint.rs 10 KB

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