Jelajahi Sumber

Abstract HttpClient into HttpClientMethods trait (#429)

* Abstract HttpClientMethods trait
---------

Co-authored-by: ok300 <106775972+ok300@users.noreply.github.com>
David Caseria 4 bulan lalu
induk
melakukan
7eb3449710

+ 1 - 0
crates/cdk-cli/src/sub_commands/mint_info.rs

@@ -1,5 +1,6 @@
 use anyhow::Result;
 use cdk::mint_url::MintUrl;
+use cdk::wallet::client::HttpClientMethods;
 use cdk::HttpClient;
 use clap::Args;
 use url::Url;

+ 16 - 11
crates/cdk-integration-tests/src/lib.rs

@@ -11,11 +11,11 @@ use cdk::cdk_lightning::MintLightning;
 use cdk::dhke::construct_proofs;
 use cdk::mint::FeeReserve;
 use cdk::nuts::{
-    CurrencyUnit, Id, KeySet, MeltMethodSettings, MintInfo, MintMethodSettings, MintQuoteState,
-    Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
+    CurrencyUnit, Id, KeySet, MeltMethodSettings, MintBolt11Request, MintInfo, MintMethodSettings,
+    MintQuoteBolt11Request, MintQuoteState, Nuts, PaymentMethod, PreMintSecrets, Proofs, State,
 };
 use cdk::types::{LnKey, QuoteTTL};
-use cdk::wallet::client::HttpClient;
+use cdk::wallet::client::{HttpClient, HttpClientMethods};
 use cdk::{Mint, Wallet};
 use cdk_fake_wallet::FakeWallet;
 use init_regtest::{get_mint_addr, get_mint_port, get_mint_url};
@@ -158,8 +158,14 @@ pub async fn mint_proofs(
 
     let wallet_client = HttpClient::new();
 
+    let request = MintQuoteBolt11Request {
+        amount,
+        unit: CurrencyUnit::Sat,
+        description,
+    };
+
     let mint_quote = wallet_client
-        .post_mint_quote(mint_url.parse()?, 1.into(), CurrencyUnit::Sat, description)
+        .post_mint_quote(mint_url.parse()?, request)
         .await?;
 
     println!("Please pay: {}", mint_quote.request);
@@ -179,13 +185,12 @@ pub async fn mint_proofs(
 
     let premint_secrets = PreMintSecrets::random(keyset_id, amount, &SplitTarget::default())?;
 
-    let mint_response = wallet_client
-        .post_mint(
-            mint_url.parse()?,
-            &mint_quote.quote,
-            premint_secrets.clone(),
-        )
-        .await?;
+    let request = MintBolt11Request {
+        quote: mint_quote.quote,
+        outputs: premint_secrets.blinded_messages(),
+    };
+
+    let mint_response = wallet_client.post_mint(mint_url.parse()?, request).await?;
 
     let pre_swap_proofs = construct_proofs(
         mint_response.signatures,

+ 12 - 10
crates/cdk-integration-tests/tests/fake_wallet.rs

@@ -5,8 +5,11 @@ use bip39::Mnemonic;
 use cdk::{
     amount::SplitTarget,
     cdk_database::WalletMemoryDatabase,
-    nuts::{CurrencyUnit, MeltQuoteState, PreMintSecrets, State},
-    wallet::{client::HttpClient, Wallet},
+    nuts::{CurrencyUnit, MeltBolt11Request, MeltQuoteState, PreMintSecrets, State},
+    wallet::{
+        client::{HttpClient, HttpClientMethods},
+        Wallet,
+    },
 };
 use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
 use cdk_integration_tests::attempt_to_swap_pending;
@@ -354,14 +357,13 @@ async fn test_fake_melt_change_in_quote() -> Result<()> {
 
     let client = HttpClient::new();
 
-    let melt_response = client
-        .post_melt(
-            MINT_URL.parse()?,
-            melt_quote.id.clone(),
-            proofs.clone(),
-            Some(premint_secrets.blinded_messages()),
-        )
-        .await?;
+    let melt_request = MeltBolt11Request {
+        quote: melt_quote.id.clone(),
+        inputs: proofs.clone(),
+        outputs: Some(premint_secrets.blinded_messages()),
+    };
+
+    let melt_response = client.post_melt(MINT_URL.parse()?, melt_request).await?;
 
     assert!(melt_response.change.is_some());
 

+ 14 - 8
crates/cdk-integration-tests/tests/regtest.rs

@@ -5,8 +5,13 @@ use bip39::Mnemonic;
 use cdk::{
     amount::{Amount, SplitTarget},
     cdk_database::WalletMemoryDatabase,
-    nuts::{CurrencyUnit, MeltQuoteState, MintQuoteState, PreMintSecrets, State},
-    wallet::{client::HttpClient, Wallet},
+    nuts::{
+        CurrencyUnit, MeltQuoteState, MintBolt11Request, MintQuoteState, PreMintSecrets, State,
+    },
+    wallet::{
+        client::{HttpClient, HttpClientMethods},
+        Wallet,
+    },
 };
 use cdk_integration_tests::init_regtest::{get_mint_url, init_cln_client, init_lnd_client};
 use lightning_invoice::Bolt11Invoice;
@@ -289,15 +294,16 @@ async fn test_cached_mint() -> Result<()> {
     let premint_secrets =
         PreMintSecrets::random(active_keyset_id, 31.into(), &SplitTarget::default()).unwrap();
 
+    let request = MintBolt11Request {
+        quote: quote.id,
+        outputs: premint_secrets.blinded_messages(),
+    };
+
     let response = http_client
-        .post_mint(
-            get_mint_url().as_str().parse()?,
-            &quote.id,
-            premint_secrets.clone(),
-        )
+        .post_mint(get_mint_url().as_str().parse()?, request.clone())
         .await?;
     let response1 = http_client
-        .post_mint(get_mint_url().as_str().parse()?, &quote.id, premint_secrets)
+        .post_mint(get_mint_url().as_str().parse()?, request)
         .await?;
 
     assert!(response == response1);

+ 2 - 2
crates/cdk/src/nuts/nut03.rs

@@ -32,11 +32,11 @@ pub struct PreSwap {
     pub fee: Amount,
 }
 
-/// Split Request [NUT-06]
+/// Swap Request [NUT-03]
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
 pub struct SwapRequest {
-    /// Proofs that are to be spent in `Split`
+    /// Proofs that are to be spent in a `Swap`
     #[cfg_attr(feature = "swagger", schema(value_type = Vec<Proof>))]
     pub inputs: Proofs,
     /// Blinded Messages for Mint to sign

+ 114 - 62
crates/cdk/src/wallet/client.rs

@@ -1,5 +1,8 @@
 //! Wallet client
 
+use std::fmt::Debug;
+
+use async_trait::async_trait;
 use reqwest::Client;
 use serde_json::Value;
 use tracing::instrument;
@@ -8,15 +11,12 @@ use url::Url;
 use super::Error;
 use crate::error::ErrorResponse;
 use crate::mint_url::MintUrl;
-use crate::nuts::nut15::Mpp;
 use crate::nuts::{
-    BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
-    KeysetResponse, MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response,
-    MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request,
-    MintQuoteBolt11Response, PreMintSecrets, Proof, PublicKey, RestoreRequest, RestoreResponse,
-    SwapRequest, SwapResponse,
+    CheckStateRequest, CheckStateResponse, Id, KeySet, KeysResponse, KeysetResponse,
+    MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
+    MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, RestoreRequest,
+    RestoreResponse, SwapRequest, SwapResponse,
 };
-use crate::{Amount, Bolt11Invoice};
 
 /// Http Client
 #[derive(Debug, Clone)]
@@ -67,10 +67,14 @@ impl HttpClient {
 
         Ok(Self { inner: client })
     }
+}
 
+#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
+#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
+impl HttpClientMethods for HttpClient {
     /// Get Active Mint Keys [NUT-01]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error> {
+    async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error> {
         let url = mint_url.join_paths(&["v1", "keys"])?;
         let keys = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -82,7 +86,7 @@ impl HttpClient {
 
     /// Get Keyset Keys [NUT-01]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error> {
+    async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error> {
         let url = mint_url.join_paths(&["v1", "keys", &keyset_id.to_string()])?;
         let keys = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -94,7 +98,7 @@ impl HttpClient {
 
     /// Get Keysets [NUT-02]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error> {
+    async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error> {
         let url = mint_url.join_paths(&["v1", "keysets"])?;
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -106,21 +110,13 @@ impl HttpClient {
 
     /// Mint Quote [NUT-04]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn post_mint_quote(
+    async fn post_mint_quote(
         &self,
         mint_url: MintUrl,
-        amount: Amount,
-        unit: CurrencyUnit,
-        description: Option<String>,
+        request: MintQuoteBolt11Request,
     ) -> Result<MintQuoteBolt11Response, Error> {
         let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?;
 
-        let request = MintQuoteBolt11Request {
-            amount,
-            unit,
-            description,
-        };
-
         let res = self
             .inner
             .post(url)
@@ -141,7 +137,7 @@ impl HttpClient {
 
     /// Mint Quote status
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_quote_status(
+    async fn get_mint_quote_status(
         &self,
         mint_url: MintUrl,
         quote_id: &str,
@@ -160,20 +156,14 @@ impl HttpClient {
     }
 
     /// Mint Tokens [NUT-04]
-    #[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))]
-    pub async fn post_mint(
+    #[instrument(skip(self, request), fields(mint_url = %mint_url))]
+    async fn post_mint(
         &self,
         mint_url: MintUrl,
-        quote: &str,
-        premint_secrets: PreMintSecrets,
+        request: MintBolt11Request,
     ) -> Result<MintBolt11Response, Error> {
         let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?;
 
-        let request = MintBolt11Request {
-            quote: quote.to_string(),
-            outputs: premint_secrets.blinded_messages(),
-        };
-
         let res = self
             .inner
             .post(url)
@@ -191,23 +181,13 @@ impl HttpClient {
 
     /// Melt Quote [NUT-05]
     #[instrument(skip(self, request), fields(mint_url = %mint_url))]
-    pub async fn post_melt_quote(
+    async fn post_melt_quote(
         &self,
         mint_url: MintUrl,
-        unit: CurrencyUnit,
-        request: Bolt11Invoice,
-        mpp_amount: Option<Amount>,
+        request: MeltQuoteBolt11Request,
     ) -> Result<MeltQuoteBolt11Response, Error> {
         let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?;
 
-        let options = mpp_amount.map(|amount| Mpp { amount });
-
-        let request = MeltQuoteBolt11Request {
-            request,
-            unit,
-            options,
-        };
-
         let res = self
             .inner
             .post(url)
@@ -225,7 +205,7 @@ impl HttpClient {
 
     /// Melt Quote Status
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_melt_quote_status(
+    async fn get_melt_quote_status(
         &self,
         mint_url: MintUrl,
         quote_id: &str,
@@ -242,22 +222,14 @@ impl HttpClient {
 
     /// Melt [NUT-05]
     /// [Nut-08] Lightning fee return if outputs defined
-    #[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))]
-    pub async fn post_melt(
+    #[instrument(skip(self, request), fields(mint_url = %mint_url))]
+    async fn post_melt(
         &self,
         mint_url: MintUrl,
-        quote: String,
-        inputs: Vec<Proof>,
-        outputs: Option<Vec<BlindedMessage>>,
+        request: MeltBolt11Request,
     ) -> Result<MeltQuoteBolt11Response, Error> {
         let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?;
 
-        let request = MeltBolt11Request {
-            quote,
-            inputs,
-            outputs,
-        };
-
         let res = self
             .inner
             .post(url)
@@ -278,9 +250,9 @@ impl HttpClient {
         }
     }
 
-    /// Split Token [NUT-06]
+    /// Swap Token [NUT-03]
     #[instrument(skip(self, swap_request), fields(mint_url = %mint_url))]
-    pub async fn post_swap(
+    async fn post_swap(
         &self,
         mint_url: MintUrl,
         swap_request: SwapRequest,
@@ -304,7 +276,7 @@ impl HttpClient {
 
     /// Get Mint Info [NUT-06]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error> {
+    async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error> {
         let url = mint_url.join_paths(&["v1", "info"])?;
 
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
@@ -319,14 +291,13 @@ impl HttpClient {
     }
 
     /// Spendable check [NUT-07]
-    #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn post_check_state(
+    #[instrument(skip(self, request), fields(mint_url = %mint_url))]
+    async fn post_check_state(
         &self,
         mint_url: MintUrl,
-        ys: Vec<PublicKey>,
+        request: CheckStateRequest,
     ) -> Result<CheckStateResponse, Error> {
         let url = mint_url.join_paths(&["v1", "checkstate"])?;
-        let request = CheckStateRequest { ys };
 
         let res = self
             .inner
@@ -345,7 +316,7 @@ impl HttpClient {
 
     /// Restore request [NUT-13]
     #[instrument(skip(self, request), fields(mint_url = %mint_url))]
-    pub async fn post_restore(
+    async fn post_restore(
         &self,
         mint_url: MintUrl,
         request: RestoreRequest,
@@ -367,3 +338,84 @@ impl HttpClient {
         }
     }
 }
+
+/// Http Client Methods
+#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
+#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
+pub trait HttpClientMethods: Debug {
+    /// Get Active Mint Keys [NUT-01]
+    async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error>;
+
+    /// Get Keyset Keys [NUT-01]
+    async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error>;
+
+    /// Get Keysets [NUT-02]
+    async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error>;
+
+    /// Mint Quote [NUT-04]
+    async fn post_mint_quote(
+        &self,
+        mint_url: MintUrl,
+        request: MintQuoteBolt11Request,
+    ) -> Result<MintQuoteBolt11Response, Error>;
+
+    /// Mint Quote status
+    async fn get_mint_quote_status(
+        &self,
+        mint_url: MintUrl,
+        quote_id: &str,
+    ) -> Result<MintQuoteBolt11Response, Error>;
+
+    /// Mint Tokens [NUT-04]
+    async fn post_mint(
+        &self,
+        mint_url: MintUrl,
+        request: MintBolt11Request,
+    ) -> Result<MintBolt11Response, Error>;
+
+    /// Melt Quote [NUT-05]
+    async fn post_melt_quote(
+        &self,
+        mint_url: MintUrl,
+        request: MeltQuoteBolt11Request,
+    ) -> Result<MeltQuoteBolt11Response, Error>;
+
+    /// Melt Quote Status
+    async fn get_melt_quote_status(
+        &self,
+        mint_url: MintUrl,
+        quote_id: &str,
+    ) -> Result<MeltQuoteBolt11Response, Error>;
+
+    /// Melt [NUT-05]
+    /// [Nut-08] Lightning fee return if outputs defined
+    async fn post_melt(
+        &self,
+        mint_url: MintUrl,
+        request: MeltBolt11Request,
+    ) -> Result<MeltQuoteBolt11Response, Error>;
+
+    /// Split Token [NUT-06]
+    async fn post_swap(
+        &self,
+        mint_url: MintUrl,
+        request: SwapRequest,
+    ) -> Result<SwapResponse, Error>;
+
+    /// Get Mint Info [NUT-06]
+    async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error>;
+
+    /// Spendable check [NUT-07]
+    async fn post_check_state(
+        &self,
+        mint_url: MintUrl,
+        request: CheckStateRequest,
+    ) -> Result<CheckStateResponse, Error>;
+
+    /// Restore request [NUT-13]
+    async fn post_restore(
+        &self,
+        mint_url: MintUrl,
+        request: RestoreRequest,
+    ) -> Result<RestoreResponse, Error>;
+}

+ 17 - 10
crates/cdk/src/wallet/melt.rs

@@ -4,6 +4,7 @@ use lightning_invoice::Bolt11Invoice;
 use tracing::instrument;
 
 use crate::nuts::nut00::ProofsMethods;
+use crate::nuts::{MeltBolt11Request, MeltQuoteBolt11Request, Mpp};
 use crate::{
     dhke::construct_proofs,
     nuts::{CurrencyUnit, MeltQuoteBolt11Response, PreMintSecrets, Proofs, State},
@@ -57,9 +58,17 @@ impl Wallet {
             _ => return Err(Error::UnitUnsupported),
         };
 
+        let options = mpp.map(|amount| Mpp { amount });
+
+        let quote_request = MeltQuoteBolt11Request {
+            request: Bolt11Invoice::from_str(&request)?,
+            unit: self.unit,
+            options,
+        };
+
         let quote_res = self
             .client
-            .post_melt_quote(self.mint_url.clone(), self.unit, invoice, mpp)
+            .post_melt_quote(self.mint_url.clone(), quote_request)
             .await?;
 
         if quote_res.amount != amount {
@@ -146,15 +155,13 @@ impl Wallet {
             proofs_total - quote_info.amount,
         )?;
 
-        let melt_response = self
-            .client
-            .post_melt(
-                self.mint_url.clone(),
-                quote_id.to_string(),
-                proofs.clone(),
-                Some(premint_secrets.blinded_messages()),
-            )
-            .await;
+        let request = MeltBolt11Request {
+            quote: quote_id.to_string(),
+            inputs: proofs.clone(),
+            outputs: Some(premint_secrets.blinded_messages()),
+        };
+
+        let melt_response = self.client.post_melt(self.mint_url.clone(), request).await;
 
         let melt_response = match melt_response {
             Ok(melt_response) => melt_response,

+ 14 - 2
crates/cdk/src/wallet/mint.rs

@@ -2,6 +2,7 @@ use tracing::instrument;
 
 use super::MintQuote;
 use crate::nuts::nut00::ProofsMethods;
+use crate::nuts::{MintBolt11Request, MintQuoteBolt11Request};
 use crate::{
     amount::SplitTarget,
     dhke::construct_proofs,
@@ -64,9 +65,15 @@ impl Wallet {
             }
         }
 
+        let request = MintQuoteBolt11Request {
+            amount,
+            unit,
+            description,
+        };
+
         let quote_res = self
             .client
-            .post_mint_quote(mint_url.clone(), amount, unit, description)
+            .post_mint_quote(mint_url.clone(), request)
             .await?;
 
         let quote = MintQuote {
@@ -212,9 +219,14 @@ impl Wallet {
             )?,
         };
 
+        let request = MintBolt11Request {
+            quote: quote_id.to_string(),
+            outputs: premint_secrets.blinded_messages(),
+        };
+
         let mint_res = self
             .client
-            .post_mint(self.mint_url.clone(), quote_id, premint_secrets.clone())
+            .post_mint(self.mint_url.clone(), request)
             .await?;
 
         let keys = self.get_keyset_keys(active_keyset_id).await?;

+ 5 - 4
crates/cdk/src/wallet/mod.rs

@@ -6,6 +6,7 @@ use std::sync::Arc;
 
 use bitcoin::bip32::Xpriv;
 use bitcoin::Network;
+use client::HttpClientMethods;
 use tracing::instrument;
 
 use crate::amount::SplitTarget;
@@ -55,7 +56,7 @@ pub struct Wallet {
     /// The targeted amount of proofs to have at each size
     pub target_proof_count: usize,
     xpriv: Xpriv,
-    client: HttpClient,
+    client: Arc<dyn HttpClientMethods + Send + Sync>,
 }
 
 impl Wallet {
@@ -88,7 +89,7 @@ impl Wallet {
         Ok(Self {
             mint_url: MintUrl::from_str(mint_url)?,
             unit,
-            client: HttpClient::new(),
+            client: Arc::new(HttpClient::new()),
             localstore,
             xpriv,
             target_proof_count: target_proof_count.unwrap_or(3),
@@ -96,8 +97,8 @@ impl Wallet {
     }
 
     /// Change HTTP client
-    pub fn set_client(&mut self, client: HttpClient) {
-        self.client = client;
+    pub fn set_client<C: HttpClientMethods + 'static + Send + Sync>(&mut self, client: C) {
+        self.client = Arc::new(client);
     }
 
     /// Fee required for proof set

+ 6 - 2
crates/cdk/src/wallet/proofs.rs

@@ -3,6 +3,7 @@ use std::collections::HashSet;
 use tracing::instrument;
 
 use crate::nuts::nut00::ProofsMethods;
+use crate::nuts::CheckStateRequest;
 use crate::{
     amount::SplitTarget,
     nuts::{Proof, ProofState, Proofs, PublicKey, SpendingConditions, State},
@@ -65,7 +66,7 @@ impl Wallet {
 
         let spendable = self
             .client
-            .post_check_state(self.mint_url.clone(), proof_ys)
+            .post_check_state(self.mint_url.clone(), CheckStateRequest { ys: proof_ys })
             .await?
             .states;
 
@@ -86,7 +87,10 @@ impl Wallet {
     pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
         let spendable = self
             .client
-            .post_check_state(self.mint_url.clone(), proofs.ys()?)
+            .post_check_state(
+                self.mint_url.clone(),
+                CheckStateRequest { ys: proofs.ys()? },
+            )
             .await?;
         let spent_ys: Vec<_> = spendable
             .states