fake_wallet.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. use std::{sync::Arc, time::Duration};
  2. use anyhow::Result;
  3. use bip39::Mnemonic;
  4. use cdk::{
  5. amount::SplitTarget,
  6. cdk_database::WalletMemoryDatabase,
  7. nuts::{CurrencyUnit, MeltQuoteState, PreMintSecrets, State},
  8. wallet::{client::HttpClient, Wallet},
  9. };
  10. use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
  11. use cdk_integration_tests::attempt_to_swap_pending;
  12. use tokio::time::sleep;
  13. const MINT_URL: &str = "http://127.0.0.1:8086";
  14. // If both pay and check return pending input proofs should remain pending
  15. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  16. async fn test_fake_tokens_pending() -> Result<()> {
  17. let wallet = Wallet::new(
  18. MINT_URL,
  19. CurrencyUnit::Sat,
  20. Arc::new(WalletMemoryDatabase::default()),
  21. &Mnemonic::generate(12)?.to_seed_normalized(""),
  22. None,
  23. )?;
  24. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  25. sleep(Duration::from_secs(5)).await;
  26. let _mint_amount = wallet
  27. .mint(&mint_quote.id, SplitTarget::default(), None)
  28. .await?;
  29. let fake_description = FakeInvoiceDescription {
  30. pay_invoice_state: MeltQuoteState::Pending,
  31. check_payment_state: MeltQuoteState::Pending,
  32. pay_err: false,
  33. check_err: false,
  34. };
  35. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  36. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  37. let melt = wallet.melt(&melt_quote.id).await;
  38. assert!(melt.is_err());
  39. attempt_to_swap_pending(&wallet).await?;
  40. Ok(())
  41. }
  42. // If the pay error fails and the check returns unknown or failed
  43. // The inputs proofs should be unset as spending
  44. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  45. async fn test_fake_melt_payment_fail() -> Result<()> {
  46. let wallet = Wallet::new(
  47. MINT_URL,
  48. CurrencyUnit::Sat,
  49. Arc::new(WalletMemoryDatabase::default()),
  50. &Mnemonic::generate(12)?.to_seed_normalized(""),
  51. None,
  52. )?;
  53. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  54. let _mint_amount = wallet
  55. .mint(&mint_quote.id, SplitTarget::default(), None)
  56. .await?;
  57. let fake_description = FakeInvoiceDescription {
  58. pay_invoice_state: MeltQuoteState::Unknown,
  59. check_payment_state: MeltQuoteState::Unknown,
  60. pay_err: true,
  61. check_err: false,
  62. };
  63. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  64. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  65. // The melt should error at the payment invoice command
  66. let melt = wallet.melt(&melt_quote.id).await;
  67. assert!(melt.is_err());
  68. let fake_description = FakeInvoiceDescription {
  69. pay_invoice_state: MeltQuoteState::Failed,
  70. check_payment_state: MeltQuoteState::Failed,
  71. pay_err: true,
  72. check_err: false,
  73. };
  74. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  75. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  76. // The melt should error at the payment invoice command
  77. let melt = wallet.melt(&melt_quote.id).await;
  78. assert!(melt.is_err());
  79. // The mint should have unset proofs from pending since payment failed
  80. let all_proof = wallet.get_proofs().await?;
  81. let states = wallet.check_proofs_spent(all_proof).await?;
  82. for state in states {
  83. assert!(state.state == State::Unspent);
  84. }
  85. let wallet_bal = wallet.total_balance().await?;
  86. assert!(wallet_bal == 100.into());
  87. Ok(())
  88. }
  89. // When both the pay_invoice and check_invoice both fail
  90. // the proofs should remain as pending
  91. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  92. async fn test_fake_melt_payment_fail_and_check() -> Result<()> {
  93. let wallet = Wallet::new(
  94. MINT_URL,
  95. CurrencyUnit::Sat,
  96. Arc::new(WalletMemoryDatabase::default()),
  97. &Mnemonic::generate(12)?.to_seed_normalized(""),
  98. None,
  99. )?;
  100. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  101. let _mint_amount = wallet
  102. .mint(&mint_quote.id, SplitTarget::default(), None)
  103. .await?;
  104. let fake_description = FakeInvoiceDescription {
  105. pay_invoice_state: MeltQuoteState::Unknown,
  106. check_payment_state: MeltQuoteState::Unknown,
  107. pay_err: true,
  108. check_err: true,
  109. };
  110. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  111. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  112. // The melt should error at the payment invoice command
  113. let melt = wallet.melt(&melt_quote.id).await;
  114. assert!(melt.is_err());
  115. let pending = wallet
  116. .localstore
  117. .get_proofs(None, None, Some(vec![State::Pending]), None)
  118. .await?;
  119. assert!(!pending.is_empty());
  120. Ok(())
  121. }
  122. // In the case that the ln backend returns a failed status but does not error
  123. // The mint should do a second check, then remove proofs from pending
  124. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  125. async fn test_fake_melt_payment_return_fail_status() -> Result<()> {
  126. let wallet = Wallet::new(
  127. MINT_URL,
  128. CurrencyUnit::Sat,
  129. Arc::new(WalletMemoryDatabase::default()),
  130. &Mnemonic::generate(12)?.to_seed_normalized(""),
  131. None,
  132. )?;
  133. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  134. let _mint_amount = wallet
  135. .mint(&mint_quote.id, SplitTarget::default(), None)
  136. .await?;
  137. let fake_description = FakeInvoiceDescription {
  138. pay_invoice_state: MeltQuoteState::Failed,
  139. check_payment_state: MeltQuoteState::Failed,
  140. pay_err: false,
  141. check_err: false,
  142. };
  143. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  144. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  145. // The melt should error at the payment invoice command
  146. let melt = wallet.melt(&melt_quote.id).await;
  147. assert!(melt.is_err());
  148. let fake_description = FakeInvoiceDescription {
  149. pay_invoice_state: MeltQuoteState::Unknown,
  150. check_payment_state: MeltQuoteState::Unknown,
  151. pay_err: false,
  152. check_err: false,
  153. };
  154. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  155. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  156. // The melt should error at the payment invoice command
  157. let melt = wallet.melt(&melt_quote.id).await;
  158. assert!(melt.is_err());
  159. let pending = wallet
  160. .localstore
  161. .get_proofs(None, None, Some(vec![State::Pending]), None)
  162. .await?;
  163. assert!(pending.is_empty());
  164. Ok(())
  165. }
  166. // In the case that the ln backend returns a failed status but does not error
  167. // The mint should do a second check, then remove proofs from pending
  168. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  169. async fn test_fake_melt_payment_error_unknown() -> Result<()> {
  170. let wallet = Wallet::new(
  171. MINT_URL,
  172. CurrencyUnit::Sat,
  173. Arc::new(WalletMemoryDatabase::default()),
  174. &Mnemonic::generate(12)?.to_seed_normalized(""),
  175. None,
  176. )?;
  177. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  178. let _mint_amount = wallet
  179. .mint(&mint_quote.id, SplitTarget::default(), None)
  180. .await?;
  181. let fake_description = FakeInvoiceDescription {
  182. pay_invoice_state: MeltQuoteState::Failed,
  183. check_payment_state: MeltQuoteState::Unknown,
  184. pay_err: true,
  185. check_err: false,
  186. };
  187. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  188. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  189. // The melt should error at the payment invoice command
  190. let melt = wallet.melt(&melt_quote.id).await;
  191. assert!(melt.is_err());
  192. let fake_description = FakeInvoiceDescription {
  193. pay_invoice_state: MeltQuoteState::Unknown,
  194. check_payment_state: MeltQuoteState::Unknown,
  195. pay_err: true,
  196. check_err: false,
  197. };
  198. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  199. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  200. // The melt should error at the payment invoice command
  201. let melt = wallet.melt(&melt_quote.id).await;
  202. assert!(melt.is_err());
  203. let pending = wallet
  204. .localstore
  205. .get_proofs(None, None, Some(vec![State::Pending]), None)
  206. .await?;
  207. assert!(pending.is_empty());
  208. Ok(())
  209. }
  210. // In the case that the ln backend returns an err
  211. // The mint should do a second check, that returns paid
  212. // Proofs should remain pending
  213. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  214. async fn test_fake_melt_payment_err_paid() -> Result<()> {
  215. let wallet = Wallet::new(
  216. MINT_URL,
  217. CurrencyUnit::Sat,
  218. Arc::new(WalletMemoryDatabase::default()),
  219. &Mnemonic::generate(12)?.to_seed_normalized(""),
  220. None,
  221. )?;
  222. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  223. let _mint_amount = wallet
  224. .mint(&mint_quote.id, SplitTarget::default(), None)
  225. .await?;
  226. let fake_description = FakeInvoiceDescription {
  227. pay_invoice_state: MeltQuoteState::Failed,
  228. check_payment_state: MeltQuoteState::Paid,
  229. pay_err: true,
  230. check_err: false,
  231. };
  232. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  233. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  234. // The melt should error at the payment invoice command
  235. let melt = wallet.melt(&melt_quote.id).await;
  236. assert!(melt.is_err());
  237. attempt_to_swap_pending(&wallet).await?;
  238. Ok(())
  239. }
  240. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  241. async fn test_fake_melt_change_in_quote() -> Result<()> {
  242. let wallet = Wallet::new(
  243. MINT_URL,
  244. CurrencyUnit::Sat,
  245. Arc::new(WalletMemoryDatabase::default()),
  246. &Mnemonic::generate(12)?.to_seed_normalized(""),
  247. None,
  248. )?;
  249. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  250. let _mint_amount = wallet
  251. .mint(&mint_quote.id, SplitTarget::default(), None)
  252. .await?;
  253. let fake_description = FakeInvoiceDescription::default();
  254. let invoice = create_fake_invoice(9000, serde_json::to_string(&fake_description).unwrap());
  255. let proofs = wallet.get_proofs().await?;
  256. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  257. let keyset = wallet.get_active_mint_keyset().await?;
  258. let premint_secrets = PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default())?;
  259. let client = HttpClient::new();
  260. let melt_response = client
  261. .post_melt(
  262. MINT_URL.parse()?,
  263. melt_quote.id.clone(),
  264. proofs.clone(),
  265. Some(premint_secrets.blinded_messages()),
  266. )
  267. .await?;
  268. assert!(melt_response.change.is_some());
  269. let check = wallet.melt_quote_status(&melt_quote.id).await?;
  270. assert_eq!(melt_response.change, check.change);
  271. Ok(())
  272. }