|
@@ -5,6 +5,7 @@ use std::time::Duration;
|
|
|
|
|
|
use anyhow::{bail, Result};
|
|
use anyhow::{bail, Result};
|
|
use bip39::Mnemonic;
|
|
use bip39::Mnemonic;
|
|
|
|
+use cashu::{MeltOptions, Mpp};
|
|
use cdk::amount::{Amount, SplitTarget};
|
|
use cdk::amount::{Amount, SplitTarget};
|
|
use cdk::cdk_database::WalletMemoryDatabase;
|
|
use cdk::cdk_database::WalletMemoryDatabase;
|
|
use cdk::nuts::nut00::ProofsMethods;
|
|
use cdk::nuts::nut00::ProofsMethods;
|
|
@@ -19,7 +20,7 @@ use cdk_integration_tests::init_regtest::{
|
|
get_mint_url, get_mint_ws_url, LND_RPC_ADDR, LND_TWO_RPC_ADDR,
|
|
get_mint_url, get_mint_ws_url, LND_RPC_ADDR, LND_TWO_RPC_ADDR,
|
|
};
|
|
};
|
|
use cdk_integration_tests::wait_for_mint_to_be_paid;
|
|
use cdk_integration_tests::wait_for_mint_to_be_paid;
|
|
-use futures::{SinkExt, StreamExt};
|
|
|
|
|
|
+use futures::{join, SinkExt, StreamExt};
|
|
use lightning_invoice::Bolt11Invoice;
|
|
use lightning_invoice::Bolt11Invoice;
|
|
use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient};
|
|
use ln_regtest_rs::ln_client::{ClnClient, LightningClient, LndClient};
|
|
use ln_regtest_rs::InvoiceStatus;
|
|
use ln_regtest_rs::InvoiceStatus;
|
|
@@ -79,14 +80,14 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
|
|
let lnd_client = init_lnd_client().await;
|
|
let lnd_client = init_lnd_client().await;
|
|
|
|
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
None,
|
|
None,
|
|
)?;
|
|
)?;
|
|
|
|
|
|
- let (ws_stream, _) = connect_async(get_mint_ws_url())
|
|
|
|
|
|
+ let (ws_stream, _) = connect_async(get_mint_ws_url("0"))
|
|
.await
|
|
.await
|
|
.expect("Failed to connect");
|
|
.expect("Failed to connect");
|
|
let (mut write, mut reader) = ws_stream.split();
|
|
let (mut write, mut reader) = ws_stream.split();
|
|
@@ -164,7 +165,7 @@ async fn test_regtest_mint_melt() -> Result<()> {
|
|
let lnd_client = init_lnd_client().await;
|
|
let lnd_client = init_lnd_client().await;
|
|
|
|
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
@@ -198,7 +199,7 @@ async fn test_restore() -> Result<()> {
|
|
|
|
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&seed,
|
|
&seed,
|
|
@@ -218,7 +219,7 @@ async fn test_restore() -> Result<()> {
|
|
assert!(wallet.total_balance().await? == 100.into());
|
|
assert!(wallet.total_balance().await? == 100.into());
|
|
|
|
|
|
let wallet_2 = Wallet::new(
|
|
let wallet_2 = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&seed,
|
|
&seed,
|
|
@@ -257,7 +258,7 @@ async fn test_pay_invoice_twice() -> Result<()> {
|
|
|
|
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&seed,
|
|
&seed,
|
|
@@ -316,7 +317,7 @@ async fn test_internal_payment() -> Result<()> {
|
|
|
|
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&seed,
|
|
&seed,
|
|
@@ -338,7 +339,7 @@ async fn test_internal_payment() -> Result<()> {
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
|
|
|
|
|
let wallet_2 = Wallet::new(
|
|
let wallet_2 = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&seed,
|
|
&seed,
|
|
@@ -360,7 +361,7 @@ async fn test_internal_payment() -> Result<()> {
|
|
.await
|
|
.await
|
|
.unwrap();
|
|
.unwrap();
|
|
|
|
|
|
- let check_paid = match get_mint_port() {
|
|
|
|
|
|
+ let check_paid = match get_mint_port("0") {
|
|
8085 => {
|
|
8085 => {
|
|
let cln_one_dir = get_cln_dir("one");
|
|
let cln_one_dir = get_cln_dir("one");
|
|
let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?;
|
|
let cln_client = ClnClient::new(cln_one_dir.clone(), None).await?;
|
|
@@ -411,7 +412,7 @@ async fn test_cached_mint() -> Result<()> {
|
|
let lnd_client = init_lnd_client().await;
|
|
let lnd_client = init_lnd_client().await;
|
|
|
|
|
|
let wallet = Wallet::new(
|
|
let wallet = Wallet::new(
|
|
- &get_mint_url(),
|
|
|
|
|
|
+ &get_mint_url("0"),
|
|
CurrencyUnit::Sat,
|
|
CurrencyUnit::Sat,
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
Arc::new(WalletMemoryDatabase::default()),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
&Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
@@ -438,7 +439,7 @@ async fn test_cached_mint() -> Result<()> {
|
|
}
|
|
}
|
|
|
|
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
- let http_client = HttpClient::new(get_mint_url().as_str().parse()?);
|
|
|
|
|
|
+ let http_client = HttpClient::new(get_mint_url("0").as_str().parse()?);
|
|
let premint_secrets =
|
|
let premint_secrets =
|
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
|
PreMintSecrets::random(active_keyset_id, 100.into(), &SplitTarget::default()).unwrap();
|
|
|
|
|
|
@@ -458,3 +459,84 @@ async fn test_cached_mint() -> Result<()> {
|
|
assert!(response == response1);
|
|
assert!(response == response1);
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
|
+async fn test_multimint_melt() -> Result<()> {
|
|
|
|
+ let lnd_client = init_lnd_client().await;
|
|
|
|
+
|
|
|
|
+ let wallet1 = Wallet::new(
|
|
|
|
+ &get_mint_url("0"),
|
|
|
|
+ CurrencyUnit::Sat,
|
|
|
|
+ Arc::new(WalletMemoryDatabase::default()),
|
|
|
|
+ &Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
|
+ None,
|
|
|
|
+ )?;
|
|
|
|
+ let wallet2 = Wallet::new(
|
|
|
|
+ &get_mint_url("1"),
|
|
|
|
+ CurrencyUnit::Sat,
|
|
|
|
+ Arc::new(WalletMemoryDatabase::default()),
|
|
|
|
+ &Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
|
+ None,
|
|
|
|
+ )?;
|
|
|
|
+
|
|
|
|
+ let mint_amount = Amount::from(100);
|
|
|
|
+
|
|
|
|
+ // Fund the wallets
|
|
|
|
+ let quote = wallet1.mint_quote(mint_amount, None).await?;
|
|
|
|
+ lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
|
|
+ loop {
|
|
|
|
+ let quote_status = wallet1.mint_quote_state("e.id).await?;
|
|
|
|
+ if quote_status.state == MintQuoteState::Paid {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ tracing::debug!("Quote not yet paid");
|
|
|
|
+ }
|
|
|
|
+ wallet1
|
|
|
|
+ .mint("e.id, SplitTarget::default(), None)
|
|
|
|
+ .await?;
|
|
|
|
+
|
|
|
|
+ let quote = wallet2.mint_quote(mint_amount, None).await?;
|
|
|
|
+ lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
|
|
+ loop {
|
|
|
|
+ let quote_status = wallet2.mint_quote_state("e.id).await?;
|
|
|
|
+ if quote_status.state == MintQuoteState::Paid {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ tracing::debug!("Quote not yet paid");
|
|
|
|
+ }
|
|
|
|
+ wallet2
|
|
|
|
+ .mint("e.id, SplitTarget::default(), None)
|
|
|
|
+ .await?;
|
|
|
|
+
|
|
|
|
+ // Get an invoice
|
|
|
|
+ let invoice = lnd_client.create_invoice(Some(50)).await?;
|
|
|
|
+
|
|
|
|
+ // Get multi-part melt quotes
|
|
|
|
+ let melt_options = MeltOptions::Mpp {
|
|
|
|
+ mpp: Mpp {
|
|
|
|
+ amount: Amount::from(25000),
|
|
|
|
+ },
|
|
|
|
+ };
|
|
|
|
+ let quote_1 = wallet1
|
|
|
|
+ .melt_quote(invoice.clone(), Some(melt_options))
|
|
|
|
+ .await
|
|
|
|
+ .expect("Could not get melt quote");
|
|
|
|
+ let quote_2 = wallet2
|
|
|
|
+ .melt_quote(invoice.clone(), Some(melt_options))
|
|
|
|
+ .await
|
|
|
|
+ .expect("Could not get melt quote");
|
|
|
|
+
|
|
|
|
+ // Multimint pay invoice
|
|
|
|
+ let result1 = wallet1.melt("e_1.id);
|
|
|
|
+ let result2 = wallet2.melt("e_2.id);
|
|
|
|
+ let result = join!(result1, result2);
|
|
|
|
+
|
|
|
|
+ // Unpack results
|
|
|
|
+ let result1 = result.0.unwrap();
|
|
|
|
+ let result2 = result.1.unwrap();
|
|
|
|
+
|
|
|
|
+ // Check
|
|
|
|
+ assert!(result1.state == result2.state);
|
|
|
|
+ assert!(result1.state == MeltQuoteState::Paid);
|
|
|
|
+ Ok(())
|
|
|
|
+}
|