|
@@ -2,27 +2,28 @@ use std::sync::Arc;
|
|
|
|
|
|
use anyhow::{bail, Result};
|
|
|
use bip39::Mnemonic;
|
|
|
+use cashu::Amount;
|
|
|
use cdk::amount::SplitTarget;
|
|
|
-use cdk::cdk_database::WalletMemoryDatabase;
|
|
|
use cdk::nuts::nut00::ProofsMethods;
|
|
|
use cdk::nuts::{
|
|
|
CurrencyUnit, MeltBolt11Request, MeltQuoteState, MintBolt11Request, PreMintSecrets, Proofs,
|
|
|
SecretKey, State, SwapRequest,
|
|
|
};
|
|
|
-use cdk::wallet::client::{HttpClient, MintConnector};
|
|
|
-use cdk::wallet::Wallet;
|
|
|
+use cdk::wallet::types::TransactionDirection;
|
|
|
+use cdk::wallet::{HttpClient, MintConnector, Wallet};
|
|
|
use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
|
|
|
use cdk_integration_tests::{attempt_to_swap_pending, wait_for_mint_to_be_paid};
|
|
|
+use cdk_sqlite::wallet::memory;
|
|
|
|
|
|
const MINT_URL: &str = "http://127.0.0.1:8086";
|
|
|
|
|
|
-// If both pay and check return pending input proofs should remain pending
|
|
|
+/// Tests that when both pay and check return pending status, input proofs should remain pending
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_tokens_pending() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -55,14 +56,14 @@ async fn test_fake_tokens_pending() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-// If the pay error fails and the check returns unknown or failed
|
|
|
-// The inputs proofs should be unset as spending
|
|
|
+/// Tests that if the pay error fails and the check returns unknown or failed,
|
|
|
+/// the input proofs should be unset as spending (returned to unspent state)
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_payment_fail() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -113,19 +114,19 @@ async fn test_fake_melt_payment_fail() -> Result<()> {
|
|
|
}
|
|
|
|
|
|
let wallet_bal = wallet.total_balance().await?;
|
|
|
- assert!(wallet_bal == 100.into());
|
|
|
+ assert_eq!(wallet_bal, 100.into());
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-// When both the pay_invoice and check_invoice both fail
|
|
|
-// the proofs should remain as pending
|
|
|
+/// Tests that when both the pay_invoice and check_invoice both fail,
|
|
|
+/// the proofs should remain in pending state
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_payment_fail_and_check() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -163,14 +164,14 @@ async fn test_fake_melt_payment_fail_and_check() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-// In the case that the ln backend returns a failed status but does not error
|
|
|
-// The mint should do a second check, then remove proofs from pending
|
|
|
+/// Tests that when the ln backend returns a failed status but does not error,
|
|
|
+/// the mint should do a second check, then remove proofs from pending state
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_payment_return_fail_status() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -223,14 +224,14 @@ async fn test_fake_melt_payment_return_fail_status() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-// In the case that the ln backend returns a failed status but does not error
|
|
|
-// The mint should do a second check, then remove proofs from pending
|
|
|
+/// Tests that when the ln backend returns an error with unknown status,
|
|
|
+/// the mint should do a second check, then remove proofs from pending state
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_payment_error_unknown() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -256,7 +257,7 @@ async fn test_fake_melt_payment_error_unknown() -> Result<()> {
|
|
|
|
|
|
// The melt should error at the payment invoice command
|
|
|
let melt = wallet.melt(&melt_quote.id).await;
|
|
|
- assert!(melt.is_err());
|
|
|
+ assert_eq!(melt.unwrap_err().to_string(), "Payment failed");
|
|
|
|
|
|
let fake_description = FakeInvoiceDescription {
|
|
|
pay_invoice_state: MeltQuoteState::Unknown,
|
|
@@ -271,7 +272,7 @@ async fn test_fake_melt_payment_error_unknown() -> Result<()> {
|
|
|
|
|
|
// The melt should error at the payment invoice command
|
|
|
let melt = wallet.melt(&melt_quote.id).await;
|
|
|
- assert!(melt.is_err());
|
|
|
+ assert_eq!(melt.unwrap_err().to_string(), "Payment failed");
|
|
|
|
|
|
let pending = wallet
|
|
|
.localstore
|
|
@@ -283,15 +284,14 @@ async fn test_fake_melt_payment_error_unknown() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-// In the case that the ln backend returns an err
|
|
|
-// The mint should do a second check, that returns paid
|
|
|
-// Proofs should remain pending
|
|
|
+/// Tests that when the ln backend returns an error but the second check returns paid,
|
|
|
+/// proofs should remain in pending state
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_payment_err_paid() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -324,12 +324,13 @@ async fn test_fake_melt_payment_err_paid() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests that change outputs in a melt quote are correctly handled
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -342,6 +343,17 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|
|
.mint(&mint_quote.id, SplitTarget::default(), None)
|
|
|
.await?;
|
|
|
|
|
|
+ let transaction = wallet
|
|
|
+ .list_transactions(Some(TransactionDirection::Incoming))
|
|
|
+ .await?
|
|
|
+ .pop()
|
|
|
+ .expect("No transaction found");
|
|
|
+ assert_eq!(wallet.mint_url, transaction.mint_url);
|
|
|
+ assert_eq!(TransactionDirection::Incoming, transaction.direction);
|
|
|
+ assert_eq!(Amount::from(100), transaction.amount);
|
|
|
+ assert_eq!(Amount::from(0), transaction.fee);
|
|
|
+ assert_eq!(CurrencyUnit::Sat, transaction.unit);
|
|
|
+
|
|
|
let fake_description = FakeInvoiceDescription::default();
|
|
|
|
|
|
let invoice = create_fake_invoice(9000, serde_json::to_string(&fake_description).unwrap());
|
|
@@ -354,13 +366,13 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|
|
|
|
|
let premint_secrets = PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default())?;
|
|
|
|
|
|
- let client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
- let melt_request = MeltBolt11Request {
|
|
|
- quote: melt_quote.id.clone(),
|
|
|
- inputs: proofs.clone(),
|
|
|
- outputs: Some(premint_secrets.blinded_messages()),
|
|
|
- };
|
|
|
+ let melt_request = MeltBolt11Request::new(
|
|
|
+ melt_quote.id.clone(),
|
|
|
+ proofs.clone(),
|
|
|
+ Some(premint_secrets.blinded_messages()),
|
|
|
+ );
|
|
|
|
|
|
let melt_response = client.post_melt(melt_request).await?;
|
|
|
|
|
@@ -374,15 +386,53 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|
|
check.sort_by(|a, b| a.amount.cmp(&b.amount));
|
|
|
|
|
|
assert_eq!(melt_change, check);
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+/// Tests that the correct database type is used based on environment variables
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
+async fn test_database_type() -> Result<()> {
|
|
|
+ // Get the database type and work dir from environment
|
|
|
+ let db_type = std::env::var("CDK_MINTD_DATABASE").expect("MINT_DATABASE env var should be set");
|
|
|
+ let work_dir =
|
|
|
+ std::env::var("CDK_MINTD_WORK_DIR").expect("CDK_MINTD_WORK_DIR env var should be set");
|
|
|
+
|
|
|
+ // Check that the correct database file exists
|
|
|
+ match db_type.as_str() {
|
|
|
+ "REDB" => {
|
|
|
+ let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.redb");
|
|
|
+ assert!(
|
|
|
+ db_path.exists(),
|
|
|
+ "Expected redb database file to exist at {:?}",
|
|
|
+ db_path
|
|
|
+ );
|
|
|
+ }
|
|
|
+ "SQLITE" => {
|
|
|
+ let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.sqlite");
|
|
|
+ assert!(
|
|
|
+ db_path.exists(),
|
|
|
+ "Expected sqlite database file to exist at {:?}",
|
|
|
+ db_path
|
|
|
+ );
|
|
|
+ }
|
|
|
+ "MEMORY" => {
|
|
|
+ // Memory database has no file to check
|
|
|
+ println!("Memory database in use - no file to check");
|
|
|
+ }
|
|
|
+ _ => bail!("Unknown database type: {}", db_type),
|
|
|
+ }
|
|
|
+
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests minting tokens with a valid witness signature
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_with_witness() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -401,12 +451,13 @@ async fn test_fake_mint_with_witness() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests that minting without a witness signature fails with the correct error
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_without_witness() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -415,7 +466,7 @@ async fn test_fake_mint_without_witness() -> Result<()> {
|
|
|
|
|
|
wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?;
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
|
|
|
@@ -437,12 +488,13 @@ async fn test_fake_mint_without_witness() -> Result<()> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Tests that minting with an incorrect witness signature fails with the correct error
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_with_wrong_witness() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -451,7 +503,7 @@ async fn test_fake_mint_with_wrong_witness() -> Result<()> {
|
|
|
|
|
|
wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?;
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
|
|
|
@@ -477,12 +529,13 @@ async fn test_fake_mint_with_wrong_witness() -> Result<()> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/// Tests that attempting to mint more tokens than allowed by the quote fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_inflated() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -510,7 +563,7 @@ async fn test_fake_mint_inflated() -> Result<()> {
|
|
|
if let Some(secret_key) = quote_info.secret_key {
|
|
|
mint_request.sign(secret_key)?;
|
|
|
}
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
let response = http_client.post_mint(mint_request.clone()).await;
|
|
|
|
|
@@ -529,12 +582,13 @@ async fn test_fake_mint_inflated() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests that attempting to mint with multiple currency units in the same request fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_multiple_units() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -550,7 +604,7 @@ async fn test_fake_mint_multiple_units() -> Result<()> {
|
|
|
let wallet_usd = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Usd,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -580,7 +634,7 @@ async fn test_fake_mint_multiple_units() -> Result<()> {
|
|
|
if let Some(secret_key) = quote_info.secret_key {
|
|
|
mint_request.sign(secret_key)?;
|
|
|
}
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
let response = http_client.post_mint(mint_request.clone()).await;
|
|
|
|
|
@@ -599,12 +653,13 @@ async fn test_fake_mint_multiple_units() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests that attempting to swap tokens with multiple currency units fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_multiple_unit_swap() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -618,7 +673,7 @@ async fn test_fake_mint_multiple_unit_swap() -> Result<()> {
|
|
|
let wallet_usd = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Usd,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -642,12 +697,9 @@ async fn test_fake_mint_multiple_unit_swap() -> Result<()> {
|
|
|
let pre_mint =
|
|
|
PreMintSecrets::random(active_keyset_id, inputs.total_amount()?, &SplitTarget::None)?;
|
|
|
|
|
|
- let swap_request = SwapRequest {
|
|
|
- inputs,
|
|
|
- outputs: pre_mint.blinded_messages(),
|
|
|
- };
|
|
|
+ let swap_request = SwapRequest::new(inputs, pre_mint.blinded_messages());
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -679,12 +731,9 @@ async fn test_fake_mint_multiple_unit_swap() -> Result<()> {
|
|
|
|
|
|
usd_outputs.append(&mut sat_outputs);
|
|
|
|
|
|
- let swap_request = SwapRequest {
|
|
|
- inputs,
|
|
|
- outputs: usd_outputs,
|
|
|
- };
|
|
|
+ let swap_request = SwapRequest::new(inputs, usd_outputs);
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -703,12 +752,13 @@ async fn test_fake_mint_multiple_unit_swap() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+/// Tests that attempting to melt tokens with multiple currency units fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_multiple_unit_melt() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -727,7 +777,7 @@ async fn test_fake_mint_multiple_unit_melt() -> Result<()> {
|
|
|
let wallet_usd = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Usd,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -752,13 +802,9 @@ async fn test_fake_mint_multiple_unit_melt() -> Result<()> {
|
|
|
let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
|
|
|
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
|
|
|
|
|
|
- let melt_request = MeltBolt11Request {
|
|
|
- quote: melt_quote.id,
|
|
|
- inputs,
|
|
|
- outputs: None,
|
|
|
- };
|
|
|
+ let melt_request = MeltBolt11Request::new(melt_quote.id, inputs, None);
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_melt(melt_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -796,13 +842,10 @@ async fn test_fake_mint_multiple_unit_melt() -> Result<()> {
|
|
|
usd_outputs.append(&mut sat_outputs);
|
|
|
let quote = wallet.melt_quote(invoice.to_string(), None).await?;
|
|
|
|
|
|
- let melt_request = MeltBolt11Request {
|
|
|
- quote: quote.id,
|
|
|
- inputs,
|
|
|
- outputs: Some(usd_outputs),
|
|
|
- };
|
|
|
+ let melt_request = MeltBolt11Request::new(quote.id, inputs, Some(usd_outputs));
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
let response = http_client.post_melt(melt_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -821,13 +864,13 @@ async fn test_fake_mint_multiple_unit_melt() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-/// Test swap where input unit != output unit
|
|
|
+/// Tests that swapping tokens where input unit doesn't match output unit fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_input_output_mismatch() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -841,11 +884,10 @@ async fn test_fake_mint_input_output_mismatch() -> Result<()> {
|
|
|
let wallet_usd = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Usd,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
|
-
|
|
|
let usd_active_keyset_id = wallet_usd.get_active_mint_keyset().await?.id;
|
|
|
|
|
|
let inputs = proofs;
|
|
@@ -856,12 +898,9 @@ async fn test_fake_mint_input_output_mismatch() -> Result<()> {
|
|
|
&SplitTarget::None,
|
|
|
)?;
|
|
|
|
|
|
- let swap_request = SwapRequest {
|
|
|
- inputs,
|
|
|
- outputs: pre_mint.blinded_messages(),
|
|
|
- };
|
|
|
+ let swap_request = SwapRequest::new(inputs, pre_mint.blinded_messages());
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -877,13 +916,13 @@ async fn test_fake_mint_input_output_mismatch() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-/// Test swap where input is less the output
|
|
|
+/// Tests that swapping tokens where output amount is greater than input amount fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_swap_inflated() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -896,17 +935,78 @@ async fn test_fake_mint_swap_inflated() -> Result<()> {
|
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
|
let pre_mint = PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None)?;
|
|
|
|
|
|
- let swap_request = SwapRequest {
|
|
|
- inputs: proofs,
|
|
|
- outputs: pre_mint.blinded_messages(),
|
|
|
- };
|
|
|
+ let swap_request = SwapRequest::new(proofs, pre_mint.blinded_messages());
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
+
|
|
|
+ match response {
|
|
|
+ Err(err) => match err {
|
|
|
+ cdk::Error::TransactionUnbalanced(_, _, _) => (),
|
|
|
+ err => {
|
|
|
+ bail!("Wrong mint error returned: {}", err.to_string());
|
|
|
+ }
|
|
|
+ },
|
|
|
+ Ok(_) => {
|
|
|
+ bail!("Should not have allowed to mint with multiple units");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+/// Tests that tokens cannot be spent again after a failed swap attempt
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
+async fn test_fake_mint_swap_spend_after_fail() -> Result<()> {
|
|
|
+ let wallet = Wallet::new(
|
|
|
+ MINT_URL,
|
|
|
+ CurrencyUnit::Sat,
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
+ &Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
+ None,
|
|
|
+ )?;
|
|
|
+
|
|
|
+ let mint_quote = wallet.mint_quote(100.into(), None).await?;
|
|
|
+
|
|
|
+ wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?;
|
|
|
+
|
|
|
+ let proofs = wallet.mint(&mint_quote.id, SplitTarget::None, None).await?;
|
|
|
+ let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
|
+
|
|
|
+ let pre_mint = PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None)?;
|
|
|
+
|
|
|
+ let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
+
|
|
|
+ assert!(response.is_ok());
|
|
|
+
|
|
|
+ let pre_mint = PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None)?;
|
|
|
+
|
|
|
+ let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
|
Err(err) => match err {
|
|
|
cdk::Error::TransactionUnbalanced(_, _, _) => (),
|
|
|
+ err => bail!("Wrong mint error returned expected TransactionUnbalanced, got: {err}"),
|
|
|
+ },
|
|
|
+ Ok(_) => bail!("Should not have allowed swap with unbalanced"),
|
|
|
+ }
|
|
|
+
|
|
|
+ let pre_mint = PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None)?;
|
|
|
+
|
|
|
+ let swap_request = SwapRequest::new(proofs, pre_mint.blinded_messages());
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
+
|
|
|
+ match response {
|
|
|
+ Err(err) => match err {
|
|
|
+ cdk::Error::TokenAlreadySpent => (),
|
|
|
err => {
|
|
|
bail!("Wrong mint error returned: {}", err.to_string());
|
|
|
}
|
|
@@ -919,13 +1019,79 @@ async fn test_fake_mint_swap_inflated() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-/// Test swap where input unit != output unit
|
|
|
+/// Tests that tokens cannot be melted after a failed swap attempt
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
+async fn test_fake_mint_melt_spend_after_fail() -> Result<()> {
|
|
|
+ let wallet = Wallet::new(
|
|
|
+ MINT_URL,
|
|
|
+ CurrencyUnit::Sat,
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
+ &Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
+ None,
|
|
|
+ )?;
|
|
|
+
|
|
|
+ let mint_quote = wallet.mint_quote(100.into(), None).await?;
|
|
|
+
|
|
|
+ wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?;
|
|
|
+
|
|
|
+ let proofs = wallet.mint(&mint_quote.id, SplitTarget::None, None).await?;
|
|
|
+ let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
|
+
|
|
|
+ let pre_mint = PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::None)?;
|
|
|
+
|
|
|
+ let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
+
|
|
|
+ assert!(response.is_ok());
|
|
|
+
|
|
|
+ let pre_mint = PreMintSecrets::random(active_keyset_id, 101.into(), &SplitTarget::None)?;
|
|
|
+
|
|
|
+ let swap_request = SwapRequest::new(proofs.clone(), pre_mint.blinded_messages());
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
+
|
|
|
+ match response {
|
|
|
+ Err(err) => match err {
|
|
|
+ cdk::Error::TransactionUnbalanced(_, _, _) => (),
|
|
|
+ err => bail!("Wrong mint error returned expected TransactionUnbalanced, got: {err}"),
|
|
|
+ },
|
|
|
+ Ok(_) => bail!("Should not have allowed swap with unbalanced"),
|
|
|
+ }
|
|
|
+
|
|
|
+ let input_amount: u64 = proofs.total_amount()?.into();
|
|
|
+ let invoice = create_fake_invoice((input_amount - 1) * 1000, "".to_string());
|
|
|
+ let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
|
|
|
+
|
|
|
+ let melt_request = MeltBolt11Request::new(melt_quote.id, proofs, None);
|
|
|
+
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
+ let response = http_client.post_melt(melt_request.clone()).await;
|
|
|
+
|
|
|
+ match response {
|
|
|
+ Err(err) => match err {
|
|
|
+ cdk::Error::TokenAlreadySpent => (),
|
|
|
+ err => {
|
|
|
+ bail!("Wrong mint error returned: {}", err.to_string());
|
|
|
+ }
|
|
|
+ },
|
|
|
+ Ok(_) => {
|
|
|
+ bail!("Should not have allowed to melt with multiple units");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+/// Tests that attempting to swap with duplicate proofs fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_duplicate_proofs_swap() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -943,12 +1109,9 @@ async fn test_fake_mint_duplicate_proofs_swap() -> Result<()> {
|
|
|
let pre_mint =
|
|
|
PreMintSecrets::random(active_keyset_id, inputs.total_amount()?, &SplitTarget::None)?;
|
|
|
|
|
|
- let swap_request = SwapRequest {
|
|
|
- inputs: inputs.clone(),
|
|
|
- outputs: pre_mint.blinded_messages(),
|
|
|
- };
|
|
|
+ let swap_request = SwapRequest::new(inputs.clone(), pre_mint.blinded_messages());
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -970,9 +1133,9 @@ async fn test_fake_mint_duplicate_proofs_swap() -> Result<()> {
|
|
|
|
|
|
let outputs = vec![blinded_message[0].clone(), blinded_message[0].clone()];
|
|
|
|
|
|
- let swap_request = SwapRequest { inputs, outputs };
|
|
|
+ let swap_request = SwapRequest::new(inputs, outputs);
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_swap(swap_request.clone()).await;
|
|
|
|
|
|
match response {
|
|
@@ -993,13 +1156,13 @@ async fn test_fake_mint_duplicate_proofs_swap() -> Result<()> {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
-/// Test duplicate proofs in melt
|
|
|
+/// Tests that attempting to melt with duplicate proofs fails
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
async fn test_fake_mint_duplicate_proofs_melt() -> Result<()> {
|
|
|
let wallet = Wallet::new(
|
|
|
MINT_URL,
|
|
|
CurrencyUnit::Sat,
|
|
|
- Arc::new(WalletMemoryDatabase::default()),
|
|
|
+ Arc::new(memory::empty().await?),
|
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
None,
|
|
|
)?;
|
|
@@ -1016,13 +1179,9 @@ async fn test_fake_mint_duplicate_proofs_melt() -> Result<()> {
|
|
|
|
|
|
let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
|
|
|
|
|
|
- let melt_request = MeltBolt11Request {
|
|
|
- quote: melt_quote.id,
|
|
|
- inputs,
|
|
|
- outputs: None,
|
|
|
- };
|
|
|
+ let melt_request = MeltBolt11Request::new(melt_quote.id, inputs, None);
|
|
|
|
|
|
- let http_client = HttpClient::new(MINT_URL.parse()?);
|
|
|
+ let http_client = HttpClient::new(MINT_URL.parse()?, None);
|
|
|
let response = http_client.post_melt(melt_request.clone()).await;
|
|
|
|
|
|
match response {
|