mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-23 07:35:03 +01:00
Add support for pure integration tests (#458)
* Embed mint_url as a field of HttpClient * Create pure integration tests * DirectMintConnection: convert between String and Uuid
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -780,6 +780,7 @@ name = "cdk-integration-tests"
|
|||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"bip39",
|
"bip39",
|
||||||
"cdk",
|
"cdk",
|
||||||
|
|||||||
@@ -144,16 +144,17 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
let mints = localstore.get_mints().await?;
|
let mints = localstore.get_mints().await?;
|
||||||
|
|
||||||
for (mint, _) in mints {
|
for (mint_url, _) in mints {
|
||||||
let mut wallet = Wallet::new(
|
let mut wallet = Wallet::new(
|
||||||
&mint.to_string(),
|
&mint_url.to_string(),
|
||||||
cdk::nuts::CurrencyUnit::Sat,
|
cdk::nuts::CurrencyUnit::Sat,
|
||||||
localstore.clone(),
|
localstore.clone(),
|
||||||
&mnemonic.to_seed_normalized(""),
|
&mnemonic.to_seed_normalized(""),
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
if let Some(proxy_url) = args.proxy.as_ref() {
|
if let Some(proxy_url) = args.proxy.as_ref() {
|
||||||
wallet.set_client(HttpClient::with_proxy(proxy_url.clone(), None, true)?);
|
let http_client = HttpClient::with_proxy(mint_url, proxy_url.clone(), None, true)?;
|
||||||
|
wallet.set_client(Arc::from(http_client));
|
||||||
}
|
}
|
||||||
|
|
||||||
wallets.push(wallet);
|
wallets.push(wallet);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use cdk::mint_url::MintUrl;
|
use cdk::mint_url::MintUrl;
|
||||||
use cdk::wallet::client::HttpClientMethods;
|
use cdk::wallet::client::MintConnector;
|
||||||
use cdk::HttpClient;
|
use cdk::HttpClient;
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
@@ -11,14 +11,13 @@ pub struct MintInfoSubcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mint_info(proxy: Option<Url>, sub_command_args: &MintInfoSubcommand) -> Result<()> {
|
pub async fn mint_info(proxy: Option<Url>, sub_command_args: &MintInfoSubcommand) -> Result<()> {
|
||||||
|
let mint_url = sub_command_args.mint_url.clone();
|
||||||
let client = match proxy {
|
let client = match proxy {
|
||||||
Some(proxy) => HttpClient::with_proxy(proxy, None, true)?,
|
Some(proxy) => HttpClient::with_proxy(mint_url, proxy, None, true)?,
|
||||||
None => HttpClient::new(),
|
None => HttpClient::new(mint_url),
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = client
|
let info = client.get_mint_info().await?;
|
||||||
.get_mint_info(sub_command_args.mint_url.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("{:#?}", info);
|
println!("{:#?}", info);
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ getrandom = { version = "0.2", features = ["js"] }
|
|||||||
instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
|
instant = { version = "0.1", features = ["wasm-bindgen", "inaccurate"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
async-trait = "0.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
bip39 = { version = "2.0", features = ["rand"] }
|
bip39 = { version = "2.0", features = ["rand"] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -10,12 +11,13 @@ use cdk::cdk_database::mint_memory::MintMemoryDatabase;
|
|||||||
use cdk::cdk_lightning::MintLightning;
|
use cdk::cdk_lightning::MintLightning;
|
||||||
use cdk::dhke::construct_proofs;
|
use cdk::dhke::construct_proofs;
|
||||||
use cdk::mint::FeeReserve;
|
use cdk::mint::FeeReserve;
|
||||||
|
use cdk::mint_url::MintUrl;
|
||||||
use cdk::nuts::{
|
use cdk::nuts::{
|
||||||
CurrencyUnit, Id, KeySet, MintBolt11Request, MintInfo, MintQuoteBolt11Request, MintQuoteState,
|
CurrencyUnit, Id, KeySet, MintBolt11Request, MintInfo, MintQuoteBolt11Request, MintQuoteState,
|
||||||
Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
|
Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
|
||||||
};
|
};
|
||||||
use cdk::types::{LnKey, QuoteTTL};
|
use cdk::types::{LnKey, QuoteTTL};
|
||||||
use cdk::wallet::client::{HttpClient, HttpClientMethods};
|
use cdk::wallet::client::{HttpClient, MintConnector};
|
||||||
use cdk::{Mint, Wallet};
|
use cdk::{Mint, Wallet};
|
||||||
use cdk_fake_wallet::FakeWallet;
|
use cdk_fake_wallet::FakeWallet;
|
||||||
use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
|
use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
|
||||||
@@ -155,7 +157,7 @@ pub async fn mint_proofs(
|
|||||||
println!("Minting for ecash");
|
println!("Minting for ecash");
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let wallet_client = HttpClient::new();
|
let wallet_client = HttpClient::new(MintUrl::from_str(mint_url)?);
|
||||||
|
|
||||||
let request = MintQuoteBolt11Request {
|
let request = MintQuoteBolt11Request {
|
||||||
amount,
|
amount,
|
||||||
@@ -163,15 +165,13 @@ pub async fn mint_proofs(
|
|||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mint_quote = wallet_client
|
let mint_quote = wallet_client.post_mint_quote(request).await?;
|
||||||
.post_mint_quote(mint_url.parse()?, request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
println!("Please pay: {}", mint_quote.request);
|
println!("Please pay: {}", mint_quote.request);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let status = wallet_client
|
let status = wallet_client
|
||||||
.get_mint_quote_status(mint_url.parse()?, &mint_quote.quote)
|
.get_mint_quote_status(&mint_quote.quote)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if status.state == MintQuoteState::Paid {
|
if status.state == MintQuoteState::Paid {
|
||||||
@@ -189,7 +189,7 @@ pub async fn mint_proofs(
|
|||||||
outputs: premint_secrets.blinded_messages(),
|
outputs: premint_secrets.blinded_messages(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mint_response = wallet_client.post_mint(mint_url.parse()?, request).await?;
|
let mint_response = wallet_client.post_mint(request).await?;
|
||||||
|
|
||||||
let pre_swap_proofs = construct_proofs(
|
let pre_swap_proofs = construct_proofs(
|
||||||
mint_response.signatures,
|
mint_response.signatures,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use cdk::cdk_database::WalletMemoryDatabase;
|
|||||||
use cdk::nuts::{
|
use cdk::nuts::{
|
||||||
CurrencyUnit, MeltBolt11Request, MeltQuoteState, MintQuoteState, PreMintSecrets, State,
|
CurrencyUnit, MeltBolt11Request, MeltQuoteState, MintQuoteState, PreMintSecrets, State,
|
||||||
};
|
};
|
||||||
use cdk::wallet::client::{HttpClient, HttpClientMethods};
|
use cdk::wallet::client::{HttpClient, MintConnector};
|
||||||
use cdk::wallet::Wallet;
|
use cdk::wallet::Wallet;
|
||||||
use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
|
use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
|
||||||
use cdk_integration_tests::attempt_to_swap_pending;
|
use cdk_integration_tests::attempt_to_swap_pending;
|
||||||
@@ -354,7 +354,7 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|||||||
|
|
||||||
let premint_secrets = PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default())?;
|
let premint_secrets = PreMintSecrets::random(keyset.id, 100.into(), &SplitTarget::default())?;
|
||||||
|
|
||||||
let client = HttpClient::new();
|
let client = HttpClient::new(MINT_URL.parse()?);
|
||||||
|
|
||||||
let melt_request = MeltBolt11Request {
|
let melt_request = MeltBolt11Request {
|
||||||
quote: melt_quote.id.clone(),
|
quote: melt_quote.id.clone(),
|
||||||
@@ -362,7 +362,7 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
|
|||||||
outputs: Some(premint_secrets.blinded_messages()),
|
outputs: Some(premint_secrets.blinded_messages()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let melt_response = client.post_melt(MINT_URL.parse()?, melt_request).await?;
|
let melt_response = client.post_melt(melt_request).await?;
|
||||||
|
|
||||||
assert!(melt_response.change.is_some());
|
assert!(melt_response.change.is_some());
|
||||||
|
|
||||||
|
|||||||
266
crates/cdk-integration-tests/tests/integration_tests_pure.rs
Normal file
266
crates/cdk-integration-tests/tests/integration_tests_pure.rs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
mod integration_tests_pure {
|
||||||
|
use std::assert_eq;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cdk::amount::SplitTarget;
|
||||||
|
use cdk::cdk_database::mint_memory::MintMemoryDatabase;
|
||||||
|
use cdk::cdk_database::WalletMemoryDatabase;
|
||||||
|
use cdk::nuts::{
|
||||||
|
CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysetResponse,
|
||||||
|
MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
|
||||||
|
MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response,
|
||||||
|
MintQuoteState, Nuts, RestoreRequest, RestoreResponse, SwapRequest, SwapResponse,
|
||||||
|
};
|
||||||
|
use cdk::types::QuoteTTL;
|
||||||
|
use cdk::util::unix_time;
|
||||||
|
use cdk::wallet::client::MintConnector;
|
||||||
|
use cdk::{Amount, Error, Mint, Wallet};
|
||||||
|
use cdk_integration_tests::create_backends_fake_wallet;
|
||||||
|
use rand::random;
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
struct DirectMintConnection {
|
||||||
|
mint: Arc<Mint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for DirectMintConnection {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"DirectMintConnection {{ mint_info: {:?} }}",
|
||||||
|
self.mint.mint_info
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the generic [MintConnector] (i.e. use the interface that expects to communicate
|
||||||
|
/// to a generic mint, where we don't know that quote ID's are [Uuid]s) for [DirectMintConnection],
|
||||||
|
/// where we know we're dealing with a mint that uses [Uuid]s for quotes.
|
||||||
|
/// Convert the requests and responses between the [String] and [Uuid] variants as necessary.
|
||||||
|
#[async_trait]
|
||||||
|
impl MintConnector for DirectMintConnection {
|
||||||
|
async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> {
|
||||||
|
self.mint.pubkeys().await.map(|pks| pks.keysets)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mint_keyset(&self, keyset_id: Id) -> Result<KeySet, Error> {
|
||||||
|
self.mint
|
||||||
|
.keyset(&keyset_id)
|
||||||
|
.await
|
||||||
|
.and_then(|res| res.ok_or(Error::UnknownKeySet))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> {
|
||||||
|
self.mint.keysets().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_mint_quote(
|
||||||
|
&self,
|
||||||
|
request: MintQuoteBolt11Request,
|
||||||
|
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
||||||
|
self.mint
|
||||||
|
.get_mint_bolt11_quote(request)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mint_quote_status(
|
||||||
|
&self,
|
||||||
|
quote_id: &str,
|
||||||
|
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
||||||
|
let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
|
||||||
|
self.mint
|
||||||
|
.check_mint_quote("e_id_uuid)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_mint(
|
||||||
|
&self,
|
||||||
|
request: MintBolt11Request<String>,
|
||||||
|
) -> Result<MintBolt11Response, Error> {
|
||||||
|
let request_uuid = request.try_into().unwrap();
|
||||||
|
self.mint.process_mint_request(request_uuid).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_melt_quote(
|
||||||
|
&self,
|
||||||
|
request: MeltQuoteBolt11Request,
|
||||||
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
|
self.mint
|
||||||
|
.get_melt_bolt11_quote(&request)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_melt_quote_status(
|
||||||
|
&self,
|
||||||
|
quote_id: &str,
|
||||||
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
|
let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
|
||||||
|
self.mint
|
||||||
|
.check_melt_quote("e_id_uuid)
|
||||||
|
.await
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_melt(
|
||||||
|
&self,
|
||||||
|
request: MeltBolt11Request<String>,
|
||||||
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
|
let request_uuid = request.try_into().unwrap();
|
||||||
|
self.mint.melt_bolt11(&request_uuid).await.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> {
|
||||||
|
self.mint.process_swap_request(swap_request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mint_info(&self) -> Result<MintInfo, Error> {
|
||||||
|
Ok(self.mint.mint_info().clone().time(unix_time()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_check_state(
|
||||||
|
&self,
|
||||||
|
request: CheckStateRequest,
|
||||||
|
) -> Result<CheckStateResponse, Error> {
|
||||||
|
self.mint.check_state(&request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
|
||||||
|
self.mint.restore(request).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mint_connector(mint: Arc<Mint>) -> DirectMintConnection {
|
||||||
|
DirectMintConnection { mint }
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_and_start_test_mint() -> anyhow::Result<Arc<Mint>> {
|
||||||
|
let fee: u64 = 0;
|
||||||
|
let mut supported_units = HashMap::new();
|
||||||
|
supported_units.insert(CurrencyUnit::Sat, (fee, 32));
|
||||||
|
|
||||||
|
let nuts = Nuts::new()
|
||||||
|
.nut07(true)
|
||||||
|
.nut08(true)
|
||||||
|
.nut09(true)
|
||||||
|
.nut10(true)
|
||||||
|
.nut11(true)
|
||||||
|
.nut12(true)
|
||||||
|
.nut14(true);
|
||||||
|
|
||||||
|
let mint_info = MintInfo::new().nuts(nuts);
|
||||||
|
|
||||||
|
let quote_ttl = QuoteTTL::new(10000, 10000);
|
||||||
|
|
||||||
|
let mint_url = "http://aaa";
|
||||||
|
|
||||||
|
let seed = random::<[u8; 32]>();
|
||||||
|
let mint: Mint = Mint::new(
|
||||||
|
mint_url,
|
||||||
|
&seed,
|
||||||
|
mint_info,
|
||||||
|
quote_ttl,
|
||||||
|
Arc::new(MintMemoryDatabase::default()),
|
||||||
|
create_backends_fake_wallet(),
|
||||||
|
supported_units,
|
||||||
|
HashMap::new(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mint_arc = Arc::new(mint);
|
||||||
|
|
||||||
|
let mint_arc_clone = Arc::clone(&mint_arc);
|
||||||
|
let shutdown = Arc::new(Notify::new());
|
||||||
|
tokio::spawn({
|
||||||
|
let shutdown = Arc::clone(&shutdown);
|
||||||
|
async move { mint_arc_clone.wait_for_paid_invoices(shutdown).await }
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(mint_arc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_test_wallet_for_mint(mint: Arc<Mint>) -> anyhow::Result<Arc<Wallet>> {
|
||||||
|
let connector = get_mint_connector(mint);
|
||||||
|
|
||||||
|
let seed = random::<[u8; 32]>();
|
||||||
|
let mint_url = connector.mint.mint_url.to_string();
|
||||||
|
let unit = CurrencyUnit::Sat;
|
||||||
|
|
||||||
|
let localstore = WalletMemoryDatabase::default();
|
||||||
|
let mut wallet = Wallet::new(&mint_url, unit, Arc::new(localstore), &seed, None)?;
|
||||||
|
|
||||||
|
wallet.set_client(Arc::from(connector));
|
||||||
|
|
||||||
|
Ok(Arc::new(wallet))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a mint quote for the given amount and checks its state in a loop. Returns when
|
||||||
|
/// amount is minted.
|
||||||
|
async fn receive(wallet: Arc<Wallet>, amount: u64) -> anyhow::Result<Amount> {
|
||||||
|
let desired_amount = Amount::from(amount);
|
||||||
|
let quote = wallet.mint_quote(desired_amount, None).await?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let status = wallet.mint_quote_state("e.id).await?;
|
||||||
|
if status.state == MintQuoteState::Paid {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet
|
||||||
|
.mint("e.id, SplitTarget::default(), None)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod nut03 {
|
||||||
|
use cdk::nuts::nut00::ProofsMethods;
|
||||||
|
use cdk::wallet::SendKind;
|
||||||
|
|
||||||
|
use crate::integration_tests_pure::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_swap_to_send() -> anyhow::Result<()> {
|
||||||
|
let mint_bob = create_and_start_test_mint().await?;
|
||||||
|
let wallet_alice = create_test_wallet_for_mint(mint_bob.clone())?;
|
||||||
|
|
||||||
|
// Alice gets 64 sats
|
||||||
|
receive(wallet_alice.clone(), 64).await?;
|
||||||
|
let balance_alice = wallet_alice.total_balance().await?;
|
||||||
|
assert_eq!(Amount::from(64), balance_alice);
|
||||||
|
|
||||||
|
// Alice wants to send 40 sats, which internally swaps
|
||||||
|
let token = wallet_alice
|
||||||
|
.send(
|
||||||
|
Amount::from(40),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
&SplitTarget::None,
|
||||||
|
&SendKind::OnlineExact,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
assert_eq!(Amount::from(40), token.proofs().total_amount()?);
|
||||||
|
assert_eq!(Amount::from(24), wallet_alice.total_balance().await?);
|
||||||
|
|
||||||
|
// Alice sends cashu, Carol receives
|
||||||
|
let wallet_carol = create_test_wallet_for_mint(mint_bob.clone())?;
|
||||||
|
let received_amount = wallet_carol
|
||||||
|
.receive_proofs(token.proofs(), SplitTarget::None, &[], &[])
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(Amount::from(40), received_amount);
|
||||||
|
assert_eq!(Amount::from(40), wallet_carol.total_balance().await?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ use cdk::nuts::{
|
|||||||
CurrencyUnit, MeltQuoteState, MintBolt11Request, MintQuoteState, NotificationPayload,
|
CurrencyUnit, MeltQuoteState, MintBolt11Request, MintQuoteState, NotificationPayload,
|
||||||
PreMintSecrets, State,
|
PreMintSecrets, State,
|
||||||
};
|
};
|
||||||
use cdk::wallet::client::{HttpClient, HttpClientMethods};
|
use cdk::wallet::client::{HttpClient, MintConnector};
|
||||||
use cdk::wallet::Wallet;
|
use cdk::wallet::Wallet;
|
||||||
use cdk_integration_tests::init_regtest::{
|
use cdk_integration_tests::init_regtest::{
|
||||||
get_mint_url, get_mint_ws_url, init_cln_client, init_lnd_client,
|
get_mint_url, get_mint_ws_url, init_cln_client, init_lnd_client,
|
||||||
@@ -374,7 +374,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();
|
let http_client = HttpClient::new(get_mint_url().as_str().parse()?);
|
||||||
let premint_secrets =
|
let premint_secrets =
|
||||||
PreMintSecrets::random(active_keyset_id, 31.into(), &SplitTarget::default()).unwrap();
|
PreMintSecrets::random(active_keyset_id, 31.into(), &SplitTarget::default()).unwrap();
|
||||||
|
|
||||||
@@ -383,12 +383,8 @@ async fn test_cached_mint() -> Result<()> {
|
|||||||
outputs: premint_secrets.blinded_messages(),
|
outputs: premint_secrets.blinded_messages(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = http_client
|
let response = http_client.post_mint(request.clone()).await?;
|
||||||
.post_mint(get_mint_url().as_str().parse()?, request.clone())
|
let response1 = http_client.post_mint(request).await?;
|
||||||
.await?;
|
|
||||||
let response1 = http_client
|
|
||||||
.post_mint(get_mint_url().as_str().parse()?, request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
assert!(response == response1);
|
assert!(response == response1);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -96,6 +96,18 @@ pub struct MintQuoteBolt11Response<Q> {
|
|||||||
pub expiry: Option<u64>,
|
pub expiry: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mint")]
|
||||||
|
impl From<MintQuoteBolt11Response<Uuid>> for MintQuoteBolt11Response<String> {
|
||||||
|
fn from(value: MintQuoteBolt11Response<Uuid>) -> Self {
|
||||||
|
Self {
|
||||||
|
quote: value.quote.to_string(),
|
||||||
|
request: value.request,
|
||||||
|
state: value.state,
|
||||||
|
expiry: value.expiry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
impl From<crate::mint::MintQuote> for MintQuoteBolt11Response<Uuid> {
|
impl From<crate::mint::MintQuote> for MintQuoteBolt11Response<Uuid> {
|
||||||
fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<Uuid> {
|
fn from(mint_quote: crate::mint::MintQuote) -> MintQuoteBolt11Response<Uuid> {
|
||||||
@@ -121,6 +133,18 @@ pub struct MintBolt11Request<Q> {
|
|||||||
pub outputs: Vec<BlindedMessage>,
|
pub outputs: Vec<BlindedMessage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mint")]
|
||||||
|
impl TryFrom<MintBolt11Request<String>> for MintBolt11Request<Uuid> {
|
||||||
|
type Error = uuid::Error;
|
||||||
|
|
||||||
|
fn try_from(value: MintBolt11Request<String>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
quote: Uuid::from_str(&value.quote)?,
|
||||||
|
outputs: value.outputs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Q> MintBolt11Request<Q> {
|
impl<Q> MintBolt11Request<Q> {
|
||||||
/// Total [`Amount`] of outputs
|
/// Total [`Amount`] of outputs
|
||||||
pub fn total_amount(&self) -> Result<Amount, Error> {
|
pub fn total_amount(&self) -> Result<Amount, Error> {
|
||||||
|
|||||||
@@ -115,6 +115,22 @@ pub struct MeltQuoteBolt11Response<Q> {
|
|||||||
pub change: Option<Vec<BlindSignature>>,
|
pub change: Option<Vec<BlindSignature>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mint")]
|
||||||
|
impl From<MeltQuoteBolt11Response<Uuid>> for MeltQuoteBolt11Response<String> {
|
||||||
|
fn from(value: MeltQuoteBolt11Response<Uuid>) -> Self {
|
||||||
|
Self {
|
||||||
|
quote: value.quote.to_string(),
|
||||||
|
amount: value.amount,
|
||||||
|
fee_reserve: value.fee_reserve,
|
||||||
|
paid: value.paid,
|
||||||
|
state: value.state,
|
||||||
|
expiry: value.expiry,
|
||||||
|
payment_preimage: value.payment_preimage,
|
||||||
|
change: value.change,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mint")]
|
#[cfg(feature = "mint")]
|
||||||
impl From<&MeltQuote> for MeltQuoteBolt11Response<Uuid> {
|
impl From<&MeltQuote> for MeltQuoteBolt11Response<Uuid> {
|
||||||
fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
|
fn from(melt_quote: &MeltQuote) -> MeltQuoteBolt11Response<Uuid> {
|
||||||
@@ -247,6 +263,19 @@ pub struct MeltBolt11Request<Q> {
|
|||||||
pub outputs: Option<Vec<BlindedMessage>>,
|
pub outputs: Option<Vec<BlindedMessage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "mint")]
|
||||||
|
impl TryFrom<MeltBolt11Request<String>> for MeltBolt11Request<Uuid> {
|
||||||
|
type Error = uuid::Error;
|
||||||
|
|
||||||
|
fn try_from(value: MeltBolt11Request<String>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
quote: Uuid::from_str(&value.quote)?,
|
||||||
|
inputs: value.inputs,
|
||||||
|
outputs: value.outputs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<Q: Serialize + DeserializeOwned> MeltBolt11Request<Q> {
|
impl<Q: Serialize + DeserializeOwned> MeltBolt11Request<Q> {
|
||||||
/// Total [`Amount`] of [`Proofs`]
|
/// Total [`Amount`] of [`Proofs`]
|
||||||
pub fn proofs_amount(&self) -> Result<Amount, Error> {
|
pub fn proofs_amount(&self) -> Result<Amount, Error> {
|
||||||
|
|||||||
@@ -33,19 +33,15 @@ macro_rules! convert_http_response {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct HttpClient {
|
pub struct HttpClient {
|
||||||
inner: Client,
|
inner: Client,
|
||||||
}
|
mint_url: MintUrl,
|
||||||
|
|
||||||
impl Default for HttpClient {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpClient {
|
impl HttpClient {
|
||||||
/// Create new [`HttpClient`]
|
/// Create new [`HttpClient`]
|
||||||
pub fn new() -> Self {
|
pub fn new(mint_url: MintUrl) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Client::new(),
|
inner: Client::new(),
|
||||||
|
mint_url,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +50,7 @@ impl HttpClient {
|
|||||||
/// Specifying `None` for `host_matcher` will use the proxy for all
|
/// Specifying `None` for `host_matcher` will use the proxy for all
|
||||||
/// requests.
|
/// requests.
|
||||||
pub fn with_proxy(
|
pub fn with_proxy(
|
||||||
|
mint_url: MintUrl,
|
||||||
proxy: Url,
|
proxy: Url,
|
||||||
host_matcher: Option<&str>,
|
host_matcher: Option<&str>,
|
||||||
accept_invalid_certs: bool,
|
accept_invalid_certs: bool,
|
||||||
@@ -76,26 +73,31 @@ impl HttpClient {
|
|||||||
.danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs
|
.danger_accept_invalid_certs(accept_invalid_certs) // Allow self-signed certs
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
Ok(Self { inner: client })
|
Ok(Self {
|
||||||
|
inner: client,
|
||||||
|
mint_url,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
impl HttpClientMethods for HttpClient {
|
impl MintConnector for HttpClient {
|
||||||
/// Get Active Mint Keys [NUT-01]
|
/// Get Active Mint Keys [NUT-01]
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error> {
|
async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "keys"])?;
|
let url = self.mint_url.join_paths(&["v1", "keys"])?;
|
||||||
let keys = self.inner.get(url).send().await?.text().await?;
|
let keys = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
Ok(convert_http_response!(KeysResponse, keys)?.keysets)
|
Ok(convert_http_response!(KeysResponse, keys)?.keysets)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get Keyset Keys [NUT-01]
|
/// Get Keyset Keys [NUT-01]
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error> {
|
async fn get_mint_keyset(&self, keyset_id: Id) -> Result<KeySet, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "keys", &keyset_id.to_string()])?;
|
let url = self
|
||||||
|
.mint_url
|
||||||
|
.join_paths(&["v1", "keys", &keyset_id.to_string()])?;
|
||||||
let keys = self.inner.get(url).send().await?.text().await?;
|
let keys = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
convert_http_response!(KeysResponse, keys)?
|
convert_http_response!(KeysResponse, keys)?
|
||||||
@@ -106,22 +108,23 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get Keysets [NUT-02]
|
/// Get Keysets [NUT-02]
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error> {
|
async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "keysets"])?;
|
let url = self.mint_url.join_paths(&["v1", "keysets"])?;
|
||||||
let res = self.inner.get(url).send().await?.text().await?;
|
let res = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
convert_http_response!(KeysetResponse, res)
|
convert_http_response!(KeysetResponse, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mint Quote [NUT-04]
|
/// Mint Quote [NUT-04]
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_mint_quote(
|
async fn post_mint_quote(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MintQuoteBolt11Request,
|
request: MintQuoteBolt11Request,
|
||||||
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?;
|
let url = self
|
||||||
|
.mint_url
|
||||||
|
.join_paths(&["v1", "mint", "quote", "bolt11"])?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -136,13 +139,14 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mint Quote status
|
/// Mint Quote status
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_mint_quote_status(
|
async fn get_mint_quote_status(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
|
let url = self
|
||||||
|
.mint_url
|
||||||
|
.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
|
||||||
|
|
||||||
let res = self.inner.get(url).send().await?.text().await?;
|
let res = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
@@ -150,13 +154,12 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Mint Tokens [NUT-04]
|
/// Mint Tokens [NUT-04]
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_mint(
|
async fn post_mint(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MintBolt11Request<String>,
|
request: MintBolt11Request<String>,
|
||||||
) -> Result<MintBolt11Response, Error> {
|
) -> Result<MintBolt11Response, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?;
|
let url = self.mint_url.join_paths(&["v1", "mint", "bolt11"])?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -171,13 +174,14 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Melt Quote [NUT-05]
|
/// Melt Quote [NUT-05]
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_melt_quote(
|
async fn post_melt_quote(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MeltQuoteBolt11Request,
|
request: MeltQuoteBolt11Request,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?;
|
let url = self
|
||||||
|
.mint_url
|
||||||
|
.join_paths(&["v1", "melt", "quote", "bolt11"])?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -192,13 +196,14 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Melt Quote Status
|
/// Melt Quote Status
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_melt_quote_status(
|
async fn get_melt_quote_status(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
|
let url = self
|
||||||
|
.mint_url
|
||||||
|
.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
|
||||||
|
|
||||||
let res = self.inner.get(url).send().await?.text().await?;
|
let res = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
@@ -207,13 +212,12 @@ impl HttpClientMethods for HttpClient {
|
|||||||
|
|
||||||
/// Melt [NUT-05]
|
/// Melt [NUT-05]
|
||||||
/// [Nut-08] Lightning fee return if outputs defined
|
/// [Nut-08] Lightning fee return if outputs defined
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_melt(
|
async fn post_melt(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MeltBolt11Request<String>,
|
request: MeltBolt11Request<String>,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?;
|
let url = self.mint_url.join_paths(&["v1", "melt", "bolt11"])?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -228,13 +232,9 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Swap Token [NUT-03]
|
/// Swap Token [NUT-03]
|
||||||
#[instrument(skip(self, swap_request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, swap_request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_swap(
|
async fn post_swap(&self, swap_request: SwapRequest) -> Result<SwapResponse, Error> {
|
||||||
&self,
|
let url = self.mint_url.join_paths(&["v1", "swap"])?;
|
||||||
mint_url: MintUrl,
|
|
||||||
swap_request: SwapRequest,
|
|
||||||
) -> Result<SwapResponse, Error> {
|
|
||||||
let url = mint_url.join_paths(&["v1", "swap"])?;
|
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -249,9 +249,9 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get Mint Info [NUT-06]
|
/// Get Mint Info [NUT-06]
|
||||||
#[instrument(skip(self), fields(mint_url = %mint_url))]
|
#[instrument(skip(self), fields(mint_url = %self.mint_url))]
|
||||||
async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error> {
|
async fn get_mint_info(&self) -> Result<MintInfo, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "info"])?;
|
let url = self.mint_url.join_paths(&["v1", "info"])?;
|
||||||
|
|
||||||
let res = self.inner.get(url).send().await?.text().await?;
|
let res = self.inner.get(url).send().await?.text().await?;
|
||||||
|
|
||||||
@@ -259,13 +259,12 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Spendable check [NUT-07]
|
/// Spendable check [NUT-07]
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_check_state(
|
async fn post_check_state(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: CheckStateRequest,
|
request: CheckStateRequest,
|
||||||
) -> Result<CheckStateResponse, Error> {
|
) -> Result<CheckStateResponse, Error> {
|
||||||
let url = mint_url.join_paths(&["v1", "checkstate"])?;
|
let url = self.mint_url.join_paths(&["v1", "checkstate"])?;
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -280,13 +279,9 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Restore request [NUT-13]
|
/// Restore request [NUT-13]
|
||||||
#[instrument(skip(self, request), fields(mint_url = %mint_url))]
|
#[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
|
||||||
async fn post_restore(
|
async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
|
||||||
&self,
|
let url = self.mint_url.join_paths(&["v1", "restore"])?;
|
||||||
mint_url: MintUrl,
|
|
||||||
request: RestoreRequest,
|
|
||||||
) -> Result<RestoreResponse, Error> {
|
|
||||||
let url = mint_url.join_paths(&["v1", "restore"])?;
|
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
.inner
|
.inner
|
||||||
@@ -301,83 +296,56 @@ impl HttpClientMethods for HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Http Client Methods
|
/// Interface that connects a wallet to a mint. Typically represents an [HttpClient].
|
||||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||||
pub trait HttpClientMethods: Debug {
|
pub trait MintConnector: Debug {
|
||||||
/// Get Active Mint Keys [NUT-01]
|
/// Get Active Mint Keys [NUT-01]
|
||||||
async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error>;
|
async fn get_mint_keys(&self) -> Result<Vec<KeySet>, Error>;
|
||||||
|
|
||||||
/// Get Keyset Keys [NUT-01]
|
/// Get Keyset Keys [NUT-01]
|
||||||
async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error>;
|
async fn get_mint_keyset(&self, keyset_id: Id) -> Result<KeySet, Error>;
|
||||||
|
|
||||||
/// Get Keysets [NUT-02]
|
/// Get Keysets [NUT-02]
|
||||||
async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error>;
|
async fn get_mint_keysets(&self) -> Result<KeysetResponse, Error>;
|
||||||
|
|
||||||
/// Mint Quote [NUT-04]
|
/// Mint Quote [NUT-04]
|
||||||
async fn post_mint_quote(
|
async fn post_mint_quote(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MintQuoteBolt11Request,
|
request: MintQuoteBolt11Request,
|
||||||
) -> Result<MintQuoteBolt11Response<String>, Error>;
|
) -> Result<MintQuoteBolt11Response<String>, Error>;
|
||||||
|
|
||||||
/// Mint Quote status
|
/// Mint Quote status
|
||||||
async fn get_mint_quote_status(
|
async fn get_mint_quote_status(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MintQuoteBolt11Response<String>, Error>;
|
) -> Result<MintQuoteBolt11Response<String>, Error>;
|
||||||
|
|
||||||
/// Mint Tokens [NUT-04]
|
/// Mint Tokens [NUT-04]
|
||||||
async fn post_mint(
|
async fn post_mint(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MintBolt11Request<String>,
|
request: MintBolt11Request<String>,
|
||||||
) -> Result<MintBolt11Response, Error>;
|
) -> Result<MintBolt11Response, Error>;
|
||||||
|
|
||||||
/// Melt Quote [NUT-05]
|
/// Melt Quote [NUT-05]
|
||||||
async fn post_melt_quote(
|
async fn post_melt_quote(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MeltQuoteBolt11Request,
|
request: MeltQuoteBolt11Request,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
||||||
|
|
||||||
/// Melt Quote Status
|
/// Melt Quote Status
|
||||||
async fn get_melt_quote_status(
|
async fn get_melt_quote_status(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
||||||
|
|
||||||
/// Melt [NUT-05]
|
/// Melt [NUT-05]
|
||||||
/// [Nut-08] Lightning fee return if outputs defined
|
/// [Nut-08] Lightning fee return if outputs defined
|
||||||
async fn post_melt(
|
async fn post_melt(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: MeltBolt11Request<String>,
|
request: MeltBolt11Request<String>,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
) -> Result<MeltQuoteBolt11Response<String>, Error>;
|
||||||
|
|
||||||
/// Split Token [NUT-06]
|
/// Split Token [NUT-06]
|
||||||
async fn post_swap(
|
async fn post_swap(&self, request: SwapRequest) -> Result<SwapResponse, Error>;
|
||||||
&self,
|
|
||||||
mint_url: MintUrl,
|
|
||||||
request: SwapRequest,
|
|
||||||
) -> Result<SwapResponse, Error>;
|
|
||||||
|
|
||||||
/// Get Mint Info [NUT-06]
|
/// Get Mint Info [NUT-06]
|
||||||
async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error>;
|
async fn get_mint_info(&self) -> Result<MintInfo, Error>;
|
||||||
|
|
||||||
/// Spendable check [NUT-07]
|
/// Spendable check [NUT-07]
|
||||||
async fn post_check_state(
|
async fn post_check_state(
|
||||||
&self,
|
&self,
|
||||||
mint_url: MintUrl,
|
|
||||||
request: CheckStateRequest,
|
request: CheckStateRequest,
|
||||||
) -> Result<CheckStateResponse, Error>;
|
) -> Result<CheckStateResponse, Error>;
|
||||||
|
|
||||||
/// Restore request [NUT-13]
|
/// Restore request [NUT-13]
|
||||||
async fn post_restore(
|
async fn post_restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error>;
|
||||||
&self,
|
|
||||||
mint_url: MintUrl,
|
|
||||||
request: RestoreRequest,
|
|
||||||
) -> Result<RestoreResponse, Error>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ impl Wallet {
|
|||||||
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
|
||||||
keys
|
keys
|
||||||
} else {
|
} else {
|
||||||
let keys = self
|
let keys = self.client.get_mint_keyset(keyset_id).await?;
|
||||||
.client
|
|
||||||
.get_mint_keyset(self.mint_url.clone(), keyset_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
keys.verify_id()?;
|
keys.verify_id()?;
|
||||||
|
|
||||||
@@ -33,7 +30,7 @@ impl Wallet {
|
|||||||
/// Queries mint for all keysets
|
/// Queries mint for all keysets
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
|
let keysets = self.client.get_mint_keysets().await?;
|
||||||
|
|
||||||
self.localstore
|
self.localstore
|
||||||
.add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
|
.add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
|
||||||
@@ -48,7 +45,7 @@ impl Wallet {
|
|||||||
/// keysets
|
/// keysets
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_active_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
pub async fn get_active_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
|
||||||
let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
|
let keysets = self.client.get_mint_keysets().await?;
|
||||||
let keysets = keysets.keysets;
|
let keysets = keysets.keysets;
|
||||||
|
|
||||||
self.localstore
|
self.localstore
|
||||||
|
|||||||
@@ -65,10 +65,7 @@ impl Wallet {
|
|||||||
options,
|
options,
|
||||||
};
|
};
|
||||||
|
|
||||||
let quote_res = self
|
let quote_res = self.client.post_melt_quote(quote_request).await?;
|
||||||
.client
|
|
||||||
.post_melt_quote(self.mint_url.clone(), quote_request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if quote_res.amount != amount {
|
if quote_res.amount != amount {
|
||||||
return Err(Error::IncorrectQuoteAmount);
|
return Err(Error::IncorrectQuoteAmount);
|
||||||
@@ -96,10 +93,7 @@ impl Wallet {
|
|||||||
&self,
|
&self,
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
) -> Result<MeltQuoteBolt11Response<String>, Error> {
|
||||||
let response = self
|
let response = self.client.get_melt_quote_status(quote_id).await?;
|
||||||
.client
|
|
||||||
.get_melt_quote_status(self.mint_url.clone(), quote_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
match self.localstore.get_melt_quote(quote_id).await? {
|
match self.localstore.get_melt_quote(quote_id).await? {
|
||||||
Some(quote) => {
|
Some(quote) => {
|
||||||
@@ -160,7 +154,7 @@ impl Wallet {
|
|||||||
outputs: Some(premint_secrets.blinded_messages()),
|
outputs: Some(premint_secrets.blinded_messages()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let melt_response = self.client.post_melt(self.mint_url.clone(), request).await;
|
let melt_response = self.client.post_melt(request).await;
|
||||||
|
|
||||||
let melt_response = match melt_response {
|
let melt_response = match melt_response {
|
||||||
Ok(melt_response) => melt_response,
|
Ok(melt_response) => melt_response,
|
||||||
|
|||||||
@@ -71,10 +71,7 @@ impl Wallet {
|
|||||||
description,
|
description,
|
||||||
};
|
};
|
||||||
|
|
||||||
let quote_res = self
|
let quote_res = self.client.post_mint_quote(request).await?;
|
||||||
.client
|
|
||||||
.post_mint_quote(mint_url.clone(), request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let quote = MintQuote {
|
let quote = MintQuote {
|
||||||
mint_url,
|
mint_url,
|
||||||
@@ -97,10 +94,7 @@ impl Wallet {
|
|||||||
&self,
|
&self,
|
||||||
quote_id: &str,
|
quote_id: &str,
|
||||||
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
) -> Result<MintQuoteBolt11Response<String>, Error> {
|
||||||
let response = self
|
let response = self.client.get_mint_quote_status(quote_id).await?;
|
||||||
.client
|
|
||||||
.get_mint_quote_status(self.mint_url.clone(), quote_id)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
match self.localstore.get_mint_quote(quote_id).await? {
|
match self.localstore.get_mint_quote(quote_id).await? {
|
||||||
Some(quote) => {
|
Some(quote) => {
|
||||||
@@ -227,10 +221,7 @@ impl Wallet {
|
|||||||
outputs: premint_secrets.blinded_messages(),
|
outputs: premint_secrets.blinded_messages(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mint_res = self
|
let mint_res = self.client.post_mint(request).await?;
|
||||||
.client
|
|
||||||
.post_mint(self.mint_url.clone(), request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
let keys = self.get_keyset_keys(active_keyset_id).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use bitcoin::bip32::Xpriv;
|
use bitcoin::bip32::Xpriv;
|
||||||
use bitcoin::Network;
|
use bitcoin::Network;
|
||||||
use client::HttpClientMethods;
|
use client::MintConnector;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::amount::SplitTarget;
|
use crate::amount::SplitTarget;
|
||||||
@@ -57,7 +57,7 @@ pub struct Wallet {
|
|||||||
/// The targeted amount of proofs to have at each size
|
/// The targeted amount of proofs to have at each size
|
||||||
pub target_proof_count: usize,
|
pub target_proof_count: usize,
|
||||||
xpriv: Xpriv,
|
xpriv: Xpriv,
|
||||||
client: Arc<dyn HttpClientMethods + Send + Sync>,
|
client: Arc<dyn MintConnector + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
@@ -86,11 +86,12 @@ impl Wallet {
|
|||||||
target_proof_count: Option<usize>,
|
target_proof_count: Option<usize>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let xpriv = Xpriv::new_master(Network::Bitcoin, seed).expect("Could not create master key");
|
let xpriv = Xpriv::new_master(Network::Bitcoin, seed).expect("Could not create master key");
|
||||||
|
let mint_url = MintUrl::from_str(mint_url)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mint_url: MintUrl::from_str(mint_url)?,
|
mint_url: mint_url.clone(),
|
||||||
unit,
|
unit,
|
||||||
client: Arc::new(HttpClient::new()),
|
client: Arc::new(HttpClient::new(mint_url)),
|
||||||
localstore,
|
localstore,
|
||||||
xpriv,
|
xpriv,
|
||||||
target_proof_count: target_proof_count.unwrap_or(3),
|
target_proof_count: target_proof_count.unwrap_or(3),
|
||||||
@@ -98,8 +99,8 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Change HTTP client
|
/// Change HTTP client
|
||||||
pub fn set_client<C: HttpClientMethods + 'static + Send + Sync>(&mut self, client: C) {
|
pub fn set_client(&mut self, client: Arc<dyn MintConnector + Send + Sync>) {
|
||||||
self.client = Arc::new(client);
|
self.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fee required for proof set
|
/// Fee required for proof set
|
||||||
@@ -160,10 +161,10 @@ impl Wallet {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Qeury mint for current mint information
|
/// Query mint for current mint information
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> {
|
pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> {
|
||||||
let mint_info = match self.client.get_mint_info(self.mint_url.clone()).await {
|
let mint_info = match self.client.get_mint_info().await {
|
||||||
Ok(mint_info) => Some(mint_info),
|
Ok(mint_info) => Some(mint_info),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
tracing::warn!("Could not get mint info {}", err);
|
tracing::warn!("Could not get mint info {}", err);
|
||||||
@@ -277,10 +278,7 @@ impl Wallet {
|
|||||||
outputs: premint_secrets.blinded_messages(),
|
outputs: premint_secrets.blinded_messages(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self
|
let response = self.client.post_restore(restore_request).await?;
|
||||||
.client
|
|
||||||
.post_restore(self.mint_url.clone(), restore_request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if response.signatures.is_empty() {
|
if response.signatures.is_empty() {
|
||||||
empty_batch += 1;
|
empty_batch += 1;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ impl Wallet {
|
|||||||
|
|
||||||
let spendable = self
|
let spendable = self
|
||||||
.client
|
.client
|
||||||
.post_check_state(self.mint_url.clone(), CheckStateRequest { ys: proof_ys })
|
.post_check_state(CheckStateRequest { ys: proof_ys })
|
||||||
.await?
|
.await?
|
||||||
.states;
|
.states;
|
||||||
|
|
||||||
@@ -86,10 +86,7 @@ impl Wallet {
|
|||||||
pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
|
pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
|
||||||
let spendable = self
|
let spendable = self
|
||||||
.client
|
.client
|
||||||
.post_check_state(
|
.post_check_state(CheckStateRequest { ys: proofs.ys()? })
|
||||||
self.mint_url.clone(),
|
|
||||||
CheckStateRequest { ys: proofs.ys()? },
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
let spent_ys: Vec<_> = spendable
|
let spent_ys: Vec<_> = spendable
|
||||||
.states
|
.states
|
||||||
|
|||||||
@@ -128,10 +128,7 @@ impl Wallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let swap_response = self
|
let swap_response = self.client.post_swap(pre_swap.swap_request).await?;
|
||||||
.client
|
|
||||||
.post_swap(mint_url.clone(), pre_swap.swap_request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Proof to keep
|
// Proof to keep
|
||||||
let recv_proofs = construct_proofs(
|
let recv_proofs = construct_proofs(
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ impl Wallet {
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let swap_response = self
|
let swap_response = self.client.post_swap(pre_swap.swap_request).await?;
|
||||||
.client
|
|
||||||
.post_swap(mint_url.clone(), pre_swap.swap_request)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;
|
let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user