cashu_wallet.rs 7.5 KB

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