Jelajahi Sumber

feat: replace async header with prefer in body (#1618)

User the async header caused issues with cors.
To fix this we are changing it so the prefer is in the body.
To support both for now the mint will accept either the header or the body.
tsk 1 Minggu lalu
induk
melakukan
fb3973750f

+ 17 - 0
crates/cashu/src/nuts/nut05.rs

@@ -92,6 +92,10 @@ pub struct MeltRequest<Q> {
     /// Blinded Message that can be used to return change [NUT-08]
     /// Blinded Message that can be used to return change [NUT-08]
     /// Amount field of BlindedMessages `SHOULD` be set to zero
     /// Amount field of BlindedMessages `SHOULD` be set to zero
     outputs: Option<Vec<BlindedMessage>>,
     outputs: Option<Vec<BlindedMessage>>,
+    /// Whether the client prefers asynchronous processing
+    #[serde(default)]
+    #[cfg_attr(feature = "swagger", schema(value_type = bool))]
+    prefer_async: bool,
 }
 }
 
 
 #[cfg(feature = "mint")]
 #[cfg(feature = "mint")]
@@ -103,6 +107,7 @@ impl TryFrom<MeltRequest<String>> for MeltRequest<QuoteId> {
             quote: QuoteId::from_str(&value.quote).map_err(|_e| Error::InvalidQuote)?,
             quote: QuoteId::from_str(&value.quote).map_err(|_e| Error::InvalidQuote)?,
             inputs: value.inputs,
             inputs: value.inputs,
             outputs: value.outputs,
             outputs: value.outputs,
+            prefer_async: value.prefer_async,
         })
         })
     }
     }
 }
 }
@@ -140,9 +145,21 @@ where
             quote,
             quote,
             inputs: inputs.without_dleqs(),
             inputs: inputs.without_dleqs(),
             outputs,
             outputs,
+            prefer_async: false,
         }
         }
     }
     }
 
 
