fake_wallet.rs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. //! Fake Wallet Integration Tests
  2. //!
  3. //! This file contains tests for the fake wallet backend functionality.
  4. //! The fake wallet simulates Lightning Network behavior for testing purposes,
  5. //! allowing verification of mint behavior in various payment scenarios without
  6. //! requiring a real Lightning node.
  7. //!
  8. //! Test Scenarios:
  9. //! - Pending payment states and proof handling
  10. //! - Payment failure cases and proof state management
  11. //! - Change output verification in melt operations
  12. //! - Witness signature validation
  13. //! - Cross-unit transaction validation
  14. //! - Overflow and balance validation
  15. //! - Duplicate proof detection
  16. use std::sync::Arc;
  17. use std::time::Duration;
  18. use bip39::Mnemonic;
  19. use cashu::Amount;
  20. use cdk::amount::SplitTarget;
  21. use cdk::nuts::nut00::ProofsMethods;
  22. use cdk::nuts::{
  23. CurrencyUnit, MeltQuoteState, MeltRequest, MintRequest, PreMintSecrets, Proofs, SecretKey,
  24. State, SwapRequest,
  25. };
  26. use cdk::wallet::types::TransactionDirection;
  27. use cdk::wallet::{HttpClient, MintConnector, Wallet};
  28. use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
  29. use cdk_integration_tests::attempt_to_swap_pending;
  30. use cdk_sqlite::wallet::memory;
  31. const MINT_URL: &str = "http://127.0.0.1:8086";
  32. /// Tests that when both pay and check return pending status, input proofs should remain pending
  33. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  34. async fn test_fake_tokens_pending() {
  35. let wallet = Wallet::new(
  36. MINT_URL,
  37. CurrencyUnit::Sat,
  38. Arc::new(memory::empty().await.unwrap()),
  39. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  40. None,
  41. )
  42. .expect("failed to create new wallet");
  43. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  44. wallet
  45. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  46. .await
  47. .unwrap();
  48. let _mint_amount = wallet
  49. .mint(&mint_quote.id, SplitTarget::default(), None)
  50. .await
  51. .unwrap();
  52. let fake_description = FakeInvoiceDescription {
  53. pay_invoice_state: MeltQuoteState::Pending,
  54. check_payment_state: MeltQuoteState::Pending,
  55. pay_err: false,
  56. check_err: false,
  57. };
  58. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  59. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  60. let melt = wallet.melt(&melt_quote.id).await;
  61. assert!(melt.is_err());
  62. attempt_to_swap_pending(&wallet).await.unwrap();
  63. }
  64. /// Tests that if the pay error fails and the check returns unknown or failed,
  65. /// the input proofs should be unset as spending (returned to unspent state)
  66. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  67. async fn test_fake_melt_payment_fail() {
  68. let wallet = Wallet::new(
  69. MINT_URL,
  70. CurrencyUnit::Sat,
  71. Arc::new(memory::empty().await.unwrap()),
  72. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  73. None,
  74. )
  75. .expect("Failed to create new wallet");
  76. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  77. wallet
  78. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  79. .await
  80. .unwrap();
  81. let _mint_amount = wallet
  82. .mint(&mint_quote.id, SplitTarget::default(), None)
  83. .await
  84. .unwrap();
  85. let fake_description = FakeInvoiceDescription {
  86. pay_invoice_state: MeltQuoteState::Unknown,
  87. check_payment_state: MeltQuoteState::Unknown,
  88. pay_err: true,
  89. check_err: false,
  90. };
  91. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  92. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  93. // The melt should error at the payment invoice command
  94. let melt = wallet.melt(&melt_quote.id).await;
  95. assert!(melt.is_err());
  96. let fake_description = FakeInvoiceDescription {
  97. pay_invoice_state: MeltQuoteState::Failed,
  98. check_payment_state: MeltQuoteState::Failed,
  99. pay_err: true,
  100. check_err: false,
  101. };
  102. let invoice = create_fake_invoice(1000, serde_json::to_string(&fake_description).unwrap());
  103. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  104. // The melt should error at the payment invoice command
  105. let melt = wallet.melt(&melt_quote.id).await;
  106. assert!(melt.is_err());
  107. // The mint should have unset proofs from pending since payment failed
  108. let all_proof = wallet.get_unspent_proofs().await.unwrap();
  109. let states = wallet.check_proofs_spent(all_proof).await.unwrap();
  110. for state in states {
  111. assert!(state.state == State::Unspent);
  112. }
  113. let wallet_bal = wallet.total_balance().await.unwrap();
  114. assert_eq!(wallet_bal, 100.into());
  115. }
  116. /// Tests that when both the pay_invoice and check_invoice both fail,
  117. /// the proofs should remain in pending state
  118. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  119. async fn test_fake_melt_payment_fail_and_check() {
  120. let wallet = Wallet::new(
  121. MINT_URL,
  122. CurrencyUnit::Sat,
  123. Arc::new(memory::empty().await.unwrap()),
  124. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  125. None,
  126. )
  127. .expect("Failed to create new wallet");
  128. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  129. wallet
  130. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  131. .await
  132. .unwrap();
  133. let _mint_amount = wallet
  134. .mint(&mint_quote.id, SplitTarget::default(), None)
  135. .await
  136. .unwrap();
  137. let fake_description = FakeInvoiceDescription {
  138. pay_invoice_state: MeltQuoteState::Unknown,
  139. check_payment_state: MeltQuoteState::Unknown,
  140. pay_err: true,
  141. check_err: true,
  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.unwrap();
  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 pending = wallet
  149. .localstore
  150. .get_proofs(None, None, Some(vec![State::Pending]), None)
  151. .await
  152. .unwrap();
  153. assert!(!pending.is_empty());
  154. }
  155. /// Tests that when the ln backend returns a failed status but does not error,
  156. /// the mint should do a second check, then remove proofs from pending state
  157. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  158. async fn test_fake_melt_payment_return_fail_status() {
  159. let wallet = Wallet::new(
  160. MINT_URL,
  161. CurrencyUnit::Sat,
  162. Arc::new(memory::empty().await.unwrap()),
  163. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  164. None,
  165. )
  166. .expect("Failed to create new wallet");
  167. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  168. wallet
  169. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  170. .await
  171. .unwrap();
  172. let _mint_amount = wallet
  173. .mint(&mint_quote.id, SplitTarget::default(), None)
  174. .await
  175. .unwrap();
  176. let fake_description = FakeInvoiceDescription {
  177. pay_invoice_state: MeltQuoteState::Failed,
  178. check_payment_state: MeltQuoteState::Failed,
  179. pay_err: false,
  180. check_err: false,
  181. };
  182. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  183. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  184. // The melt should error at the payment invoice command
  185. let melt = wallet.melt(&melt_quote.id).await;
  186. assert!(melt.is_err());
  187. let fake_description = FakeInvoiceDescription {
  188. pay_invoice_state: MeltQuoteState::Unknown,
  189. check_payment_state: MeltQuoteState::Unknown,
  190. pay_err: false,
  191. check_err: false,
  192. };
  193. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  194. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  195. // The melt should error at the payment invoice command
  196. let melt = wallet.melt(&melt_quote.id).await;
  197. assert!(melt.is_err());
  198. let pending = wallet
  199. .localstore
  200. .get_proofs(None, None, Some(vec![State::Pending]), None)
  201. .await
  202. .unwrap();
  203. assert!(pending.is_empty());
  204. }
  205. /// Tests that when the ln backend returns an error with unknown status,
  206. /// the mint should do a second check, then remove proofs from pending state
  207. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  208. async fn test_fake_melt_payment_error_unknown() {
  209. let wallet = Wallet::new(
  210. MINT_URL,
  211. CurrencyUnit::Sat,
  212. Arc::new(memory::empty().await.unwrap()),
  213. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  214. None,
  215. )
  216. .unwrap();
  217. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  218. wallet
  219. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  220. .await
  221. .unwrap();
  222. let _mint_amount = wallet
  223. .mint(&mint_quote.id, SplitTarget::default(), None)
  224. .await
  225. .unwrap();
  226. let fake_description = FakeInvoiceDescription {
  227. pay_invoice_state: MeltQuoteState::Failed,
  228. check_payment_state: MeltQuoteState::Unknown,
  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.unwrap();
  234. // The melt should error at the payment invoice command
  235. let melt = wallet.melt(&melt_quote.id).await;
  236. assert_eq!(melt.unwrap_err().to_string(), "Payment failed");
  237. let fake_description = FakeInvoiceDescription {
  238. pay_invoice_state: MeltQuoteState::Unknown,
  239. check_payment_state: MeltQuoteState::Unknown,
  240. pay_err: true,
  241. check_err: false,
  242. };
  243. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  244. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  245. // The melt should error at the payment invoice command
  246. let melt = wallet.melt(&melt_quote.id).await;
  247. assert_eq!(melt.unwrap_err().to_string(), "Payment failed");
  248. let pending = wallet
  249. .localstore
  250. .get_proofs(None, None, Some(vec![State::Pending]), None)
  251. .await
  252. .unwrap();
  253. assert!(pending.is_empty());
  254. }
  255. /// Tests that when the ln backend returns an error but the second check returns paid,
  256. /// proofs should remain in pending state
  257. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  258. async fn test_fake_melt_payment_err_paid() {
  259. let wallet = Wallet::new(
  260. MINT_URL,
  261. CurrencyUnit::Sat,
  262. Arc::new(memory::empty().await.unwrap()),
  263. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  264. None,
  265. )
  266. .expect("Failed to create new wallet");
  267. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  268. wallet
  269. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  270. .await
  271. .unwrap();
  272. let _mint_amount = wallet
  273. .mint(&mint_quote.id, SplitTarget::default(), None)
  274. .await
  275. .unwrap();
  276. let fake_description = FakeInvoiceDescription {
  277. pay_invoice_state: MeltQuoteState::Failed,
  278. check_payment_state: MeltQuoteState::Paid,
  279. pay_err: true,
  280. check_err: false,
  281. };
  282. let invoice = create_fake_invoice(7000, serde_json::to_string(&fake_description).unwrap());
  283. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  284. // The melt should error at the payment invoice command
  285. let melt = wallet.melt(&melt_quote.id).await;
  286. assert!(melt.is_err());
  287. attempt_to_swap_pending(&wallet).await.unwrap();
  288. }
  289. /// Tests that change outputs in a melt quote are correctly handled
  290. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  291. async fn test_fake_melt_change_in_quote() {
  292. let wallet = Wallet::new(
  293. MINT_URL,
  294. CurrencyUnit::Sat,
  295. Arc::new(memory::empty().await.unwrap()),
  296. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  297. None,
  298. )
  299. .expect("Failed to create new wallet");
  300. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  301. wallet
  302. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  303. .await
  304. .unwrap();
  305. let _mint_amount = wallet
  306. .mint(&mint_quote.id, SplitTarget::default(), None)
  307. .await
  308. .unwrap();
  309. let transaction = wallet
  310. .list_transactions(Some(TransactionDirection::Incoming))
  311. .await
  312. .unwrap()
  313. .pop()
  314. .expect("No transaction found");
  315. assert_eq!(wallet.mint_url, transaction.mint_url);
  316. assert_eq!(TransactionDirection::Incoming, transaction.direction);
  317. assert_eq!(Amount::from(100), transaction.amount);
  318. assert_eq!(Amount::from(0), transaction.fee);
  319. assert_eq!(CurrencyUnit::Sat, transaction.unit);
  320. let fake_description = FakeInvoiceDescription::default();
  321. let invoice = create_fake_invoice(9000, serde_json::to_string(&fake_description).unwrap());
  322. let proofs = wallet.get_unspent_proofs().await.unwrap();
  323. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  324. let keyset = wallet.fetch_active_keyset().await.unwrap();
  325. let premint_secrets =
  326. PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default()).unwrap();
  327. let client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  328. let melt_request = MeltRequest::new(
  329. melt_quote.id.clone(),
  330. proofs.clone(),
  331. Some(premint_secrets.blinded_messages()),
  332. );
  333. let melt_response = client.post_melt(melt_request).await.unwrap();
  334. assert!(melt_response.change.is_some());
  335. let check = wallet.melt_quote_status(&melt_quote.id).await.unwrap();
  336. let mut melt_change = melt_response.change.unwrap();
  337. melt_change.sort_by(|a, b| a.amount.cmp(&b.amount));
  338. let mut check = check.change.unwrap();
  339. check.sort_by(|a, b| a.amount.cmp(&b.amount));
  340. assert_eq!(melt_change, check);
  341. }
  342. /// Tests minting tokens with a valid witness signature
  343. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  344. async fn test_fake_mint_with_witness() {
  345. let wallet = Wallet::new(
  346. MINT_URL,
  347. CurrencyUnit::Sat,
  348. Arc::new(memory::empty().await.unwrap()),
  349. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  350. None,
  351. )
  352. .expect("failed to create new wallet");
  353. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  354. wallet
  355. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  356. .await
  357. .unwrap();
  358. let proofs = wallet
  359. .mint(&mint_quote.id, SplitTarget::default(), None)
  360. .await
  361. .unwrap();
  362. let mint_amount = proofs.total_amount().unwrap();
  363. assert!(mint_amount == 100.into());
  364. }
  365. /// Tests that minting without a witness signature fails with the correct error
  366. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  367. async fn test_fake_mint_without_witness() {
  368. let wallet = Wallet::new(
  369. MINT_URL,
  370. CurrencyUnit::Sat,
  371. Arc::new(memory::empty().await.unwrap()),
  372. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  373. None,
  374. )
  375. .expect("failed to create new wallet");
  376. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  377. wallet
  378. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  379. .await
  380. .unwrap();
  381. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  382. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  383. let premint_secrets =
  384. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
  385. let request = MintRequest {
  386. quote: mint_quote.id,
  387. outputs: premint_secrets.blinded_messages(),
  388. signature: None,
  389. };
  390. let response = http_client.post_mint(request.clone()).await;
  391. match response {
  392. Err(cdk::error::Error::SignatureMissingOrInvalid) => {} //pass
  393. Err(err) => panic!("Wrong mint response for minting without witness: {}", err),
  394. Ok(_) => panic!("Minting should not have succeed without a witness"),
  395. }
  396. }
  397. /// Tests that minting with an incorrect witness signature fails with the correct error
  398. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  399. async fn test_fake_mint_with_wrong_witness() {
  400. let wallet = Wallet::new(
  401. MINT_URL,
  402. CurrencyUnit::Sat,
  403. Arc::new(memory::empty().await.unwrap()),
  404. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  405. None,
  406. )
  407. .expect("failed to create new wallet");
  408. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  409. wallet
  410. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  411. .await
  412. .unwrap();
  413. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  414. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  415. let premint_secrets =
  416. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
  417. let mut request = MintRequest {
  418. quote: mint_quote.id,
  419. outputs: premint_secrets.blinded_messages(),
  420. signature: None,
  421. };
  422. let secret_key = SecretKey::generate();
  423. request
  424. .sign(secret_key)
  425. .expect("failed to sign the mint request");
  426. let response = http_client.post_mint(request.clone()).await;
  427. match response {
  428. Err(cdk::error::Error::SignatureMissingOrInvalid) => {} //pass
  429. Err(err) => panic!("Wrong mint response for minting without witness: {}", err),
  430. Ok(_) => panic!("Minting should not have succeed without a witness"),
  431. }
  432. }
  433. /// Tests that attempting to mint more tokens than allowed by the quote fails
  434. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  435. async fn test_fake_mint_inflated() {
  436. let wallet = Wallet::new(
  437. MINT_URL,
  438. CurrencyUnit::Sat,
  439. Arc::new(memory::empty().await.unwrap()),
  440. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  441. None,
  442. )
  443. .expect("failed to create new wallet");
  444. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  445. wallet
  446. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  447. .await
  448. .unwrap();
  449. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  450. let pre_mint =
  451. PreMintSecrets::random(active_keyset_id, 500.into(), &SplitTarget::None).unwrap();
  452. let quote_info = wallet
  453. .localstore
  454. .get_mint_quote(&mint_quote.id)
  455. .await
  456. .unwrap()
  457. .expect("there is a quote");
  458. let mut mint_request = MintRequest {
  459. quote: mint_quote.id,
  460. outputs: pre_mint.blinded_messages(),
  461. signature: None,
  462. };
  463. if let Some(secret_key) = quote_info.secret_key {
  464. mint_request
  465. .sign(secret_key)
  466. .expect("failed to sign the mint request");
  467. }
  468. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  469. let response = http_client.post_mint(mint_request.clone()).await;
  470. match response {
  471. Err(err) => match err {
  472. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  473. err => {
  474. panic!("Wrong mint error returned: {}", err);
  475. }
  476. },
  477. Ok(_) => {
  478. panic!("Should not have allowed second payment");
  479. }
  480. }
  481. }
  482. /// Tests that attempting to mint with multiple currency units in the same request fails
  483. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  484. async fn test_fake_mint_multiple_units() {
  485. let wallet = Wallet::new(
  486. MINT_URL,
  487. CurrencyUnit::Sat,
  488. Arc::new(memory::empty().await.unwrap()),
  489. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  490. None,
  491. )
  492. .expect("failed to create new wallet");
  493. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  494. wallet
  495. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  496. .await
  497. .unwrap();
  498. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  499. let pre_mint = PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
  500. let wallet_usd = Wallet::new(
  501. MINT_URL,
  502. CurrencyUnit::Usd,
  503. Arc::new(memory::empty().await.unwrap()),
  504. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  505. None,
  506. )
  507. .expect("failed to create new wallet");
  508. let active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
  509. let usd_pre_mint =
  510. PreMintSecrets::random(active_keyset_id, 50.into(), &SplitTarget::None).unwrap();
  511. let quote_info = wallet
  512. .localstore
  513. .get_mint_quote(&mint_quote.id)
  514. .await
  515. .unwrap()
  516. .expect("there is a quote");
  517. let mut sat_outputs = pre_mint.blinded_messages();
  518. let mut usd_outputs = usd_pre_mint.blinded_messages();
  519. sat_outputs.append(&mut usd_outputs);
  520. let mut mint_request = MintRequest {
  521. quote: mint_quote.id,
  522. outputs: sat_outputs,
  523. signature: None,
  524. };
  525. if let Some(secret_key) = quote_info.secret_key {
  526. mint_request
  527. .sign(secret_key)
  528. .expect("failed to sign the mint request");
  529. }
  530. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  531. let response = http_client.post_mint(mint_request.clone()).await;
  532. match response {
  533. Err(err) => match err {
  534. cdk::Error::MultipleUnits => (),
  535. err => {
  536. panic!("Wrong mint error returned: {}", err);
  537. }
  538. },
  539. Ok(_) => {
  540. panic!("Should not have allowed to mint with multiple units");
  541. }
  542. }
  543. }
  544. /// Tests that attempting to swap tokens with multiple currency units fails
  545. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  546. async fn test_fake_mint_multiple_unit_swap() {
  547. let wallet = Wallet::new(
  548. MINT_URL,
  549. CurrencyUnit::Sat,
  550. Arc::new(memory::empty().await.unwrap()),
  551. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  552. None,
  553. )
  554. .expect("failed to create new wallet");
  555. wallet.refresh_keysets().await.unwrap();
  556. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  557. wallet
  558. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  559. .await
  560. .unwrap();
  561. let proofs = wallet
  562. .mint(&mint_quote.id, SplitTarget::None, None)
  563. .await
  564. .unwrap();
  565. let wallet_usd = Wallet::new(
  566. MINT_URL,
  567. CurrencyUnit::Usd,
  568. Arc::new(memory::empty().await.unwrap()),
  569. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  570. None,
  571. )
  572. .expect("failed to create usd wallet");
  573. wallet_usd.refresh_keysets().await.unwrap();
  574. let mint_quote = wallet_usd.mint_quote(100.into(), None).await.unwrap();
  575. wallet
  576. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  577. .await
  578. .unwrap();
  579. let usd_proofs = wallet_usd
  580. .mint(&mint_quote.id, SplitTarget::None, None)
  581. .await
  582. .unwrap();
  583. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  584. {
  585. let inputs: Proofs = vec![
  586. proofs.first().expect("There is a proof").clone(),
  587. usd_proofs.first().expect("There is a proof").clone(),
  588. ];
  589. let pre_mint = PreMintSecrets::random(
  590. active_keyset_id,
  591. inputs.total_amount().unwrap(),
  592. &SplitTarget::None,
  593. )
  594. .unwrap();
  595. let swap_request = SwapRequest::new(inputs, pre_mint.blinded_messages());
  596. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  597. let response = http_client.post_swap(swap_request.clone()).await;
  598. match response {
  599. Err(err) => match err {
  600. cdk::Error::MultipleUnits => (),
  601. err => {
  602. panic!("Wrong mint error returned: {}", err);
  603. }
  604. },
  605. Ok(_) => {
  606. panic!("Should not have allowed to mint with multiple units");
  607. }
  608. }
  609. }
  610. {
  611. let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
  612. let inputs: Proofs = proofs.into_iter().take(2).collect();
  613. let total_inputs = inputs.total_amount().unwrap();
  614. let half = total_inputs / 2.into();
  615. let usd_pre_mint =
  616. PreMintSecrets::random(usd_active_keyset_id, half, &SplitTarget::None).unwrap();
  617. let pre_mint =
  618. PreMintSecrets::random(active_keyset_id, total_inputs - half, &SplitTarget::None)
  619. .unwrap();
  620. let mut usd_outputs = usd_pre_mint.blinded_messages();
  621. let mut sat_outputs = pre_mint.blinded_messages();
  622. usd_outputs.append(&mut sat_outputs);
  623. let swap_request = SwapRequest::new(inputs, usd_outputs);
  624. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  625. let response = http_client.post_swap(swap_request.clone()).await;
  626. match response {
  627. Err(err) => match err {
  628. cdk::Error::MultipleUnits => (),
  629. err => {
  630. panic!("Wrong mint error returned: {}", err);
  631. }
  632. },
  633. Ok(_) => {
  634. panic!("Should not have allowed to mint with multiple units");
  635. }
  636. }
  637. }
  638. }
  639. /// Tests that attempting to melt tokens with multiple currency units fails
  640. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  641. async fn test_fake_mint_multiple_unit_melt() {
  642. let wallet = Wallet::new(
  643. MINT_URL,
  644. CurrencyUnit::Sat,
  645. Arc::new(memory::empty().await.unwrap()),
  646. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  647. None,
  648. )
  649. .expect("failed to create new wallet");
  650. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  651. wallet
  652. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  653. .await
  654. .unwrap();
  655. let proofs = wallet
  656. .mint(&mint_quote.id, SplitTarget::None, None)
  657. .await
  658. .unwrap();
  659. println!("Minted sat");
  660. let wallet_usd = Wallet::new(
  661. MINT_URL,
  662. CurrencyUnit::Usd,
  663. Arc::new(memory::empty().await.unwrap()),
  664. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  665. None,
  666. )
  667. .expect("failed to create new wallet");
  668. let mint_quote = wallet_usd.mint_quote(100.into(), None).await.unwrap();
  669. println!("Minted quote usd");
  670. wallet
  671. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  672. .await
  673. .unwrap();
  674. let usd_proofs = wallet_usd
  675. .mint(&mint_quote.id, SplitTarget::None, None)
  676. .await
  677. .unwrap();
  678. {
  679. let inputs: Proofs = vec![
  680. proofs.first().expect("There is a proof").clone(),
  681. usd_proofs.first().expect("There is a proof").clone(),
  682. ];
  683. let input_amount: u64 = inputs.total_amount().unwrap().into();
  684. let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
  685. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  686. let melt_request = MeltRequest::new(melt_quote.id, inputs, None);
  687. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  688. let response = http_client.post_melt(melt_request.clone()).await;
  689. match response {
  690. Err(err) => match err {
  691. cdk::Error::MultipleUnits => (),
  692. err => {
  693. panic!("Wrong mint error returned: {}", err);
  694. }
  695. },
  696. Ok(_) => {
  697. panic!("Should not have allowed to melt with multiple units");
  698. }
  699. }
  700. }
  701. {
  702. let inputs: Proofs = vec![proofs.first().expect("There is a proof").clone()];
  703. let input_amount: u64 = inputs.total_amount().unwrap().into();
  704. let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
  705. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  706. let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
  707. let usd_pre_mint = PreMintSecrets::random(
  708. usd_active_keyset_id,
  709. inputs.total_amount().unwrap() + 100.into(),
  710. &SplitTarget::None,
  711. )
  712. .unwrap();
  713. let pre_mint =
  714. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
  715. let mut usd_outputs = usd_pre_mint.blinded_messages();
  716. let mut sat_outputs = pre_mint.blinded_messages();
  717. usd_outputs.append(&mut sat_outputs);
  718. let quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  719. let melt_request = MeltRequest::new(quote.id, inputs, Some(usd_outputs));
  720. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  721. let response = http_client.post_melt(melt_request.clone()).await;
  722. match response {
  723. Err(err) => match err {
  724. cdk::Error::MultipleUnits => (),
  725. err => {
  726. panic!("Wrong mint error returned: {}", err);
  727. }
  728. },
  729. Ok(_) => {
  730. panic!("Should not have allowed to melt with multiple units");
  731. }
  732. }
  733. }
  734. }
  735. /// Tests that swapping tokens where input unit doesn't match output unit fails
  736. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  737. async fn test_fake_mint_input_output_mismatch() {
  738. let wallet = Wallet::new(
  739. MINT_URL,
  740. CurrencyUnit::Sat,
  741. Arc::new(memory::empty().await.unwrap()),
  742. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  743. None,
  744. )
  745. .expect("failed to create new wallet");
  746. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  747. wallet
  748. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  749. .await
  750. .unwrap();
  751. let proofs = wallet
  752. .mint(&mint_quote.id, SplitTarget::None, None)
  753. .await
  754. .unwrap();
  755. let wallet_usd = Wallet::new(
  756. MINT_URL,
  757. CurrencyUnit::Usd,
  758. Arc::new(memory::empty().await.unwrap()),
  759. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  760. None,
  761. )
  762. .expect("failed to create new usd wallet");
  763. let usd_active_keyset_id = wallet_usd.fetch_active_keyset().await.unwrap().id;
  764. let inputs = proofs;
  765. let pre_mint = PreMintSecrets::random(
  766. usd_active_keyset_id,
  767. inputs.total_amount().unwrap(),
  768. &SplitTarget::None,
  769. )
  770. .unwrap();
  771. let swap_request = SwapRequest::new(inputs, pre_mint.blinded_messages());
  772. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  773. let response = http_client.post_swap(swap_request.clone()).await;
  774. match response {
  775. Err(err) => match err {
  776. cdk::Error::UnitMismatch => (),
  777. err => panic!("Wrong error returned: {}", err),
  778. },
  779. Ok(_) => {
  780. panic!("Should not have allowed to mint with multiple units");
  781. }
  782. }
  783. }
  784. /// Tests that swapping tokens where output amount is greater than input amount fails
  785. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  786. async fn test_fake_mint_swap_inflated() {
  787. let wallet = Wallet::new(
  788. MINT_URL,
  789. CurrencyUnit::Sat,
  790. Arc::new(memory::empty().await.unwrap()),
  791. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  792. None,
  793. )
  794. .expect("failed to create new wallet");
  795. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  796. wallet
  797. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  798. .await
  799. .unwrap();
  800. let proofs = wallet
  801. .mint(&mint_quote.id, SplitTarget::None, None)
  802. .await
  803. .unwrap();
  804. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  805. let pre_mint =
  806. PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None).unwrap();
  807. let swap_request = SwapRequest::new(proofs, pre_mint.blinded_messages());
  808. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  809. let response = http_client.post_swap(swap_request.clone()).await;
  810. match response {
  811. Err(err) => match err {
  812. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  813. err => {
  814. panic!("Wrong mint error returned: {}", err);
  815. }
  816. },
  817. Ok(_) => {
  818. panic!("Should not have allowed to mint with multiple units");
  819. }
  820. }
  821. }
  822. /// Tests that tokens cannot be spent again after a failed swap attempt
  823. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  824. async fn test_fake_mint_swap_spend_after_fail() {
  825. let wallet = Wallet::new(
  826. MINT_URL,
  827. CurrencyUnit::Sat,
  828. Arc::new(memory::empty().await.unwrap()),
  829. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  830. None,
  831. )
  832. .expect("failed to create new wallet");
  833. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  834. wallet
  835. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  836. .await
  837. .unwrap();
  838. let proofs = wallet
  839. .mint(&mint_quote.id, SplitTarget::None, None)
  840. .await
  841. .unwrap();
  842. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  843. let pre_mint =
  844. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
  845. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  846. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  847. let response = http_client.post_swap(swap_request.clone()).await;
  848. assert!(response.is_ok());
  849. let pre_mint =
  850. PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None).unwrap();
  851. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  852. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  853. let response = http_client.post_swap(swap_request.clone()).await;
  854. match response {
  855. Err(err) => match err {
  856. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  857. err => panic!("Wrong mint error returned expected TransactionUnbalanced, got: {err}"),
  858. },
  859. Ok(_) => panic!("Should not have allowed swap with unbalanced"),
  860. }
  861. let pre_mint =
  862. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
  863. let swap_request = SwapRequest::new(proofs, pre_mint.blinded_messages());
  864. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  865. let response = http_client.post_swap(swap_request.clone()).await;
  866. match response {
  867. Err(err) => match err {
  868. cdk::Error::TokenAlreadySpent => (),
  869. err => {
  870. panic!("Wrong mint error returned: {}", err);
  871. }
  872. },
  873. Ok(_) => {
  874. panic!("Should not have allowed to mint with multiple units");
  875. }
  876. }
  877. }
  878. /// Tests that tokens cannot be melted after a failed swap attempt
  879. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  880. async fn test_fake_mint_melt_spend_after_fail() {
  881. let wallet = Wallet::new(
  882. MINT_URL,
  883. CurrencyUnit::Sat,
  884. Arc::new(memory::empty().await.unwrap()),
  885. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  886. None,
  887. )
  888. .expect("failed to create new wallet");
  889. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  890. wallet
  891. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  892. .await
  893. .unwrap();
  894. let proofs = wallet
  895. .mint(&mint_quote.id, SplitTarget::None, None)
  896. .await
  897. .unwrap();
  898. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  899. let pre_mint =
  900. PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None).unwrap();
  901. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  902. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  903. let response = http_client.post_swap(swap_request.clone()).await;
  904. assert!(response.is_ok());
  905. let pre_mint =
  906. PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None).unwrap();
  907. let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
  908. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  909. let response = http_client.post_swap(swap_request.clone()).await;
  910. match response {
  911. Err(err) => match err {
  912. cdk::Error::TransactionUnbalanced(_, _, _) => (),
  913. err => panic!("Wrong mint error returned expected TransactionUnbalanced, got: {err}"),
  914. },
  915. Ok(_) => panic!("Should not have allowed swap with unbalanced"),
  916. }
  917. let input_amount: u64 = proofs.total_amount().unwrap().into();
  918. let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
  919. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  920. let melt_request = MeltRequest::new(melt_quote.id, proofs, None);
  921. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  922. let response = http_client.post_melt(melt_request.clone()).await;
  923. match response {
  924. Err(err) => match err {
  925. cdk::Error::TokenAlreadySpent => (),
  926. err => {
  927. panic!("Wrong mint error returned: {}", err);
  928. }
  929. },
  930. Ok(_) => {
  931. panic!("Should not have allowed to melt with multiple units");
  932. }
  933. }
  934. }
  935. /// Tests that attempting to swap with duplicate proofs fails
  936. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  937. async fn test_fake_mint_duplicate_proofs_swap() {
  938. let wallet = Wallet::new(
  939. MINT_URL,
  940. CurrencyUnit::Sat,
  941. Arc::new(memory::empty().await.unwrap()),
  942. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  943. None,
  944. )
  945. .expect("failed to create new wallet");
  946. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  947. wallet
  948. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  949. .await
  950. .unwrap();
  951. let proofs = wallet
  952. .mint(&mint_quote.id, SplitTarget::None, None)
  953. .await
  954. .unwrap();
  955. let active_keyset_id = wallet.fetch_active_keyset().await.unwrap().id;
  956. let inputs = vec![proofs[0].clone(), proofs[0].clone()];
  957. let pre_mint = PreMintSecrets::random(
  958. active_keyset_id,
  959. inputs.total_amount().unwrap(),
  960. &SplitTarget::None,
  961. )
  962. .unwrap();
  963. let swap_request = SwapRequest::new(inputs.clone(), pre_mint.blinded_messages());
  964. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  965. let response = http_client.post_swap(swap_request.clone()).await;
  966. match response {
  967. Err(err) => match err {
  968. cdk::Error::DuplicateInputs => (),
  969. err => {
  970. panic!(
  971. "Wrong mint error returned, expected duplicate inputs: {}",
  972. err
  973. );
  974. }
  975. },
  976. Ok(_) => {
  977. panic!("Should not have allowed duplicate inputs");
  978. }
  979. }
  980. let blinded_message = pre_mint.blinded_messages();
  981. let inputs = vec![proofs[0].clone()];
  982. let outputs = vec![blinded_message[0].clone(), blinded_message[0].clone()];
  983. let swap_request = SwapRequest::new(inputs, outputs);
  984. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  985. let response = http_client.post_swap(swap_request.clone()).await;
  986. match response {
  987. Err(err) => match err {
  988. cdk::Error::DuplicateOutputs => (),
  989. err => {
  990. panic!(
  991. "Wrong mint error returned, expected duplicate outputs: {}",
  992. err
  993. );
  994. }
  995. },
  996. Ok(_) => {
  997. panic!("Should not have allow duplicate inputs");
  998. }
  999. }
  1000. }
  1001. /// Tests that attempting to melt with duplicate proofs fails
  1002. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  1003. async fn test_fake_mint_duplicate_proofs_melt() {
  1004. let wallet = Wallet::new(
  1005. MINT_URL,
  1006. CurrencyUnit::Sat,
  1007. Arc::new(memory::empty().await.unwrap()),
  1008. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  1009. None,
  1010. )
  1011. .expect("failed to create new wallet");
  1012. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  1013. wallet
  1014. .wait_for_payment(&mint_quote, Duration::from_secs(60))
  1015. .await
  1016. .unwrap();
  1017. let proofs = wallet
  1018. .mint(&mint_quote.id, SplitTarget::None, None)
  1019. .await
  1020. .unwrap();
  1021. let inputs = vec![proofs[0].clone(), proofs[0].clone()];
  1022. let invoice = create_fake_invoice(7000, "".to_string());
  1023. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  1024. let melt_request = MeltRequest::new(melt_quote.id, inputs, None);
  1025. let http_client = HttpClient::new(MINT_URL.parse().unwrap(), None);
  1026. let response = http_client.post_melt(melt_request.clone()).await;
  1027. match response {
  1028. Err(err) => match err {
  1029. cdk::Error::DuplicateInputs => (),
  1030. err => {
  1031. panic!("Wrong mint error returned: {}", err);
  1032. }
  1033. },
  1034. Ok(_) => {
  1035. panic!("Should not have allow duplicate inputs");
  1036. }
  1037. }
  1038. }