cashu_wallet.rs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. //! Cashu Wallet
  2. use std::str::FromStr;
  3. use bitcoin::Amount;
  4. use crate::{
  5. client::Client,
  6. dhke::construct_proofs,
  7. error::Error,
  8. types::{
  9. BlindedMessages, MintKeys, Proof, ProofsStatus, RequestMintResponse, SendProofs,
  10. SplitPayload, SplitRequest, TokenData,
  11. },
  12. };
  13. pub struct CashuWallet {
  14. pub client: Client,
  15. pub mint_keys: MintKeys,
  16. }
  17. impl CashuWallet {
  18. pub fn new(client: Client, mint_keys: MintKeys) -> Self {
  19. Self { client, mint_keys }
  20. }
  21. /// Check if a proof is spent
  22. pub async fn check_proofs_spent(&self, proofs: Vec<Proof>) -> Result<ProofsStatus, Error> {
  23. let spendable = self.client.check_spendable(&proofs).await?;
  24. // Separate proofs in spent and unspent based on mint response
  25. let (spendable, spent): (Vec<_>, Vec<_>) = proofs
  26. .iter()
  27. .zip(spendable.spendable.iter())
  28. .partition(|(_, &b)| b);
  29. Ok(ProofsStatus {
  30. spendable: spendable.into_iter().map(|(s, _)| s).cloned().collect(),
  31. spent: spent.into_iter().map(|(s, _)| s).cloned().collect(),
  32. })
  33. }
  34. /// Request Token Mint
  35. pub async fn request_mint(&self, amount: Amount) -> Result<RequestMintResponse, Error> {
  36. self.client.request_mint(amount).await
  37. }
  38. /// Mint Token
  39. pub async fn mint_token(
  40. &self,
  41. amount: Amount,
  42. payment_hash: &str,
  43. ) -> Result<Vec<Proof>, Error> {
  44. let blinded_messages = BlindedMessages::random(amount)?;
  45. let mint_res = self
  46. .client
  47. .mint(blinded_messages.clone(), payment_hash)
  48. .await?;
  49. let proofs = construct_proofs(
  50. mint_res.promises,
  51. blinded_messages.rs,
  52. blinded_messages.secrets,
  53. &self.mint_keys,
  54. )?;
  55. Ok(proofs)
  56. }
  57. /// Check fee
  58. pub async fn check_fee(&self, invoice: lightning_invoice::Invoice) -> Result<Amount, Error> {
  59. Ok(self.client.check_fees(invoice).await?.fee)
  60. }
  61. /// Receive
  62. pub async fn receive(&self, encoded_token: &str) -> Result<Vec<Proof>, Error> {
  63. let token_data = TokenData::from_str(encoded_token)?;
  64. let mut proofs = vec![];
  65. for token in token_data.token {
  66. if token.proofs.is_empty() {
  67. continue;
  68. }
  69. let keys = if token.mint.eq(&self.client.mint_url) {
  70. self.mint_keys.clone()
  71. } else {
  72. // FIXME:
  73. println!("No match");
  74. self.mint_keys.clone()
  75. // CashuMint::new(token.mint).get_keys().await.unwrap()
  76. };
  77. // Sum amount of all proofs
  78. let amount = token
  79. .proofs
  80. .iter()
  81. .fold(Amount::ZERO, |acc, p| acc + p.amount);
  82. let split_payload = self
  83. .create_split(Amount::ZERO, amount, token.proofs)
  84. .await?;
  85. let split_response = self.client.split(split_payload.split_payload).await?;
  86. // Proof to keep
  87. let keep_proofs = construct_proofs(
  88. split_response.fst,
  89. split_payload.keep_blinded_messages.rs,
  90. split_payload.keep_blinded_messages.secrets,
  91. &keys,
  92. )?;
  93. // Proofs to send
  94. let send_proofs = construct_proofs(
  95. split_response.snd,
  96. split_payload.send_blinded_messages.rs,
  97. split_payload.send_blinded_messages.secrets,
  98. &keys,
  99. )?;
  100. proofs.push(keep_proofs);
  101. proofs.push(send_proofs);
  102. }
  103. Ok(proofs.iter().flatten().cloned().collect())
  104. }
  105. /// Create Split Payload
  106. async fn create_split(
  107. &self,
  108. keep_amount: Amount,
  109. send_amount: Amount,
  110. proofs: Vec<Proof>,
  111. ) -> Result<SplitPayload, Error> {
  112. let keep_blinded_messages = BlindedMessages::random(keep_amount)?;
  113. let send_blinded_messages = BlindedMessages::random(send_amount)?;
  114. let outputs = {
  115. let mut outputs = keep_blinded_messages.blinded_messages.clone();
  116. outputs.extend(send_blinded_messages.blinded_messages.clone());
  117. outputs
  118. };
  119. let split_payload = SplitRequest {
  120. amount: send_amount,
  121. proofs,
  122. outputs,
  123. };
  124. Ok(SplitPayload {
  125. keep_blinded_messages,
  126. send_blinded_messages,
  127. split_payload,
  128. })
  129. }
  130. /// Send
  131. pub async fn send(&self, amount: Amount, proofs: Vec<Proof>) -> Result<SendProofs, Error> {
  132. let mut amount_available = Amount::ZERO;
  133. let mut send_proofs = SendProofs::default();
  134. for proof in proofs {
  135. if amount_available > amount {
  136. send_proofs.change_proofs.push(proof);
  137. break;
  138. } else {
  139. amount_available += proof.amount;
  140. send_proofs.send_proofs.push(proof);
  141. }
  142. }
  143. if amount_available.lt(&amount) {
  144. println!("Not enough funds");
  145. return Err(Error::InsufficantFunds);
  146. }
  147. // If amount available is EQUAL to send amount no need to split
  148. if amount_available.eq(&amount) {
  149. return Ok(send_proofs);
  150. }
  151. let amount_to_keep = amount_available - amount;
  152. let amount_to_send = amount;
  153. let split_payload = self
  154. .create_split(amount_to_keep, amount_to_send, send_proofs.send_proofs)
  155. .await?;
  156. let split_response = self.client.split(split_payload.split_payload).await?;
  157. // Proof to keep
  158. let keep_proofs = construct_proofs(
  159. split_response.fst,
  160. split_payload.keep_blinded_messages.rs,
  161. split_payload.keep_blinded_messages.secrets,
  162. &self.mint_keys,
  163. )?;
  164. // Proofs to send
  165. let send_proofs = construct_proofs(
  166. split_response.snd,
  167. split_payload.send_blinded_messages.rs,
  168. split_payload.send_blinded_messages.secrets,
  169. &self.mint_keys,
  170. )?;
  171. // println!("Send Proofs: {:#?}", send_proofs);
  172. // println!("Keep Proofs: {:#?}", keep_proofs);
  173. Ok(SendProofs {
  174. change_proofs: keep_proofs,
  175. send_proofs,
  176. })
  177. }
  178. pub fn proofs_to_token(&self, proofs: Vec<Proof>, memo: Option<String>) -> String {
  179. TokenData::new(self.client.mint_url.clone(), proofs, memo).to_string()
  180. }
  181. }