+    /// Set the prefer_async flag for asynchronous processing
+    pub fn prefer_async(mut self, prefer_async: bool) -> Self {
+        self.prefer_async = prefer_async;
+        self
+    }
+
+    /// Get the prefer_async flag
+    pub fn is_prefer_async(&self) -> bool {
+        self.prefer_async
+    }
+
     /// Get quote
     /// Get quote
     pub fn quote(&self) -> &Q {
     pub fn quote(&self) -> &Q {
         &self.quote
         &self.quote

+ 4 - 1
crates/cdk-axum/src/custom_handlers.rs

@@ -338,7 +338,10 @@ pub async fn post_melt_custom(
         .await
         .await
         .map_err(into_response)?;
         .map_err(into_response)?;
 
 
-    let res = if prefer.respond_async {
+    // Check for async preference in either the Prefer header or the request body
+    let respond_async = prefer.respond_async || payload.is_prefer_async();
+
+    let res = if respond_async {
         // Asynchronous processing - return immediately after setup
         // Asynchronous processing - return immediately after setup
         state
         state
             .mint
             .mint

+ 1 - 2
crates/cdk-integration-tests/src/init_pure_tests.rs

@@ -139,11 +139,10 @@ impl MintConnector for DirectMintConnection {
             .map(Into::into)
             .map(Into::into)
     }
     }
 
 
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         &self,
         _method: &PaymentMethod,
         _method: &PaymentMethod,
         request: MeltRequest<String>,
         request: MeltRequest<String>,
-        _options: cdk::wallet::MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         let request_uuid = request.try_into().unwrap();
         let request_uuid = request.try_into().unwrap();
         self.mint.melt(&request_uuid).await.map(Into::into)
         self.mint.melt(&request_uuid).await.map(Into::into)

+ 6 - 23
crates/cdk/examples/mint-token-bolt12-with-custom-http.rs

@@ -56,22 +56,12 @@ impl HttpTransport for CustomHttp {
         panic!("Not supported");
         panic!("Not supported");
     }
     }
 
 
-    async fn http_get_with_headers<R>(
-        &self,
-        url: Url,
-        _auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, Error>
+    async fn http_get<R>(&self, url: Url, _auth: Option<AuthToken>) -> Result<R, Error>
     where
     where
         R: DeserializeOwned,
         R: DeserializeOwned,
     {
     {
-        let mut request = self.agent.get(url.as_str());
-
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
-        request
+        self.agent
+            .get(url.as_str())
             .call()
             .call()
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .body_mut()
             .body_mut()
@@ -79,25 +69,18 @@ impl HttpTransport for CustomHttp {
             .map_err(|e| Error::HttpError(None, e.to_string()))
             .map_err(|e| Error::HttpError(None, e.to_string()))
     }
     }
 
 
-    /// HTTP Post request
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         &self,
         url: Url,
         url: Url,
         _auth_token: Option<AuthToken>,
         _auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
         payload: &P,
     ) -> Result<R, Error>
     ) -> Result<R, Error>
     where
     where
         P: Serialize + ?Sized + Send + Sync,
         P: Serialize + ?Sized + Send + Sync,
         R: DeserializeOwned,
         R: DeserializeOwned,
     {
     {
-        let mut request = self.agent.post(url.as_str());
-
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
-        request
+        self.agent
+            .post(url.as_str())
             .send_json(payload)
             .send_json(payload)
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .map_err(|e| Error::HttpError(None, e.to_string()))?
             .body_mut()
             .body_mut()

+ 3 - 7
crates/cdk/src/wallet/melt/saga/mod.rs

@@ -50,7 +50,6 @@ use super::MeltConfirmOptions;
 use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::nut00::ProofsMethods;
 use crate::nuts::{MeltRequest, PreMintSecrets, Proofs, State};
 use crate::nuts::{MeltRequest, PreMintSecrets, Proofs, State};
 use crate::util::unix_time;
 use crate::util::unix_time;
-use crate::wallet::mint_connector::MeltOptions;
 use crate::wallet::saga::{add_compensation, new_compensations, Compensations};
 use crate::wallet::saga::{add_compensation, new_compensations, Compensations};
 use crate::{ensure_cdk, Amount, Error, Wallet};
 use crate::{ensure_cdk, Amount, Error, Wallet};
 
 
@@ -872,16 +871,13 @@ impl<'a> MeltSaga<'a, MeltRequested> {
             quote_info.id.clone(),
             quote_info.id.clone(),
             self.state_data.final_proofs.clone(),
             self.state_data.final_proofs.clone(),
             Some(self.state_data.premint_secrets.blinded_messages()),
             Some(self.state_data.premint_secrets.blinded_messages()),
-        );
+        )
+        .prefer_async(true);
 
 
         let melt_result = self
         let melt_result = self
             .wallet
             .wallet
             .client
             .client
-            .post_melt_with_options(
-                &quote_info.payment_method,
-                request,
-                MeltOptions { async_melt: true },
-            )
+            .post_melt(&quote_info.payment_method, request)
             .await;
             .await;
 
 
         let melt_response = match melt_result {
         let melt_response = match melt_result {

+ 7 - 30
crates/cdk/src/wallet/mint_connector/http_client.rs

@@ -15,7 +15,7 @@ use url::Url;
 use web_time::{Duration, Instant};
 use web_time::{Duration, Instant};
 
 
 use super::transport::Transport;
 use super::transport::Transport;
-use super::{Error, MeltOptions, MintConnector};
+use super::{Error, MintConnector};
 use crate::mint_url::MintUrl;
 use crate::mint_url::MintUrl;
 use crate::nuts::nut00::{KnownMethod, PaymentMethod};
 use crate::nuts::nut00::{KnownMethod, PaymentMethod};
 use crate::nuts::nut22::MintAuthRequest;
 use crate::nuts::nut22::MintAuthRequest;
@@ -116,7 +116,6 @@ where
         method: nut19::Method,
         method: nut19::Method,
         path: nut19::Path,
         path: nut19::Path,
         auth_token: Option<AuthToken>,
         auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
         payload: &P,
     ) -> Result<R, Error>
     ) -> Result<R, Error>
     where
     where
@@ -152,16 +151,8 @@ where
             };
             };
 
 
             let result = match method {
             let result = match method {
-                nut19::Method::Get => {
-                    transport
-                        .http_get_with_headers(url, auth_token.clone(), custom_headers)
-                        .await
-                }
-                nut19::Method::Post => {
-                    transport
-                        .http_post_with_headers(url, auth_token.clone(), custom_headers, payload)
-                        .await
-                }
+                nut19::Method::Get => transport.http_get(url, auth_token.clone()).await,
+                nut19::Method::Post => transport.http_post(url, auth_token.clone(), payload).await,
             };
             };
 
 
             if result.is_ok() {
             if result.is_ok() {
@@ -319,7 +310,7 @@ where
             PaymentMethod::Custom(m) => nut19::Path::custom_mint(m),
             PaymentMethod::Custom(m) => nut19::Path::custom_mint(m),
         };
         };
 
 
-        self.retriable_http_request(nut19::Method::Post, path, auth_token, &[], &request)
+        self.retriable_http_request(nut19::Method::Post, path, auth_token, &request)
             .await
             .await
     }
     }
 
 
