fake_wallet.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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_millis(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. sleep(Duration::from_millis(5)).await;
  55. let _mint_amount = wallet
  56. .mint(&mint_quote.id, SplitTarget::default(), None)
  57. .await?;
  58. let fake_description = FakeInvoiceDescription {
  59. pay_invoice_state: MeltQuoteState::Unknown,
  60. check_payment_state: MeltQuoteState::Unknown,
  61. pay_err: true,
  62. check_err: false,
  63. };
  64. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  65. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  66. // The melt should error at the payment invoice command
  67. let melt = wallet.melt(&melt_quote.id).await;
  68. assert!(melt.is_err());
  69. let fake_description = FakeInvoiceDescription {
  70. pay_invoice_state: MeltQuoteState::Failed,
  71. check_payment_state: MeltQuoteState::Failed,
  72. pay_err: true,
  73. check_err: false,
  74. };
  75. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  76. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  77. // The melt should error at the payment invoice command
  78. let melt = wallet.melt(&melt_quote.id).await;
  79. assert!(melt.is_err());
  80. // The mint should have unset proofs from pending since payment failed
  81. let all_proof = wallet.get_proofs().await?;
  82. let states = wallet.check_proofs_spent(all_proof).await?;
  83. for state in states {
  84. assert!(state.state == State::Unspent);
  85. }
  86. let wallet_bal = wallet.total_balance().await?;
  87. assert!(wallet_bal == 100.into());
  88. Ok(())
  89. }
  90. // When both the pay_invoice and check_invoice both fail
  91. // the proofs should remain as pending
  92. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  93. async fn test_fake_melt_payment_fail_and_check() -> Result<()> {
  94. let wallet = Wallet::new(
  95. MINT_URL,
  96. CurrencyUnit::Sat,
  97. Arc::new(WalletMemoryDatabase::default()),
  98. &Mnemonic::generate(12)?.to_seed_normalized(""),
  99. None,
  100. )?;
  101. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  102. sleep(Duration::from_millis(5)).await;
  103. let _mint_amount = wallet
  104. .mint(&mint_quote.id, SplitTarget::default(), None)
  105. .await?;
  106. let fake_description = FakeInvoiceDescription {
  107. pay_invoice_state: MeltQuoteState::Unknown,
  108. check_payment_state: MeltQuoteState::Unknown,
  109. pay_err: true,
  110. check_err: true,
  111. };
  112. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  113. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  114. // The melt should error at the payment invoice command
  115. let melt = wallet.melt(&melt_quote.id).await;
  116. assert!(melt.is_err());
  117. let pending = wallet
  118. .localstore
  119. .get_proofs(None, None, Some(vec![State::Pending]), None)
  120. .await?;
  121. assert!(!pending.is_empty());
  122. Ok(())
  123. }
  124. // In the case that the ln backend returns a failed status but does not error
  125. // The mint should do a second check, then remove proofs from pending
  126. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  127. async fn test_fake_melt_payment_return_fail_status() -> Result<()> {
  128. let wallet = Wallet::new(
  129. MINT_URL,
  130. CurrencyUnit::Sat,
  131. Arc::new(WalletMemoryDatabase::default()),
  132. &Mnemonic::generate(12)?.to_seed_normalized(""),
  133. None,
  134. )?;
  135. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  136. sleep(Duration::from_millis(5)).await;
  137. let _mint_amount = wallet
  138. .mint(&mint_quote.id, SplitTarget::default(), None)
  139. .await?;
  140. let fake_description = FakeInvoiceDescription {
  141. pay_invoice_state: MeltQuoteState::Failed,
  142. check_payment_state: MeltQuoteState::Failed,
  143. pay_err: false,
  144. check_err: false,
  145. };
  146. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  147. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  148. // The melt should error at the payment invoice command
  149. let melt = wallet.melt(&melt_quote.id).await;
  150. assert!(melt.is_err());
  151. let fake_description = FakeInvoiceDescription {
  152. pay_invoice_state: MeltQuoteState::Unknown,
  153. check_payment_state: MeltQuoteState::Unknown,
  154. pay_err: false,
  155. check_err: false,
  156. };
  157. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  158. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  159. // The melt should error at the payment invoice command
  160. let melt = wallet.melt(&melt_quote.id).await;
  161. assert!(melt.is_err());
  162. let pending = wallet
  163. .localstore
  164. .get_proofs(None, None, Some(vec![State::Pending]), None)
  165. .await?;
  166. assert!(pending.is_empty());
  167. Ok(())
  168. }
  169. // In the case that the ln backend returns a failed status but does not error
  170. // The mint should do a second check, then remove proofs from pending
  171. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  172. async fn test_fake_melt_payment_error_unknown() -> Result<()> {
  173. let wallet = Wallet::new(
  174. MINT_URL,
  175. CurrencyUnit::Sat,
  176. Arc::new(WalletMemoryDatabase::default()),
  177. &Mnemonic::generate(12)?.to_seed_normalized(""),
  178. None,
  179. )?;
  180. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  181. sleep(Duration::from_millis(5)).await;
  182. let _mint_amount = wallet
  183. .mint(&mint_quote.id, SplitTarget::default(), None)
  184. .await?;
  185. let fake_description = FakeInvoiceDescription {
  186. pay_invoice_state: MeltQuoteState::Failed,
  187. check_payment_state: MeltQuoteState::Unknown,
  188. pay_err: true,
  189. check_err: false,
  190. };
  191. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  192. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  193. // The melt should error at the payment invoice command
  194. let melt = wallet.melt(&melt_quote.id).await;
  195. assert!(melt.is_err());
  196. let fake_description = FakeInvoiceDescription {
  197. pay_invoice_state: MeltQuoteState::Unknown,
  198. check_payment_state: MeltQuoteState::Unknown,
  199. pay_err: true,
  200. check_err: false,
  201. };
  202. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  203. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  204. // The melt should error at the payment invoice command
  205. let melt = wallet.melt(&melt_quote.id).await;
  206. assert!(melt.is_err());
  207. let pending = wallet
  208. .localstore
  209. .get_proofs(None, None, Some(vec![State::Pending]), None)
  210. .await?;
  211. assert!(pending.is_empty());
  212. Ok(())
  213. }
  214. // In the case that the ln backend returns an err
  215. // The mint should do a second check, that returns paid
  216. // Proofs should remain pending
  217. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  218. async fn test_fake_melt_payment_err_paid() -> Result<()> {
  219. let wallet = Wallet::new(
  220. MINT_URL,
  221. CurrencyUnit::Sat,
  222. Arc::new(WalletMemoryDatabase::default()),
  223. &Mnemonic::generate(12)?.to_seed_normalized(""),
  224. None,
  225. )?;
  226. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  227. sleep(Duration::from_millis(5)).await;
  228. let _mint_amount = wallet
  229. .mint(&mint_quote.id, SplitTarget::default(), None)
  230. .await?;
  231. let fake_description = FakeInvoiceDescription {
  232. pay_invoice_state: MeltQuoteState::Failed,
  233. check_payment_state: MeltQuoteState::Paid,
  234. pay_err: true,
  235. check_err: false,
  236. };
  237. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  238. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  239. // The melt should error at the payment invoice command
  240. let melt = wallet.melt(&melt_quote.id).await;
  241. assert!(melt.is_err());
  242. attempt_to_swap_pending(&wallet).await?;
  243. Ok(())
  244. }
  245. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  246. async fn test_fake_melt_change_in_quote() -> Result<()> {
  247. let wallet = Wallet::new(
  248. MINT_URL,
  249. CurrencyUnit::Sat,
  250. Arc::new(WalletMemoryDatabase::default()),
  251. &Mnemonic::generate(12)?.to_seed_normalized(""),
  252. None,
  253. )?;
  254. let mint_quote = wallet.mint_quote(100.into(), None).await?;
  255. sleep(Duration::from_millis(5)).await;
  256. let _mint_amount = wallet
  257. .mint(&mint_quote.id, SplitTarget::default(), None)
  258. .await?;
  259. let fake_description = FakeInvoiceDescription::default();
  260. let invoice = create_fake_invoice(9000, serde_json::to_string(&fake_description).unwrap());
  261. let proofs = wallet.get_proofs().await?;
  262. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
  263. let keyset = wallet.get_active_mint_keyset().await?;
  264. let premint_secrets = PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default())?;
  265. let client = HttpClient::new();
  266. let melt_response = client
  267. .post_melt(
  268. MINT_URL.parse()?,
  269. melt_quote.id.clone(),
  270. proofs.clone(),
  271. Some(premint_secrets.blinded_messages()),
  272. )
  273. .await?;
  274. assert!(melt_response.change.is_some());
  275. let check = wallet.melt_quote_status(&melt_quote.id).await?;
  276. assert_eq!(
  277. melt_response
  278. .change
  279. .unwrap()
  280. .sort_by(|a, b| a.amount.cmp(&b.amount)),
  281. check
  282. .change
  283. .unwrap()
  284. .sort_by(|a, b| a.amount.cmp(&b.amount))
  285. );
  286. Ok(())
  287. }