fake_wallet.rs 41 KB

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