@@ -365,11 +356,10 @@ where
     /// 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 = %self.mint_url))]
     #[instrument(skip(self, request), fields(mint_url = %self.mint_url))]
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         &self,
         method: &PaymentMethod,
         method: &PaymentMethod,
         request: MeltRequest<String>,
         request: MeltRequest<String>,
-        options: MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         let auth_token = self
         let auth_token = self
             .get_auth_token(Method::Post, RoutePath::Melt(method.to_string()))
             .get_auth_token(Method::Post, RoutePath::Melt(method.to_string()))
@@ -385,20 +375,8 @@ where
             PaymentMethod::Custom(m) => nut19::Path::custom_melt(m),
             PaymentMethod::Custom(m) => nut19::Path::custom_melt(m),
         };
         };
 
 
-        let custom_headers = if options.async_melt {
-            vec![("Prefer", "respond-async")]
-        } else {
-            vec![]
-        };
-
-        self.retriable_http_request(
-            nut19::Method::Post,
-            path,
-            auth_token,
-            &custom_headers,
-            &request,
-        )
-        .await
+        self.retriable_http_request(nut19::Method::Post, path, auth_token, &request)
+            .await
     }
     }
 
 
     /// Swap Token [NUT-03]
     /// Swap Token [NUT-03]
@@ -410,7 +388,6 @@ where
             nut19::Method::Post,
             nut19::Method::Post,
             nut19::Path::Swap,
             nut19::Path::Swap,
             auth_token,
             auth_token,
-            &[],
             &swap_request,
             &swap_request,
         )
         )
         .await
         .await

+ 0 - 18
crates/cdk/src/wallet/mint_connector/mod.rs

@@ -22,13 +22,6 @@ use crate::wallet::AuthWallet;
 pub mod http_client;
 pub mod http_client;
 pub mod transport;
 pub mod transport;
 
 
-/// Melt Options
-#[derive(Debug, Clone, Default)]
-pub struct MeltOptions {
-    /// Prefer respond-async
-    pub async_melt: bool,
-}
-
 /// Auth HTTP Client with async transport
 /// Auth HTTP Client with async transport
 pub type AuthHttpClient = http_client::AuthHttpClient<transport::Async>;
 pub type AuthHttpClient = http_client::AuthHttpClient<transport::Async>;
 /// Default Http Client with async transport (non-Tor)
 /// Default Http Client with async transport (non-Tor)
@@ -98,17 +91,6 @@ pub trait MintConnector: Debug {
         &self,
         &self,
         method: &PaymentMethod,
         method: &PaymentMethod,
         request: MeltRequest<String>,
         request: MeltRequest<String>,
-    ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        self.post_melt_with_options(method, request, MeltOptions::default())
-            .await
-    }
-
-    /// Melt [NUT-05] with options (e.g. async)
-    async fn post_melt_with_options(
-        &self,
-        method: &PaymentMethod,
-        request: MeltRequest<String>,
-        options: MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error>;
     ) -> Result<MeltQuoteBolt11Response<String>, Error>;
 
 
     /// Split Token [NUT-06]
     /// Split Token [NUT-06]

+ 2 - 45
crates/cdk/src/wallet/mint_connector/transport.rs

@@ -38,19 +38,6 @@ pub trait Transport: Default + Send + Sync + Debug + Clone {
         auth: Option<cdk_common::AuthToken>,
         auth: Option<cdk_common::AuthToken>,
     ) -> Result<R, super::Error>
     ) -> Result<R, super::Error>
     where
     where
