From d83bd7c4ea5888b82f57c9e92c804609b9dad1fe Mon Sep 17 00:00:00 2001 From: Kendall Weihe Date: Mon, 30 Sep 2024 18:25:15 -0400 Subject: [PATCH] Add async everywhere (#126) --- Cargo.toml | 6 +- bindings/tbdex_uniffi/Cargo.toml | 1 + .../tbdex_uniffi/src/http_client/balances.rs | 9 +- .../tbdex_uniffi/src/http_client/exchanges.rs | 27 ++- .../tbdex_uniffi/src/http_client/offerings.rs | 6 +- bindings/tbdex_uniffi/src/lib.rs | 12 ++ bindings/tbdex_uniffi/src/messages/cancel.rs | 9 +- bindings/tbdex_uniffi/src/messages/close.rs | 9 +- bindings/tbdex_uniffi/src/messages/order.rs | 9 +- .../src/messages/order_instructions.rs | 9 +- .../tbdex_uniffi/src/messages/order_status.rs | 9 +- bindings/tbdex_uniffi/src/messages/quote.rs | 9 +- bindings/tbdex_uniffi/src/messages/rfq.rs | 9 +- .../tbdex_uniffi/src/resources/balance.rs | 8 +- .../tbdex_uniffi/src/resources/offering.rs | 8 +- bindings/tbdex_wasm/Cargo.toml | 2 + bindings/tbdex_wasm/src/errors.rs | 5 + bindings/tbdex_wasm/src/foreign_fetch.rs | 93 +++++---- bindings/tbdex_wasm/src/messages/cancel.rs | 4 +- bindings/tbdex_wasm/src/messages/close.rs | 4 +- bindings/tbdex_wasm/src/messages/order.rs | 4 +- .../src/messages/order_instructions.rs | 4 +- .../tbdex_wasm/src/messages/order_status.rs | 4 +- bindings/tbdex_wasm/src/messages/quote.rs | 4 +- bindings/tbdex_wasm/src/messages/rfq.rs | 7 +- bindings/tbdex_wasm/src/resources/balance.rs | 4 +- bindings/tbdex_wasm/src/resources/offering.rs | 4 +- bound/typescript/src/messages/cancel.ts | 4 +- bound/typescript/src/messages/close.ts | 4 +- .../src/messages/order-instructions.ts | 9 +- bound/typescript/src/messages/order-status.ts | 4 +- bound/typescript/src/messages/order.ts | 4 +- bound/typescript/src/messages/quote.ts | 4 +- bound/typescript/src/messages/rfq.ts | 8 +- bound/typescript/src/resources/balance.ts | 4 +- bound/typescript/src/resources/offering.ts | 4 +- bound/typescript/src/wasm/foreign-fetch.ts | 193 ++---------------- bound/typescript/tests/test-vectors.test.ts | 72 +++---- crates/tbdex/Cargo.toml | 1 + crates/tbdex/src/errors.rs | 2 + crates/tbdex/src/http_client/balances.rs | 8 +- crates/tbdex/src/http_client/exchanges.rs | 39 ++-- crates/tbdex/src/http_client/mod.rs | 21 +- crates/tbdex/src/http_client/offerings.rs | 8 +- crates/tbdex/src/json_schemas/mod.rs | 3 +- crates/tbdex/src/messages/cancel.rs | 5 +- crates/tbdex/src/messages/close.rs | 5 +- crates/tbdex/src/messages/order.rs | 5 +- .../tbdex/src/messages/order_instructions.rs | 5 +- crates/tbdex/src/messages/order_status.rs | 5 +- crates/tbdex/src/messages/quote.rs | 5 +- crates/tbdex/src/messages/rfq.rs | 22 +- crates/tbdex/src/resources/balance.rs | 5 +- crates/tbdex/src/resources/offering.rs | 5 +- crates/tbdex/src/signature.rs | 4 +- 55 files changed, 348 insertions(+), 389 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b48e307b..49bb4e74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ lazy_static = "1.5.0" serde = { version = "1.0.193", features = ["derive", "rc"] } serde_json = "1.0.108" thiserror = "1.0.50" -http-std = { git = "https://github.com/TBD54566975/web5-rs", rev = "005bd3c04574039ad7c3e41eeefae877b9895104" } -web5 = { git = "https://github.com/TBD54566975/web5-rs", rev = "005bd3c04574039ad7c3e41eeefae877b9895104" } -web5_uniffi_wrapper = { git = "https://github.com/TBD54566975/web5-rs", rev = "005bd3c04574039ad7c3e41eeefae877b9895104" } +http-std = { git = "https://github.com/TBD54566975/web5-rs", rev = "ef1a31970411686ed3a2fd502f5952048cc39876" } +web5 = { git = "https://github.com/TBD54566975/web5-rs", rev = "ef1a31970411686ed3a2fd502f5952048cc39876" } +web5_uniffi_wrapper = { git = "https://github.com/TBD54566975/web5-rs", rev = "ef1a31970411686ed3a2fd502f5952048cc39876" } diff --git a/bindings/tbdex_uniffi/Cargo.toml b/bindings/tbdex_uniffi/Cargo.toml index 879773a5..3fd96bfa 100644 --- a/bindings/tbdex_uniffi/Cargo.toml +++ b/bindings/tbdex_uniffi/Cargo.toml @@ -9,6 +9,7 @@ license-file.workspace = true [dependencies] serde_json = { workspace = true } tbdex = { path = "../../crates/tbdex" } +tokio = { version = "1.38.0", features = ["full"] } thiserror = { workspace = true } uniffi = { version = "0.27.1", features = ["cli"] } web5 = { workspace = true } diff --git a/bindings/tbdex_uniffi/src/http_client/balances.rs b/bindings/tbdex_uniffi/src/http_client/balances.rs index fd12ba0e..6ec9b62e 100644 --- a/bindings/tbdex_uniffi/src/http_client/balances.rs +++ b/bindings/tbdex_uniffi/src/http_client/balances.rs @@ -1,10 +1,13 @@ -use crate::{errors::Result, resources::balance::Balance}; +use crate::{errors::Result, get_rt, resources::balance::Balance}; use std::sync::{Arc, RwLock}; use web5_uniffi_wrapper::dids::bearer_did::BearerDid; pub fn get_balances(pfi_did_uri: String, bearer_did: Arc) -> Result>> { - let inner_balances = - tbdex::http_client::balances::get_balances(&pfi_did_uri, &bearer_did.0.clone())?; + let rt = get_rt()?; + let inner_balances = rt.block_on(tbdex::http_client::balances::get_balances( + &pfi_did_uri, + &bearer_did.0.clone(), + ))?; let balances = inner_balances .into_iter() diff --git a/bindings/tbdex_uniffi/src/http_client/exchanges.rs b/bindings/tbdex_uniffi/src/http_client/exchanges.rs index 4500b4d9..7101da07 100644 --- a/bindings/tbdex_uniffi/src/http_client/exchanges.rs +++ b/bindings/tbdex_uniffi/src/http_client/exchanges.rs @@ -1,5 +1,6 @@ use crate::{ errors::Result, + get_rt, messages::{ cancel::Cancel, close::Close, order::Order, order_instructions::OrderInstructions, order_status::OrderStatus, quote::Quote, rfq::Rfq, @@ -48,17 +49,27 @@ impl Exchange { } pub fn create_exchange(rfq: Arc, reply_to: Option) -> Result<()> { - tbdex::http_client::exchanges::create_exchange(&rfq.to_inner()?, reply_to)?; + let rt = get_rt()?; + rt.block_on(tbdex::http_client::exchanges::create_exchange( + &rfq.to_inner()?, + reply_to, + ))?; Ok(()) } pub fn submit_order(order: Arc) -> Result<()> { - tbdex::http_client::exchanges::submit_order(&order.get_data()?)?; + let rt = get_rt()?; + rt.block_on(tbdex::http_client::exchanges::submit_order( + &order.get_data()?, + ))?; Ok(()) } pub fn submit_cancel(cancel: Arc) -> Result<()> { - tbdex::http_client::exchanges::submit_cancel(&cancel.get_data()?)?; + let rt = get_rt()?; + rt.block_on(tbdex::http_client::exchanges::submit_cancel( + &cancel.get_data()?, + ))?; Ok(()) } @@ -67,11 +78,12 @@ pub fn get_exchange( bearer_did: Arc, exchange_id: String, ) -> Result { - let inner_exchange = tbdex::http_client::exchanges::get_exchange( + let rt = get_rt()?; + let inner_exchange = rt.block_on(tbdex::http_client::exchanges::get_exchange( &pfi_did_uri, &bearer_did.0.clone(), &exchange_id, - )?; + ))?; Ok(Exchange::from_inner(inner_exchange)) } @@ -81,10 +93,11 @@ pub fn get_exchange_ids( bearer_did: Arc, query_params: Option, ) -> Result> { - let exchange_ids = tbdex::http_client::exchanges::get_exchange_ids( + let rt = get_rt()?; + let exchange_ids = rt.block_on(tbdex::http_client::exchanges::get_exchange_ids( &pfi_did_uri, &bearer_did.0.clone(), query_params, - )?; + ))?; Ok(exchange_ids) } diff --git a/bindings/tbdex_uniffi/src/http_client/offerings.rs b/bindings/tbdex_uniffi/src/http_client/offerings.rs index b1b81221..b76f4b7b 100644 --- a/bindings/tbdex_uniffi/src/http_client/offerings.rs +++ b/bindings/tbdex_uniffi/src/http_client/offerings.rs @@ -1,8 +1,10 @@ -use crate::{errors::Result, resources::offering::Offering}; +use crate::{errors::Result, get_rt, resources::offering::Offering}; use std::sync::{Arc, RwLock}; pub fn get_offerings(pfi_did_uri: String) -> Result>> { - let inner_offerings = tbdex::http_client::offerings::get_offerings(&pfi_did_uri)?; + let rt = get_rt()?; + let inner_offerings = + rt.block_on(tbdex::http_client::offerings::get_offerings(&pfi_did_uri))?; let offerings = inner_offerings .into_iter() diff --git a/bindings/tbdex_uniffi/src/lib.rs b/bindings/tbdex_uniffi/src/lib.rs index ea1524c8..817bebe8 100644 --- a/bindings/tbdex_uniffi/src/lib.rs +++ b/bindings/tbdex_uniffi/src/lib.rs @@ -40,6 +40,7 @@ use crate::{ offering::{data::Offering as OfferingData, Offering}, }, }; +use errors::Result; use tbdex::{ http::{ErrorDetail as ErrorDetailData, ErrorResponseBody as ErrorResponseBodyData}, http_client::exchanges::GetExchangeIdsQueryParams as GetExchangeIdsQueryParamsData, @@ -64,6 +65,7 @@ use tbdex::{ ResourceKind, ResourceMetadata as ResourceMetadataData, }, }; +use tokio::runtime::Runtime; use web5::{ crypto::jwk::Jwk as JwkData, dids::{ @@ -81,4 +83,14 @@ use web5_uniffi_wrapper::{ errors::Web5Error, }; +pub fn get_rt() -> Result { + let rt = Runtime::new().map_err(|e| { + tbdex::errors::TbdexError::AsyncRuntime(format!( + "unable to instantiate tokio runtime {}", + e + )) + })?; + Ok(rt) +} + uniffi::include_scaffolding!("tbdex"); diff --git a/bindings/tbdex_uniffi/src/messages/cancel.rs b/bindings/tbdex_uniffi/src/messages/cancel.rs index af56fea8..05d55357 100644 --- a/bindings/tbdex_uniffi/src/messages/cancel.rs +++ b/bindings/tbdex_uniffi/src/messages/cancel.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -48,7 +51,7 @@ impl Cancel { pub fn verify(&self) -> Result<()> { let cancel = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(cancel.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(cancel.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/close.rs b/bindings/tbdex_uniffi/src/messages/close.rs index 5e493e81..da486301 100644 --- a/bindings/tbdex_uniffi/src/messages/close.rs +++ b/bindings/tbdex_uniffi/src/messages/close.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -48,7 +51,7 @@ impl Close { pub fn verify(&self) -> Result<()> { let close = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(close.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(close.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/order.rs b/bindings/tbdex_uniffi/src/messages/order.rs index 3feb46cf..222a8cd2 100644 --- a/bindings/tbdex_uniffi/src/messages/order.rs +++ b/bindings/tbdex_uniffi/src/messages/order.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -47,7 +50,7 @@ impl Order { pub fn verify(&self) -> Result<()> { let order = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(order.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(order.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/order_instructions.rs b/bindings/tbdex_uniffi/src/messages/order_instructions.rs index 78f43323..1b7505db 100644 --- a/bindings/tbdex_uniffi/src/messages/order_instructions.rs +++ b/bindings/tbdex_uniffi/src/messages/order_instructions.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -51,7 +54,7 @@ impl OrderInstructions { pub fn verify(&self) -> Result<()> { let order_instructions = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(order_instructions.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(order_instructions.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/order_status.rs b/bindings/tbdex_uniffi/src/messages/order_status.rs index dcc2436f..79320399 100644 --- a/bindings/tbdex_uniffi/src/messages/order_status.rs +++ b/bindings/tbdex_uniffi/src/messages/order_status.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -49,7 +52,7 @@ impl OrderStatus { pub fn verify(&self) -> Result<()> { let order_status = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(order_status.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(order_status.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/quote.rs b/bindings/tbdex_uniffi/src/messages/quote.rs index 74bc084b..b02557f1 100644 --- a/bindings/tbdex_uniffi/src/messages/quote.rs +++ b/bindings/tbdex_uniffi/src/messages/quote.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -48,7 +51,7 @@ impl Quote { pub fn verify(&self) -> Result<()> { let quote = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(quote.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(quote.verify())?) } } diff --git a/bindings/tbdex_uniffi/src/messages/rfq.rs b/bindings/tbdex_uniffi/src/messages/rfq.rs index a0db713b..fcae60d3 100644 --- a/bindings/tbdex_uniffi/src/messages/rfq.rs +++ b/bindings/tbdex_uniffi/src/messages/rfq.rs @@ -1,5 +1,6 @@ use crate::{ errors::{Result, TbdexError}, + get_rt, resources::offering::Offering, }; use std::sync::{Arc, RwLock}; @@ -72,14 +73,14 @@ impl Rfq { pub fn verify(&self) -> Result<()> { let rfq = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(rfq.verify()?) + let rt = get_rt()?; + Ok(rt.block_on(rfq.verify())?) } pub fn verify_offering_requirements(&self, offering: Arc) -> Result<()> { let rfq = self.0.read().map_err(TbdexError::from_poison_error)?; - - Ok(rfq.verify_offering_requirements(&offering.to_inner()?)?) + let rt = get_rt()?; + Ok(rt.block_on(rfq.verify_offering_requirements(&offering.to_inner()?))?) } pub fn verify_all_private_data(&self) -> Result<()> { diff --git a/bindings/tbdex_uniffi/src/resources/balance.rs b/bindings/tbdex_uniffi/src/resources/balance.rs index 482bcae2..d590fa49 100644 --- a/bindings/tbdex_uniffi/src/resources/balance.rs +++ b/bindings/tbdex_uniffi/src/resources/balance.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -49,7 +52,8 @@ impl Balance { pub fn verify(&self) -> Result<()> { let inner_balance = self.0.read().map_err(TbdexError::from_poison_error)?; - inner_balance.verify()?; + let rt = get_rt()?; + rt.block_on(inner_balance.verify())?; Ok(()) } } diff --git a/bindings/tbdex_uniffi/src/resources/offering.rs b/bindings/tbdex_uniffi/src/resources/offering.rs index 24f8f38e..3c438bc2 100644 --- a/bindings/tbdex_uniffi/src/resources/offering.rs +++ b/bindings/tbdex_uniffi/src/resources/offering.rs @@ -1,4 +1,7 @@ -use crate::errors::{Result, TbdexError}; +use crate::{ + errors::{Result, TbdexError}, + get_rt, +}; use std::sync::{Arc, RwLock}; use tbdex::{ json::{FromJson, ToJson}, @@ -57,7 +60,8 @@ impl Offering { pub fn verify(&self) -> Result<()> { let inner_offering = self.0.read().map_err(TbdexError::from_poison_error)?; - inner_offering.verify()?; + let rt = get_rt()?; + rt.block_on(inner_offering.verify())?; Ok(()) } } diff --git a/bindings/tbdex_wasm/Cargo.toml b/bindings/tbdex_wasm/Cargo.toml index 19915dbb..86d2ee3b 100644 --- a/bindings/tbdex_wasm/Cargo.toml +++ b/bindings/tbdex_wasm/Cargo.toml @@ -7,6 +7,7 @@ repository.workspace = true license-file.workspace = true [dependencies] +async-trait = "0.1.83" http-std = { workspace = true } js-sys = "0.3.70" lazy_static = { workspace = true } @@ -15,6 +16,7 @@ serde_json = { workspace = true } serde-wasm-bindgen = "0.6.5" tbdex = { path = "../../crates/tbdex" } wasm-bindgen = "0.2.93" +wasm-bindgen-futures = "0.4.43" web5 = { workspace = true } web-sys = { version = "0.3.70", features = ["console"] } diff --git a/bindings/tbdex_wasm/src/errors.rs b/bindings/tbdex_wasm/src/errors.rs index e104d2da..09ede111 100644 --- a/bindings/tbdex_wasm/src/errors.rs +++ b/bindings/tbdex_wasm/src/errors.rs @@ -1,3 +1,4 @@ +use http_std::Error as HttpStdError; use serde::Serialize; use serde_wasm_bindgen::to_value; use tbdex::errors::TbdexError; @@ -48,3 +49,7 @@ pub fn map_err(err: TbdexError) -> JsValue { pub fn map_web5_err(err: Web5Error) -> JsValue { map_err(TbdexError::Web5Error(err)) } + +pub fn map_http_std_err(err: HttpStdError) -> JsValue { + map_err(TbdexError::HttpStdError(err)) +} diff --git a/bindings/tbdex_wasm/src/foreign_fetch.rs b/bindings/tbdex_wasm/src/foreign_fetch.rs index ba78d9d0..68be944b 100644 --- a/bindings/tbdex_wasm/src/foreign_fetch.rs +++ b/bindings/tbdex_wasm/src/foreign_fetch.rs @@ -1,18 +1,46 @@ -use crate::errors::{map_err, Result}; -use http_std::{Client, FetchOptions, Method, Response}; -use std::{collections::HashMap, sync::Arc}; -use tbdex::errors::TbdexError; +use crate::errors::{map_http_std_err, Result}; +use async_trait::async_trait; +use http_std::{Client, Error as HttpStdError, FetchOptions, Method, Response}; +use js_sys::Promise; +use serde::Deserialize; +use std::{ + collections::HashMap, + future::Future, + pin::Pin, + str::FromStr, + sync::Arc, + task::{Context, Poll}, +}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +use wasm_bindgen_futures::JsFuture; #[wasm_bindgen] extern "C" { #[wasm_bindgen( - typescript_type = "{ fetch: (url: string, options?: WasmFetchOptions) => WasmResponse }" + typescript_type = "{ fetch: (url: string, options?: WasmFetchOptions) => Promise }" )] pub type ForeignFetch; #[wasm_bindgen(method)] - fn fetch(this: &ForeignFetch, url: &str, options: Option) -> WasmResponse; + fn fetch(this: &ForeignFetch, url: &str, options: Option) -> Promise; +} + +struct SendJsFuture(JsFuture); + +/** + * TODO + * [KW]: + * this is not thread safe and could cause issues + * the solution is to implement message passing across threads + */ +unsafe impl Send for SendJsFuture {} + +impl Future for SendJsFuture { + type Output = std::result::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unsafe { self.map_unchecked_mut(|s| &mut s.0) }.poll(cx) + } } pub struct ConcreteForeignFetch(ForeignFetch); @@ -26,11 +54,30 @@ pub struct ConcreteForeignFetch(ForeignFetch); unsafe impl Send for ConcreteForeignFetch {} unsafe impl Sync for ConcreteForeignFetch {} +#[async_trait] impl Client for ConcreteForeignFetch { - fn fetch(&self, url: &str, options: Option) -> http_std::Result { + async fn fetch(&self, url: &str, options: Option) -> http_std::Result { let wasm_options = options.map(WasmFetchOptions::from); - let wasm_response = self.0.fetch(url, wasm_options); - Ok(wasm_response.into()) + + let wasm_response_promise = self.0.fetch(url, wasm_options); + let response_jsvalue = SendJsFuture(JsFuture::from(wasm_response_promise)) + .await + .map_err(|e| { + HttpStdError::Unknown(format!( + "rust future resolution error {}", + js_sys::JSON::stringify(&e) + .unwrap_or_else(|_| JsValue::from_str("null").into()) + .as_string() + .unwrap_or_else(|| "null".to_string()) + )) + })?; + + let response = + serde_wasm_bindgen::from_value::(response_jsvalue).map_err(|e| { + HttpStdError::Unknown(format!("rust WasmResponse from JsValue error {}", e)) + })?; + + Ok(response) } } @@ -50,31 +97,6 @@ impl From for WasmFetchOptions { } } -// TODO move to web5-rs -// impl FromStr for Method { -// type Err = TbdexError; - -// fn from_str(s: &str) -> Result { -// match s.to_ascii_uppercase().as_ref() { -// "GET" => Ok(Method::Get), -// "POST" => Ok(Method::Post), -// "PUT" => Ok(Method::Put), -// _ => return Err(TbdexError::HttpClient(format!("unknown method {}", s))), -// } -// } -// } -fn method_from_str(method: &str) -> Result { - match method.to_ascii_uppercase().as_ref() { - "GET" => Ok(Method::Get), - "POST" => Ok(Method::Post), - "PUT" => Ok(Method::Put), - _ => Err(map_err(TbdexError::HttpClient(format!( - "unknown method {}", - method - )))), - } -} - #[wasm_bindgen] impl WasmFetchOptions { #[wasm_bindgen(constructor)] @@ -84,7 +106,7 @@ impl WasmFetchOptions { body: Option>, ) -> Result { let method = if let Some(m) = method { - Some(method_from_str(&m)?) + Some(Method::from_str(&m).map_err(map_http_std_err)?) } else { None }; @@ -123,6 +145,7 @@ impl WasmFetchOptions { } } +#[derive(Deserialize)] #[wasm_bindgen] pub struct WasmResponse { inner: Response, diff --git a/bindings/tbdex_wasm/src/messages/cancel.rs b/bindings/tbdex_wasm/src/messages/cancel.rs index 242c6206..a381faf0 100644 --- a/bindings/tbdex_wasm/src/messages/cancel.rs +++ b/bindings/tbdex_wasm/src/messages/cancel.rs @@ -58,8 +58,8 @@ impl WasmCancel { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/close.rs b/bindings/tbdex_wasm/src/messages/close.rs index 4e0e2640..fc0b7109 100644 --- a/bindings/tbdex_wasm/src/messages/close.rs +++ b/bindings/tbdex_wasm/src/messages/close.rs @@ -58,8 +58,8 @@ impl WasmClose { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/order.rs b/bindings/tbdex_wasm/src/messages/order.rs index ac8a1c4e..c2adf0a6 100644 --- a/bindings/tbdex_wasm/src/messages/order.rs +++ b/bindings/tbdex_wasm/src/messages/order.rs @@ -56,8 +56,8 @@ impl WasmOrder { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/order_instructions.rs b/bindings/tbdex_wasm/src/messages/order_instructions.rs index 7d5eeaee..4a4502fb 100644 --- a/bindings/tbdex_wasm/src/messages/order_instructions.rs +++ b/bindings/tbdex_wasm/src/messages/order_instructions.rs @@ -69,8 +69,8 @@ impl WasmOrderInstructions { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/order_status.rs b/bindings/tbdex_wasm/src/messages/order_status.rs index c7f73893..1f069cec 100644 --- a/bindings/tbdex_wasm/src/messages/order_status.rs +++ b/bindings/tbdex_wasm/src/messages/order_status.rs @@ -64,8 +64,8 @@ impl WasmOrderStatus { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/quote.rs b/bindings/tbdex_wasm/src/messages/quote.rs index 076d3e37..7f86f56b 100644 --- a/bindings/tbdex_wasm/src/messages/quote.rs +++ b/bindings/tbdex_wasm/src/messages/quote.rs @@ -58,8 +58,8 @@ impl WasmQuote { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/messages/rfq.rs b/bindings/tbdex_wasm/src/messages/rfq.rs index b9a1aa96..e2e70b1d 100644 --- a/bindings/tbdex_wasm/src/messages/rfq.rs +++ b/bindings/tbdex_wasm/src/messages/rfq.rs @@ -74,14 +74,15 @@ impl WasmRfq { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen] - pub fn verify_offering_requirements(&self, offering: WasmOffering) -> Result<()> { + pub async fn verify_offering_requirements(&self, offering: WasmOffering) -> Result<()> { self.inner .verify_offering_requirements(&offering.into()) + .await .map_err(map_err) } diff --git a/bindings/tbdex_wasm/src/resources/balance.rs b/bindings/tbdex_wasm/src/resources/balance.rs index 066c1028..c7c15e46 100644 --- a/bindings/tbdex_wasm/src/resources/balance.rs +++ b/bindings/tbdex_wasm/src/resources/balance.rs @@ -54,8 +54,8 @@ impl WasmBalance { } #[wasm_bindgen] - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bindings/tbdex_wasm/src/resources/offering.rs b/bindings/tbdex_wasm/src/resources/offering.rs index 63160256..61b4fe99 100644 --- a/bindings/tbdex_wasm/src/resources/offering.rs +++ b/bindings/tbdex_wasm/src/resources/offering.rs @@ -60,8 +60,8 @@ impl WasmOffering { self.inner.sign(&bearer_did.into()).map_err(map_err) } - pub fn verify(&self) -> Result<()> { - self.inner.verify().map_err(map_err) + pub async fn verify(&self) -> Result<()> { + self.inner.verify().await.map_err(map_err) } #[wasm_bindgen(getter)] diff --git a/bound/typescript/src/messages/cancel.ts b/bound/typescript/src/messages/cancel.ts index 04d6036f..e394ea05 100644 --- a/bound/typescript/src/messages/cancel.ts +++ b/bound/typescript/src/messages/cancel.ts @@ -88,9 +88,9 @@ export class Cancel { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/close.ts b/bound/typescript/src/messages/close.ts index 4a81fb78..68b6b825 100644 --- a/bound/typescript/src/messages/close.ts +++ b/bound/typescript/src/messages/close.ts @@ -88,9 +88,9 @@ export class Close { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/order-instructions.ts b/bound/typescript/src/messages/order-instructions.ts index b75f6ae3..833fe0c1 100644 --- a/bound/typescript/src/messages/order-instructions.ts +++ b/bound/typescript/src/messages/order-instructions.ts @@ -1,7 +1,10 @@ import { BearerDid } from "../bearer-did"; import { tbdexError } from "../errors"; import wasm from "../wasm"; -import { MessageMetadata, OrderInstructionsData } from "../wasm/generated-mappings"; +import { + MessageMetadata, + OrderInstructionsData, +} from "../wasm/generated-mappings"; export class OrderInstructions { readonly metadata: MessageMetadata; @@ -96,9 +99,9 @@ export class OrderInstructions { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/order-status.ts b/bound/typescript/src/messages/order-status.ts index 63e63a05..77710cb7 100644 --- a/bound/typescript/src/messages/order-status.ts +++ b/bound/typescript/src/messages/order-status.ts @@ -92,9 +92,9 @@ export class OrderStatus { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/order.ts b/bound/typescript/src/messages/order.ts index 06d3118f..c1e63357 100644 --- a/bound/typescript/src/messages/order.ts +++ b/bound/typescript/src/messages/order.ts @@ -80,9 +80,9 @@ export class Order { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/quote.ts b/bound/typescript/src/messages/quote.ts index cd0fcbb0..a2e304c0 100644 --- a/bound/typescript/src/messages/quote.ts +++ b/bound/typescript/src/messages/quote.ts @@ -88,9 +88,9 @@ export class Quote { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/messages/rfq.ts b/bound/typescript/src/messages/rfq.ts index 38c63781..203cbaaf 100644 --- a/bound/typescript/src/messages/rfq.ts +++ b/bound/typescript/src/messages/rfq.ts @@ -109,17 +109,17 @@ export class Rfq { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } }; - verifyOfferingRequirements = (offering: Offering) => { + verifyOfferingRequirements = async (offering: Offering) => { try { - this.toWASM().verify_offering_requirements(offering.toWASM()); + await this.toWASM().verify_offering_requirements(offering.toWASM()); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/resources/balance.ts b/bound/typescript/src/resources/balance.ts index 78d6a0b2..df479355 100644 --- a/bound/typescript/src/resources/balance.ts +++ b/bound/typescript/src/resources/balance.ts @@ -82,9 +82,9 @@ export class Balance { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/resources/offering.ts b/bound/typescript/src/resources/offering.ts index e0335142..74489f72 100644 --- a/bound/typescript/src/resources/offering.ts +++ b/bound/typescript/src/resources/offering.ts @@ -83,9 +83,9 @@ export class Offering { } }; - verify = () => { + verify = async () => { try { - this.toWASM().verify(); + await this.toWASM().verify(); } catch (error) { throw tbdexError(error); } diff --git a/bound/typescript/src/wasm/foreign-fetch.ts b/bound/typescript/src/wasm/foreign-fetch.ts index 6f35afb3..efbcf29e 100644 --- a/bound/typescript/src/wasm/foreign-fetch.ts +++ b/bound/typescript/src/wasm/foreign-fetch.ts @@ -1,190 +1,33 @@ import wasm from "."; import { FetchOptions, Response } from "./generated-mappings"; -let workerThreads: any | undefined; - -const IS_NODEJS = - typeof process !== "undefined" && - process.versions != null && - process.versions.node != null; - -if (IS_NODEJS) { - try { - workerThreads = await import("worker_threads"); - } catch (err) { - console.error("Failed to load worker_threads in Node.js environment:", err); - } -} - export const ForeignFetch = { - fetch: ( + fetch: async ( url: string, wasmFetchOptions?: wasm.WasmFetchOptions - ): wasm.WasmResponse => { - return fetchSync(url, wasmFetchOptions); - }, -}; - -const fetchSync = ( - url: string, - wasmFetchOptions?: wasm.WasmFetchOptions -): wasm.WasmResponse => { - if (IS_NODEJS) { - const response = fetchSyncNode( - url, - wasmFetchOptions ? FetchOptions.fromWASM(wasmFetchOptions) : undefined - ); - return Response.toWASM(response); - } else { - const response = fetchSyncBrowser( - url, - wasmFetchOptions ? FetchOptions.fromWASM(wasmFetchOptions) : undefined - ); - return Response.toWASM(response); - } -}; - -const fetchSyncNode = (url: string, options?: FetchOptions): Response => { - const statusBuffer = new SharedArrayBuffer(4); - const headersBuffer = new SharedArrayBuffer(1024); - const bodyBuffer = new SharedArrayBuffer(1024 * 10); - const headersLengthBuffer = new SharedArrayBuffer(4); - const bodyLengthBuffer = new SharedArrayBuffer(4); - - const statusArray = new Int32Array(statusBuffer); - const headersArray = new Uint8Array(headersBuffer); - const bodyArray = new Uint8Array(bodyBuffer); - const headersLengthArray = new Int32Array(headersLengthBuffer); - const bodyLengthArray = new Int32Array(bodyLengthBuffer); - - const workerCode = ` - const { parentPort } = require('worker_threads'); - const statusArray = new Int32Array(require('worker_threads').workerData.statusBuffer); - const headersArray = new Uint8Array(require('worker_threads').workerData.headersBuffer); - const bodyArray = new Uint8Array(require('worker_threads').workerData.bodyBuffer); - const headersLengthArray = new Int32Array(require('worker_threads').workerData.headersLengthBuffer); - const bodyLengthArray = new Int32Array(require('worker_threads').workerData.bodyLengthBuffer); - - parentPort.on('message', async (options) => { - try { - const { method, headers, body } = options; - - const response = await fetch(options.url, { - method: method || 'GET', - headers: headers, - body: body ? Buffer.from(body) : undefined - }); - - const responseBody = new Uint8Array(await response.arrayBuffer()); - const responseHeaders = JSON.stringify(Array.from(response.headers.entries())); // Convert headers to JSON - - // Write status code to shared buffer - Atomics.store(statusArray, 0, response.status); - - // Write headers to the headers buffer - const encoder = new TextEncoder(); - const encodedHeaders = encoder.encode(responseHeaders); - headersArray.set(encodedHeaders, 0); // Store headers starting at index 0 - - // Write header length - Atomics.store(headersLengthArray, 0, encodedHeaders.length); // Store headers length - - // Write body to the body buffer - bodyArray.set(responseBody, 0); // Store body starting at index 0 - - // Write body length - Atomics.store(bodyLengthArray, 0, responseBody.length); // Store body length - - // Notify the main thread that the response is ready - Atomics.notify(statusArray, 0); - } catch (error) { - console.error('Worker fetch error:', error); - Atomics.store(statusArray, 0, -1); // Indicate failure - Atomics.notify(statusArray, 0); - } - }); - `; + ): Promise => { + const options = wasmFetchOptions + ? FetchOptions.fromWASM(wasmFetchOptions) + : undefined; - if (workerThreads === undefined) - throw Error("worker_threads must be imported"); - - if (workerThreads.isMainThread) { - const worker = new workerThreads.Worker(workerCode, { - eval: true, - workerData: { - statusBuffer, - headersBuffer, - bodyBuffer, - headersLengthBuffer, - bodyLengthBuffer, - }, - }); - - worker.postMessage({ - url: url, - method: options?.method, + const fetchResponse = await fetch(url, { + method: options?.method || "GET", headers: options?.headers, - body: options?.body, + body: options?.body ? Buffer.from(options.body) : undefined, }); - Atomics.wait(statusArray, 0, 0); - - const statusCode = Atomics.load(statusArray, 0); - - if (statusCode === -1) { - throw new Error("Fetch request failed in the worker"); - } - - const headersLength = Atomics.load(headersLengthArray, 0); - const bodyLength = Atomics.load(bodyLengthArray, 0); - - const decoder = new TextDecoder(); - const decodedHeaders = decoder.decode(headersArray.slice(0, headersLength)); - const headers = JSON.parse(decodedHeaders); - - const body = bodyArray.slice(0, bodyLength); + const body = new Uint8Array(await fetchResponse.arrayBuffer()); + const headers: Record = {}; + fetchResponse.headers.forEach((value, key) => { + headers[key] = value; + }); const response: Response = { - statusCode, - headers, - body, + statusCode: fetchResponse.status, + body: body, + headers: headers, }; - return response; - } - - throw Error("must be main thread"); -}; - -const fetchSyncBrowser = ( - url: string, - fetchOptions?: FetchOptions -): Response => { - const xhr = new XMLHttpRequest(); - xhr.open(fetchOptions?.method || "GET", url, false); - - if (fetchOptions?.headers) { - Object.entries(fetchOptions.headers).forEach(([key, value]) => { - xhr.setRequestHeader(key, value as string); - }); - } - - xhr.overrideMimeType("text/plain; charset=x-user-defined"); - - xhr.send(fetchOptions?.body ? new Uint8Array(fetchOptions.body) : null); - - const responseText = xhr.responseText; - const length = responseText.length; - const body = new Uint8Array(length); - for (let i = 0; i < length; i++) { - body[i] = responseText.charCodeAt(i) & 0xff; - } - - const response: Response = { - statusCode: xhr.status, - headers: xhr.getAllResponseHeaders(), - body: body, - }; - - return response; + return Response.toWASM(response); + }, }; diff --git a/bound/typescript/tests/test-vectors.test.ts b/bound/typescript/tests/test-vectors.test.ts index d82cdfd8..b86a392e 100644 --- a/bound/typescript/tests/test-vectors.test.ts +++ b/bound/typescript/tests/test-vectors.test.ts @@ -32,7 +32,7 @@ describe("test vectors", () => { }); describe("offering", () => { - it("should parse", () => { + it("should parse", async () => { const input = OfferingVector.input; const offering = Offering.fromJSONString(input); expect(offering.metadata).to.deep.equal(OfferingVector.output.metadata); @@ -43,10 +43,10 @@ describe("test vectors", () => { const offeringJSON = JSON.parse(offeringJSONString); expect(offeringJSON).to.deep.equal(OfferingVector.output); - offering.verify(); + await offering.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const offering = Offering.create( OfferingVector.output.metadata.from, OfferingVector.output.data, @@ -54,12 +54,12 @@ describe("test vectors", () => { ); offering.sign(bearerDID); - offering.verify(); + await offering.verify(); }); }); describe("balance", () => { - it("should parse", () => { + it("should parse", async () => { const input = BalanceVector.input; const balance = Balance.fromJSONString(input); expect(balance.metadata).to.deep.equal(BalanceVector.output.metadata); @@ -70,10 +70,10 @@ describe("test vectors", () => { const balanceJSON = JSON.parse(balanceJSONString); expect(balanceJSON).to.deep.equal(BalanceVector.output); - balance.verify(); + await balance.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const balance = Balance.create( BalanceVector.output.metadata.from, BalanceVector.output.data, @@ -81,12 +81,12 @@ describe("test vectors", () => { ); balance.sign(bearerDID); - balance.verify(); + await balance.verify(); }); }); describe("rfq", () => { - it("should parse", () => { + it("should parse", async () => { const input = RfqVector.input; const rfq = Rfq.fromJSONString(input); expect(rfq.metadata).to.deep.equal(RfqVector.output.metadata); @@ -97,10 +97,10 @@ describe("test vectors", () => { const rfqJSON = JSON.parse(rfqJSONString); expect(rfqJSON).to.deep.equal(RfqVector.output); - rfq.verify(); + await rfq.verify(); }); - it("should parse with private data omitted", () => { + it("should parse with private data omitted", async () => { const input = RfqOmitPrivateDataVector.input; const rfq = Rfq.fromJSONString(input); expect(rfq.metadata).to.deep.equal( @@ -113,10 +113,10 @@ describe("test vectors", () => { const rfqJSON = JSON.parse(rfqJSONString); expect(rfqJSON).to.deep.equal(RfqOmitPrivateDataVector.output); - rfq.verify(); + await rfq.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const createRfqData: CreateRfqData = { claims: RfqVector.output.privateData.claims, offeringId: RfqVector.output.data.offeringId, @@ -139,12 +139,12 @@ describe("test vectors", () => { ); rfq.sign(bearerDID); - rfq.verify(); + await rfq.verify(); }); }); describe("quote", () => { - it("should parse", () => { + it("should parse", async () => { // TODO test vector needs updating, needs the `paymentInstruction`'s on the payin & payout removed // const input = QuoteVector.input; // const quote = Quote.fromJSONString(input); @@ -156,10 +156,10 @@ describe("test vectors", () => { // const quoteJSON = JSON.parse(quoteJSONString); // expect(quoteJSON).to.deep.equal(QuoteVector.output); // - // quote.verify(); + // await quote.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const quote = Quote.create( QuoteVector.output.metadata.to, QuoteVector.output.metadata.from, @@ -169,12 +169,12 @@ describe("test vectors", () => { ); quote.sign(bearerDID); - quote.verify(); + await quote.verify(); }); }); describe("order", () => { - it("should parse", () => { + it("should parse", async () => { const input = OrderVector.input; const order = Order.fromJSONString(input); expect(order.metadata).to.deep.equal(OrderVector.output.metadata); @@ -185,10 +185,10 @@ describe("test vectors", () => { const orderJSON = JSON.parse(orderJSONString); expect(orderJSON).to.deep.equal(OrderVector.output); - order.verify(); + await order.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const order = Order.create( OrderVector.output.metadata.to, OrderVector.output.metadata.from, @@ -197,22 +197,22 @@ describe("test vectors", () => { ); order.sign(bearerDID); - order.verify(); + await order.verify(); }); }); describe("order instructions", () => { - it("should parse", () => { + it("should parse", async () => { // todo create test vector }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { // todo create test vector }); }); describe("cancel", () => { - it("should parse", () => { + it("should parse", async () => { const input = CancelVector.input; const cancel = Cancel.fromJSONString(input); expect(cancel.metadata).to.deep.equal(CancelVector.output.metadata); @@ -223,10 +223,10 @@ describe("test vectors", () => { const cancelJSON = JSON.parse(cancelJSONString); expect(cancelJSON).to.deep.equal(CancelVector.output); - cancel.verify(); + await cancel.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const cancel = Cancel.create( CancelVector.output.metadata.to, CancelVector.output.metadata.from, @@ -236,12 +236,12 @@ describe("test vectors", () => { ); cancel.sign(bearerDID); - cancel.verify(); + await cancel.verify(); }); }); describe("order status", () => { - it("should parse", () => { + it("should parse", async () => { const input = OrderStatusVector.input; const orderStatus = OrderStatus.fromJSONString(input); expect(orderStatus.metadata).to.deep.equal( @@ -256,10 +256,10 @@ describe("test vectors", () => { const orderStatusJSON = JSON.parse(orderStatusJSONString); expect(orderStatusJSON).to.deep.equal(OrderStatusVector.output); - orderStatus.verify(); + await orderStatus.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const orderStatus = OrderStatus.create( OrderStatusVector.output.metadata.to, OrderStatusVector.output.metadata.from, @@ -269,12 +269,12 @@ describe("test vectors", () => { ); orderStatus.sign(bearerDID); - orderStatus.verify(); + await orderStatus.verify(); }); }); describe("close", () => { - it("should parse", () => { + it("should parse", async () => { const input = CloseVector.input; const close = Close.fromJSONString(input); expect(close.metadata).to.deep.equal(CloseVector.output.metadata); @@ -285,10 +285,10 @@ describe("test vectors", () => { const closeJSON = JSON.parse(closeJSONString); expect(closeJSON).to.deep.equal(CloseVector.output); - close.verify(); + await close.verify(); }); - it("should create, sign, and verify", () => { + it("should create, sign, and verify", async () => { const close = Close.create( CloseVector.output.metadata.to, CloseVector.output.metadata.from, @@ -298,7 +298,7 @@ describe("test vectors", () => { ); close.sign(bearerDID); - close.verify(); + await close.verify(); }); }); }); diff --git a/crates/tbdex/Cargo.toml b/crates/tbdex/Cargo.toml index 5e8f6f4c..6f64c09d 100644 --- a/crates/tbdex/Cargo.toml +++ b/crates/tbdex/Cargo.toml @@ -10,6 +10,7 @@ build = "build.rs" [dependencies] base64 = "0.22.0" chrono = "0.4.38" +futures = "0.3.30" jsonschema = { version = "0.18.0", default-features = false, features = [ "draft201909", "draft202012", diff --git a/crates/tbdex/src/errors.rs b/crates/tbdex/src/errors.rs index ec9de75f..ef65b17d 100644 --- a/crates/tbdex/src/errors.rs +++ b/crates/tbdex/src/errors.rs @@ -6,6 +6,8 @@ use web5::errors::Web5Error; #[derive(thiserror::Error, Debug, Clone, PartialEq)] pub enum TbdexError { + #[error("async runtime error {0}")] + AsyncRuntime(String), #[error("json error {0}")] Json(String), #[error("json schema error {0}")] diff --git a/crates/tbdex/src/http_client/balances.rs b/crates/tbdex/src/http_client/balances.rs index 8ff1b975..babbc99d 100644 --- a/crates/tbdex/src/http_client/balances.rs +++ b/crates/tbdex/src/http_client/balances.rs @@ -2,16 +2,16 @@ use super::{generate_access_token, get_json, get_service_endpoint, Result}; use crate::{http::balances::GetBalancesResponseBody, resources::balance::Balance}; use web5::dids::bearer_did::BearerDid; -pub fn get_balances(pfi_did_uri: &str, bearer_did: &BearerDid) -> Result> { - let service_endpoint = get_service_endpoint(pfi_did_uri)?; +pub async fn get_balances(pfi_did_uri: &str, bearer_did: &BearerDid) -> Result> { + let service_endpoint = get_service_endpoint(pfi_did_uri).await?; let balances_endpoint = format!("{}/balances", service_endpoint); let access_token = generate_access_token(pfi_did_uri, bearer_did)?; let get_balances_response_body = - get_json::(&balances_endpoint, Some(access_token))?; + get_json::(&balances_endpoint, Some(access_token)).await?; for balance in &get_balances_response_body.data { - balance.verify()?; + balance.verify().await?; } Ok(get_balances_response_body.data) diff --git a/crates/tbdex/src/http_client/exchanges.rs b/crates/tbdex/src/http_client/exchanges.rs index 41a38d35..4b538d53 100644 --- a/crates/tbdex/src/http_client/exchanges.rs +++ b/crates/tbdex/src/http_client/exchanges.rs @@ -34,11 +34,11 @@ pub struct Exchange { pub close: Option>, } -pub fn create_exchange(rfq: &Rfq, reply_to: Option) -> Result<()> { - let service_endpoint = get_service_endpoint(&rfq.metadata.to)?; +pub async fn create_exchange(rfq: &Rfq, reply_to: Option) -> Result<()> { + let service_endpoint = get_service_endpoint(&rfq.metadata.to).await?; let create_exchange_endpoint = format!("{}/exchanges", service_endpoint); - rfq.verify()?; + rfq.verify().await?; post_json( &create_exchange_endpoint, @@ -46,60 +46,63 @@ pub fn create_exchange(rfq: &Rfq, reply_to: Option) -> Result<()> { message: rfq.clone(), reply_to, }, - )?; + ) + .await?; Ok(()) } -pub fn submit_order(order: &Order) -> Result<()> { - let service_endpoint = get_service_endpoint(&order.metadata.to)?; +pub async fn submit_order(order: &Order) -> Result<()> { + let service_endpoint = get_service_endpoint(&order.metadata.to).await?; let submit_order_endpoint = format!( "{}/exchanges/{}", service_endpoint, order.metadata.exchange_id ); - order.verify()?; + order.verify().await?; put_json( &submit_order_endpoint, &UpdateExchangeRequestBody { message: WalletUpdateMessage::Order(Arc::new(order.clone())), }, - )?; + ) + .await?; Ok(()) } -pub fn submit_cancel(cancel: &Cancel) -> Result<()> { - let service_endpoint = get_service_endpoint(&cancel.metadata.to)?; +pub async fn submit_cancel(cancel: &Cancel) -> Result<()> { + let service_endpoint = get_service_endpoint(&cancel.metadata.to).await?; let submit_cancel_endpoint = format!( "{}/exchanges/{}", service_endpoint, cancel.metadata.exchange_id ); - cancel.verify()?; + cancel.verify().await?; put_json( &submit_cancel_endpoint, &UpdateExchangeRequestBody { message: WalletUpdateMessage::Cancel(Arc::new(cancel.clone())), }, - )?; + ) + .await?; Ok(()) } -pub fn get_exchange( +pub async fn get_exchange( pfi_did_uri: &str, bearer_did: &BearerDid, exchange_id: &str, ) -> Result { - let service_endpoint = get_service_endpoint(pfi_did_uri)?; + let service_endpoint = get_service_endpoint(pfi_did_uri).await?; let get_exchange_endpoint = format!("{}/exchanges/{}", service_endpoint, exchange_id); let access_token = generate_access_token(pfi_did_uri, bearer_did)?; let get_exchange_response_body = - get_json::(&get_exchange_endpoint, Some(access_token))?; + get_json::(&get_exchange_endpoint, Some(access_token)).await?; let mut exchange = Exchange::default(); @@ -142,12 +145,12 @@ pub struct GetExchangeIdsQueryParams { pub pagination_limit: Option, } -pub fn get_exchange_ids( +pub async fn get_exchange_ids( pfi_did: &str, requestor_did: &BearerDid, query_params: Option, ) -> Result> { - let service_endpoint = get_service_endpoint(pfi_did)?; + let service_endpoint = get_service_endpoint(pfi_did).await?; let get_exchanges_endpoint = format!("{}/exchanges", service_endpoint); let get_exchanges_endpoint = if let Some(params) = query_params { @@ -162,7 +165,7 @@ pub fn get_exchange_ids( let access_token = generate_access_token(pfi_did, requestor_did)?; let get_exchanges_response_body = - get_json::(&get_exchanges_endpoint, Some(access_token))?; + get_json::(&get_exchanges_endpoint, Some(access_token)).await?; Ok(get_exchanges_response_body.data) } diff --git a/crates/tbdex/src/http_client/mod.rs b/crates/tbdex/src/http_client/mod.rs index 59d3f6f2..8bb48e99 100644 --- a/crates/tbdex/src/http_client/mod.rs +++ b/crates/tbdex/src/http_client/mod.rs @@ -36,8 +36,8 @@ fn generate_access_token(pfi_did_uri: &str, bearer_did: &BearerDid) -> Result Result { - let resolution_result = ResolutionResult::resolve(pfi_did_uri); +async fn get_service_endpoint(pfi_did_uri: &str) -> Result { + let resolution_result = ResolutionResult::resolve(pfi_did_uri).await; let endpoint = match &resolution_result.document { None => { @@ -97,7 +97,10 @@ fn add_pagination( format!("{}{}", endpoint, query_string) } -pub(crate) fn get_json(url: &str, access_token: Option) -> Result { +pub(crate) async fn get_json( + url: &str, + access_token: Option, +) -> Result { let options = access_token.map(|access_token| FetchOptions { headers: Some( [( @@ -109,7 +112,7 @@ pub(crate) fn get_json(url: &str, access_token: Option(url: &str, access_token: Option(url: &str, body: &T) -> Result<()> { +pub(crate) async fn post_json(url: &str, body: &T) -> Result<()> { let body = serde_json::to_vec(body)?; let response = http_std::fetch( @@ -137,7 +140,8 @@ pub(crate) fn post_json(url: &str, body: &T) -> Result<()> { ), body: Some(body), }), - )?; + ) + .await?; if !(200..300).contains(&response.status_code) { return Err(TbdexError::Http(format!( @@ -149,7 +153,7 @@ pub(crate) fn post_json(url: &str, body: &T) -> Result<()> { Ok(()) } -pub(crate) fn put_json(url: &str, body: &T) -> Result<()> { +pub(crate) async fn put_json(url: &str, body: &T) -> Result<()> { let body = serde_json::to_vec(body)?; let response = http_std::fetch( @@ -163,7 +167,8 @@ pub(crate) fn put_json(url: &str, body: &T) -> Result<()> { ), body: Some(body), }), - )?; + ) + .await?; if !(200..300).contains(&response.status_code) { return Err(TbdexError::Http(format!( diff --git a/crates/tbdex/src/http_client/offerings.rs b/crates/tbdex/src/http_client/offerings.rs index 49835131..ec704aa4 100644 --- a/crates/tbdex/src/http_client/offerings.rs +++ b/crates/tbdex/src/http_client/offerings.rs @@ -1,14 +1,14 @@ use super::{get_json, get_service_endpoint, Result}; use crate::{http::offerings::GetOfferingsResponseBody, resources::offering::Offering}; -pub fn get_offerings(pfi_did_uri: &str) -> Result> { - let service_endpoint = get_service_endpoint(pfi_did_uri)?; +pub async fn get_offerings(pfi_did_uri: &str) -> Result> { + let service_endpoint = get_service_endpoint(pfi_did_uri).await?; let offerings_endpoint = format!("{}/offerings", service_endpoint); let get_offerings_response_body = - get_json::(&offerings_endpoint, None)?; + get_json::(&offerings_endpoint, None).await?; for offering in &get_offerings_response_body.data { - offering.verify()?; + offering.verify().await?; } Ok(get_offerings_response_body.data) diff --git a/crates/tbdex/src/json_schemas/mod.rs b/crates/tbdex/src/json_schemas/mod.rs index 201e0236..a2394f13 100644 --- a/crates/tbdex/src/json_schemas/mod.rs +++ b/crates/tbdex/src/json_schemas/mod.rs @@ -5,6 +5,7 @@ use crate::{ http_client::get_json, json_schemas::generated::DRAFT_07_JSON_SCHEMA, }; +use futures::executor::block_on; use generated::DEFINITIONS_JSON_SCHEMA; use jsonschema::{JSONSchema, SchemaResolver, SchemaResolverError}; use serde::Serialize; @@ -49,7 +50,7 @@ impl SchemaResolver for LocalSchemaResolver { if let Some(schema) = self.schemas.get(&LocalSchemaResolver::normalize_url(url)) { Ok(std::sync::Arc::new(schema.clone())) } else { - match get_json::(url.as_str(), None) { + match block_on(get_json::(url.as_str(), None)) { Ok(schema) => Ok(Arc::new(schema)), Err(err) => Err(SchemaResolverError::new(std::io::Error::new( std::io::ErrorKind::NotFound, diff --git a/crates/tbdex/src/messages/cancel.rs b/crates/tbdex/src/messages/cancel.rs index cfdbe824..ab2c341e 100644 --- a/crates/tbdex/src/messages/cancel.rs +++ b/crates/tbdex/src/messages/cancel.rs @@ -96,7 +96,7 @@ impl Cancel { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -108,7 +108,8 @@ impl Cancel { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/close.rs b/crates/tbdex/src/messages/close.rs index a553dddb..b44fc063 100644 --- a/crates/tbdex/src/messages/close.rs +++ b/crates/tbdex/src/messages/close.rs @@ -96,7 +96,7 @@ impl Close { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -108,7 +108,8 @@ impl Close { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/order.rs b/crates/tbdex/src/messages/order.rs index f0e86aed..02772d96 100644 --- a/crates/tbdex/src/messages/order.rs +++ b/crates/tbdex/src/messages/order.rs @@ -96,7 +96,7 @@ impl Order { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -108,7 +108,8 @@ impl Order { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(&OrderData {})?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/order_instructions.rs b/crates/tbdex/src/messages/order_instructions.rs index 081dc514..0f703c93 100644 --- a/crates/tbdex/src/messages/order_instructions.rs +++ b/crates/tbdex/src/messages/order_instructions.rs @@ -96,7 +96,7 @@ impl OrderInstructions { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -108,7 +108,8 @@ impl OrderInstructions { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/order_status.rs b/crates/tbdex/src/messages/order_status.rs index c38a79e9..53afa5f3 100644 --- a/crates/tbdex/src/messages/order_status.rs +++ b/crates/tbdex/src/messages/order_status.rs @@ -99,7 +99,7 @@ impl OrderStatus { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -111,7 +111,8 @@ impl OrderStatus { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/quote.rs b/crates/tbdex/src/messages/quote.rs index 0d11f1e2..ee10dc52 100644 --- a/crates/tbdex/src/messages/quote.rs +++ b/crates/tbdex/src/messages/quote.rs @@ -96,7 +96,7 @@ impl Quote { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -108,7 +108,8 @@ impl Quote { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/messages/rfq.rs b/crates/tbdex/src/messages/rfq.rs index 5e89bce7..993e8b81 100644 --- a/crates/tbdex/src/messages/rfq.rs +++ b/crates/tbdex/src/messages/rfq.rs @@ -114,7 +114,7 @@ impl Rfq { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(MESSAGE_JSON_SCHEMA, self)?; @@ -131,7 +131,8 @@ impl Rfq { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } @@ -148,7 +149,7 @@ impl Rfq { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify_offering_requirements(&self, offering: &Offering) -> Result<()> { + pub async fn verify_offering_requirements(&self, offering: &Offering) -> Result<()> { // verify protocol version if offering.metadata.protocol != self.metadata.protocol { return Err(TbdexError::OfferingVerification(format!( @@ -279,6 +280,7 @@ impl Rfq { if let Some(required_claims) = &offering.data.required_claims { let vc_jwts = required_claims .select_credentials(&private_data.claims.clone().unwrap_or_default()) + .await .map_err(|_| { TbdexError::OfferingVerification("failed to select credentials".to_string()) })?; @@ -290,12 +292,14 @@ impl Rfq { } for vc_jwt in vc_jwts { - VerifiableCredential::from_vc_jwt(&vc_jwt, true).map_err(|_| { - TbdexError::OfferingVerification(format!( - "vc_jwt failed verifiction {}", - vc_jwt - )) - })?; + VerifiableCredential::from_vc_jwt(&vc_jwt, true) + .await + .map_err(|_| { + TbdexError::OfferingVerification(format!( + "vc_jwt failed verifiction {}", + vc_jwt + )) + })?; } } diff --git a/crates/tbdex/src/resources/balance.rs b/crates/tbdex/src/resources/balance.rs index d7b7b659..aaceca6e 100644 --- a/crates/tbdex/src/resources/balance.rs +++ b/crates/tbdex/src/resources/balance.rs @@ -86,7 +86,7 @@ impl Balance { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(RESOURCE_JSON_SCHEMA, self)?; @@ -98,7 +98,8 @@ impl Balance { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/resources/offering.rs b/crates/tbdex/src/resources/offering.rs index b4d8802f..fa691eda 100644 --- a/crates/tbdex/src/resources/offering.rs +++ b/crates/tbdex/src/resources/offering.rs @@ -88,7 +88,7 @@ impl Offering { /// # Returns /// /// An empty result if verification succeeds, or an error if verification fails. - pub fn verify(&self) -> Result<()> { + pub async fn verify(&self) -> Result<()> { // verify resource json schema crate::json_schemas::validate_from_str(RESOURCE_JSON_SCHEMA, self)?; @@ -100,7 +100,8 @@ impl Offering { &serde_json::to_value(self.metadata.clone())?, &serde_json::to_value(self.data.clone())?, &self.signature, - )?; + ) + .await?; Ok(()) } diff --git a/crates/tbdex/src/signature.rs b/crates/tbdex/src/signature.rs index 72aad412..27173770 100644 --- a/crates/tbdex/src/signature.rs +++ b/crates/tbdex/src/signature.rs @@ -24,7 +24,7 @@ pub fn sign(bearer_did: &BearerDid, metadata: &Value, data: &Value) -> Result Result<()> { +pub async fn verify(metadata: &Value, data: &Value, detached_compact_jws: &str) -> Result<()> { // re-attach the payload let mut combined = Map::new(); combined.insert("metadata".to_string(), metadata.clone()); @@ -41,7 +41,7 @@ pub fn verify(metadata: &Value, data: &Value, detached_compact_jws: &str) -> Res } let compact_jws = format!("{}.{}.{}", parts[0], payload, parts[2]); - let _ = Jws::from_compact_jws(&compact_jws, true)?; + let _ = Jws::from_compact_jws(&compact_jws, true).await?; Ok(()) }