-        R: serde::de::DeserializeOwned,
-    {
-        self.http_get_with_headers(url, auth, &[]).await
-    }
-
-    /// HTTP Get request with custom headers
-    async fn http_get_with_headers<R>(
-        &self,
-        url: url::Url,
-        auth: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, super::Error>
-    where
         R: serde::de::DeserializeOwned;
         R: serde::de::DeserializeOwned;
 
 
     /// HTTP Post request
     /// HTTP Post request
@@ -62,22 +49,6 @@ pub trait Transport: Default + Send + Sync + Debug + Clone {
     ) -> Result<R, super::Error>
     ) -> Result<R, super::Error>
     where
     where
         P: serde::Serialize + ?Sized + Send + Sync,
         P: serde::Serialize + ?Sized + Send + Sync,
-        R: serde::de::DeserializeOwned,
-    {
-        self.http_post_with_headers(url, auth_token, &[], payload)
-            .await
-    }
-
-    /// HTTP Post request with custom headers
-    async fn http_post_with_headers<P, R>(
-        &self,
-        url: url::Url,
-        auth_token: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
-        payload: &P,
-    ) -> Result<R, super::Error>
-    where
-        P: serde::Serialize + ?Sized + Send + Sync,
         R: serde::de::DeserializeOwned;
         R: serde::de::DeserializeOwned;
 }
 }
 
 
@@ -164,12 +135,7 @@ impl Transport for Async {
             .collect::<Vec<_>>())
             .collect::<Vec<_>>())
     }
     }
 
 
-    async fn http_get_with_headers<R>(
-        &self,
-        url: Url,
-        auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
-    ) -> Result<R, Error>
+    async fn http_get<R>(&self, url: Url, auth: Option<AuthToken>) -> Result<R, Error>
     where
     where
         R: DeserializeOwned,
         R: DeserializeOwned,
     {
     {
@@ -180,10 +146,6 @@ impl Transport for Async {
             request = request.header(auth.header_key(), auth.to_string());
             request = request.header(auth.header_key(), auth.to_string());
         }
         }
 
 
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
         let response = request
         let response = request
             .send()
             .send()
             .await
             .await
@@ -201,11 +163,10 @@ impl Transport for Async {
         })
         })
     }
     }
 
 
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         &self,
         url: Url,
         url: Url,
         auth_token: Option<AuthToken>,
         auth_token: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
         payload: &P,
     ) -> Result<R, Error>
     ) -> Result<R, Error>
     where
     where
@@ -219,10 +180,6 @@ impl Transport for Async {
             request = request.header(auth.header_key(), auth.to_string());
             request = request.header(auth.header_key(), auth.to_string());
         }
         }
 
 
-        for (key, value) in custom_headers {
-            request = request.header(*key, *value);
-        }
-
         let response = request
         let response = request
             .send()
             .send()
             .await
             .await

+ 5 - 13
crates/cdk/src/wallet/mint_connector/transport/tor_transport.rs

@@ -149,7 +149,6 @@ impl TorAsync {
         method: http::Method,
         method: http::Method,
         url: Url,
         url: Url,
         auth: Option<AuthToken>,
         auth: Option<AuthToken>,
-        custom_headers: &[(&str, &str)],
         mut body: Option<Vec<u8>>,
         mut body: Option<Vec<u8>>,
     ) -> Result<R, Error>
     ) -> Result<R, Error>
     where
     where
@@ -176,10 +175,6 @@ impl TorAsync {
         let mut builder = Request::builder().method(method).uri(uri);
         let mut builder = Request::builder().method(method).uri(uri);
         builder = builder.header(header::ACCEPT, "application/json");
         builder = builder.header(header::ACCEPT, "application/json");
 
 
-        for (key, value) in custom_headers {
-            builder = builder.header(*key, *value);
-        }
-
         let mut req = if let Some(b) = body.take() {
         let mut req = if let Some(b) = body.take() {
             builder
             builder
                 .header(http::header::CONTENT_TYPE, "application/json")
                 .header(http::header::CONTENT_TYPE, "application/json")
@@ -237,24 +232,21 @@ impl Transport for TorAsync {
         panic!("not supported with TorAsync transport");
         panic!("not supported with TorAsync transport");
     }
     }
 
 
-    async fn http_get_with_headers<R>(
+    async fn http_get<R>(
         &self,
         &self,
         url: url::Url,
         url: url::Url,
         auth: Option<cdk_common::AuthToken>,
         auth: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
     ) -> Result<R, super::super::Error>
     ) -> Result<R, super::super::Error>
     where
     where
         R: serde::de::DeserializeOwned,
         R: serde::de::DeserializeOwned,
     {
     {
-        self.request::<R>(Method::GET, url, auth, custom_headers, None)
-            .await
+        self.request::<R>(Method::GET, url, auth, None).await
     }
     }
 
 
-    async fn http_post_with_headers<P, R>(
+    async fn http_post<P, R>(
         &self,
         &self,
         url: url::Url,
         url: url::Url,
         auth_token: Option<cdk_common::AuthToken>,
         auth_token: Option<cdk_common::AuthToken>,
-        custom_headers: &[(&str, &str)],
         payload: &P,
         payload: &P,
     ) -> Result<R, super::super::Error>
     ) -> Result<R, super::super::Error>
     where
     where
@@ -262,7 +254,7 @@ impl Transport for TorAsync {
         R: serde::de::DeserializeOwned,
         R: serde::de::DeserializeOwned,
     {
     {
         let body = serde_json::to_vec(payload).map_err(|e| Error::Custom(e.to_string()))?;
         let body = serde_json::to_vec(payload).map_err(|e| Error::Custom(e.to_string()))?;
-        self.request::<R>(Method::POST, url, auth_token, custom_headers, Some(body))
+        self.request::<R>(Method::POST, url, auth_token, Some(body))
             .await
             .await
     }
     }
 
 
@@ -323,7 +315,7 @@ impl Transport for TorAsync {
         }
         }
 
 
         let resp: DnsResp = self
         let resp: DnsResp = self
-            .request::<DnsResp>(Method::GET, url, None, &[], None::<Vec<u8>>)
+            .request::<DnsResp>(Method::GET, url, None, None::<Vec<u8>>)
             .await?;
             .await?;
 
 
         let answers = resp.Answer.unwrap_or_default();
         let answers = resp.Answer.unwrap_or_default();

+ 1 - 2
crates/cdk/src/wallet/mod.rs

@@ -71,8 +71,7 @@ pub use cdk_common::wallet as types;
 pub use melt::{MeltConfirmOptions, MeltOutcome, PendingMelt, PreparedMelt};
 pub use melt::{MeltConfirmOptions, MeltOutcome, PendingMelt, PreparedMelt};
 pub use mint_connector::transport::Transport as HttpTransport;
 pub use mint_connector::transport::Transport as HttpTransport;
 pub use mint_connector::{
 pub use mint_connector::{
-    AuthHttpClient, HttpClient, LnurlPayInvoiceResponse, LnurlPayResponse, MeltOptions,
-    MintConnector,
+    AuthHttpClient, HttpClient, LnurlPayInvoiceResponse, LnurlPayResponse, MintConnector,
 };
 };
 pub use multi_mint_wallet::{MultiMintReceiveOptions, MultiMintSendOptions, MultiMintWallet};
 pub use multi_mint_wallet::{MultiMintReceiveOptions, MultiMintSendOptions, MultiMintWallet};
 #[cfg(feature = "nostr")]
 #[cfg(feature = "nostr")]

+ 1 - 2
crates/cdk/src/wallet/test_utils.rs

@@ -349,11 +349,10 @@ impl MintConnector for MockMintConnector {
         unimplemented!()
         unimplemented!()
     }
     }
 
 
-    async fn post_melt_with_options(
+    async fn post_melt(
         &self,
         &self,
         _method: &PaymentMethod,
         _method: &PaymentMethod,
         _request: MeltRequest<String>,
         _request: MeltRequest<String>,
-        _options: crate::wallet::MeltOptions,
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
     ) -> Result<MeltQuoteBolt11Response<String>, Error> {
         unimplemented!()
         unimplemented!()
     }
     }