From 849934d1b3ac69bb49a906388c8d17e70183845b Mon Sep 17 00:00:00 2001 From: Simon Bihel Date: Mon, 8 Mar 2021 10:04:33 +0000 Subject: [PATCH] Fix WASM compilation for resolvers that use HTTP - WASM compilation of reqwest - Remove async Send for wasm32 target - Install wasm target in CI - Vendor openssl for Android --- .github/workflows/build.yml | 5 ++++ did-ethr/Cargo.toml | 4 +++- did-ethr/src/lib.rs | 3 ++- did-key/src/lib.rs | 3 ++- did-sol/Cargo.toml | 4 +++- did-sol/src/lib.rs | 3 ++- did-tezos/Cargo.toml | 7 ++++-- did-tezos/src/explorer.rs | 4 ++-- did-tezos/src/lib.rs | 3 ++- did-web/Cargo.toml | 11 +++++---- did-web/src/lib.rs | 48 ++++++++++++++++++------------------- src/did.rs | 12 ++++++---- src/did_resolve.rs | 9 ++++--- src/ldp.rs | 36 ++++++++++++++++++---------- src/vc.rs | 9 ++++--- 15 files changed, 101 insertions(+), 60 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 64c5a14e4..e4b4d0d6c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,3 +65,8 @@ jobs: npm i cp ../vc-test/config.json . npm test + + - name: Test WASM compilation + run: | + rustup target add wasm32-unknown-unknown + cargo check --workspace --target wasm32-unknown-unknown diff --git a/did-ethr/Cargo.toml b/did-ethr/Cargo.toml index 5284fc410..44acbe222 100644 --- a/did-ethr/Cargo.toml +++ b/did-ethr/Cargo.toml @@ -7,9 +7,11 @@ edition = "2018" [dependencies] ssi = { path = "../", default-features = false, features = ["secp256k1", "keccak-hash"] } chrono = { version = "0.4", features = ["serde"] } -tokio = { version = "1.0", features = ["macros", "rt"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-trait = "0.1" libsecp256k1 = { version = "0.3" } keccak-hash = "0.7" + +[dev-dependencies] +tokio = { version = "1.0", features = ["macros"] } diff --git a/did-ethr/src/lib.rs b/did-ethr/src/lib.rs index bf72f512e..800361ea1 100644 --- a/did-ethr/src/lib.rs +++ b/did-ethr/src/lib.rs @@ -45,7 +45,8 @@ fn parse_did(did: &str) -> Option<(i64, String)> { Some((chain_id, address)) } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDEthr { async fn resolve( &self, diff --git a/did-key/src/lib.rs b/did-key/src/lib.rs index 7373debd4..c37564149 100644 --- a/did-key/src/lib.rs +++ b/did-key/src/lib.rs @@ -28,7 +28,8 @@ pub enum DIDKeyError { pub struct DIDKey; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDKey { async fn resolve( &self, diff --git a/did-sol/Cargo.toml b/did-sol/Cargo.toml index 451f4f5f2..143911af6 100644 --- a/did-sol/Cargo.toml +++ b/did-sol/Cargo.toml @@ -7,8 +7,10 @@ edition = "2018" [dependencies] ssi = { path = "../", default-features = false, features = [] } chrono = { version = "0.4", features = ["serde"] } -tokio = { version = "1.0", features = ["macros", "rt"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-trait = "0.1" bs58 = { version = "0.4", features = ["check"] } + +[dev-dependencies] +tokio = { version = "1.0", features = ["macros"] } diff --git a/did-sol/src/lib.rs b/did-sol/src/lib.rs index aec879060..44ff09ea6 100644 --- a/did-sol/src/lib.rs +++ b/did-sol/src/lib.rs @@ -30,7 +30,8 @@ fn parse_did(did: &str) -> Option<(String, Vec)> { Some((address, bytes)) } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDSol { async fn resolve( &self, diff --git a/did-tezos/Cargo.toml b/did-tezos/Cargo.toml index a83c5b971..32e177c3d 100644 --- a/did-tezos/Cargo.toml +++ b/did-tezos/Cargo.toml @@ -13,8 +13,7 @@ p256 = ["ssi/p256"] [dependencies] ssi = { path = "../", default-features = false } chrono = { version = "0.4" } -tokio = { version = "1.0", features = ["macros", "rt"] } -reqwest = { version = "0.11", features = ["json", "default-tls"] } +reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" async-trait = "0.1" @@ -23,5 +22,9 @@ anyhow = "1.0.33" json-patch = "0.2.6" bs58 = { version = "0.4", features = ["check"] } +[target.'cfg(target_os = "android")'.dependencies.reqwest] +features = ["native-tls-vendored"] + [dev-dependencies] +tokio = { version = "1.0", features = ["macros"] } tezedge_client = { git = "https://github.com/binier/tezedge-client", rev = "672e61297f0ba7d1d94168a2588b9b27d76c1fc4", package = "lib" } diff --git a/did-tezos/src/explorer.rs b/did-tezos/src/explorer.rs index 895de11a0..64f5f7168 100644 --- a/did-tezos/src/explorer.rs +++ b/did-tezos/src/explorer.rs @@ -30,7 +30,7 @@ struct SearchResult { } pub async fn retrieve_did_manager(address: &str, network: &str) -> Result> { - let client = reqwest::Client::new(); + let client = reqwest::Client::builder().build()?; let mut search_result: SearchResult = client .get(&format!("{}v1/search", BCD_URL)) .query(&[ @@ -73,7 +73,7 @@ struct ServiceResultItem { } pub async fn execute_service_view(did: &str, contract: &str, network: &str) -> Result { - let client = reqwest::Client::new(); + let client = reqwest::Client::builder().build()?; let service_result: ServiceResult = client .post(&format!( "{}v1/contract/{}/{}/views/execute", diff --git a/did-tezos/src/lib.rs b/did-tezos/src/lib.rs index 4fe509e38..349f538ed 100644 --- a/did-tezos/src/lib.rs +++ b/did-tezos/src/lib.rs @@ -28,7 +28,8 @@ use std::convert::TryInto; /// [Specification](https://github.com/spruceid/did-tezos/) pub struct DIDTz; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDTz { async fn resolve( &self, diff --git a/did-web/Cargo.toml b/did-web/Cargo.toml index b0fbe5f6d..b1ab393a9 100644 --- a/did-web/Cargo.toml +++ b/did-web/Cargo.toml @@ -7,13 +7,16 @@ edition = "2018" [dependencies] ssi = { path = "../", default-features = false } async-trait = "0.1" -hyper = { version = "0.14", features = ["server", "client", "http1", "stream"] } -hyper-tls = "0.5" +reqwest = { version = "0.11", features = ["json"] } http = "0.2" serde_json = "1.0" -tokio = { version = "1.0", features = ["macros"] } -futures = "0.3" + +[target.'cfg(target_os = "android")'.dependencies.reqwest] +features = ["native-tls-vendored"] [dev-dependencies] +tokio = { version = "1.0", features = ["macros"] } serde = { version = "1.0", features = ["derive"] } async-std = { version = "1.9", features = ["attributes"] } +futures = "0.3" +hyper = { version = "0.14", features = ["server", "client", "http1", "stream"] } diff --git a/did-web/src/lib.rs b/did-web/src/lib.rs index 5761e5b1a..e158cff8c 100644 --- a/did-web/src/lib.rs +++ b/did-web/src/lib.rs @@ -1,11 +1,9 @@ use async_trait::async_trait; -use hyper::{Client, Request, StatusCode}; -use hyper_tls::HttpsConnector; use ssi::did::{DIDMethod, Document}; use ssi::did_resolve::{ DIDResolver, DocumentMetadata, ResolutionInputMetadata, ResolutionMetadata, ERROR_INVALID_DID, - ERROR_NOT_FOUND, TYPE_DID_LD_JSON, + TYPE_DID_LD_JSON, }; // For testing, enable handling requests at localhost. @@ -50,7 +48,8 @@ fn did_web_url(did: &str) -> Result { } /// -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDWeb { async fn resolve( &self, @@ -91,22 +90,30 @@ impl DIDResolver for DIDWeb { Err(meta) => return (meta, Vec::new(), None), Ok(url) => url, }; - let https = HttpsConnector::new(); // TODO: https://w3c-ccg.github.io/did-method-web/#in-transit-security - let client = Client::builder().build::<_, hyper::Body>(https); + let client = match reqwest::Client::builder().build() { + Ok(c) => c, + Err(err) => { + return ( + ResolutionMetadata::from_error(&format!( + "Error building HTTP client: {}", + err.to_string() + )), + Vec::new(), + None, + ) + } + }; let accept = input_metadata .accept .clone() .unwrap_or_else(|| "application/json".to_string()); - let request = match Request::get(url) - .header("Accept", accept) - .body(hyper::Body::default()) - { + let resp = match client.get(&url).header("Accept", accept).send().await { Ok(req) => req, Err(err) => { return ( ResolutionMetadata::from_error(&format!( - "Error building HTTP request: {}", + "Error sending HTTP request : {}", err.to_string() )), Vec::new(), @@ -114,24 +121,17 @@ impl DIDResolver for DIDWeb { ) } }; - let mut resp = match client.request(request).await { - Ok(resp) => resp, + match resp.error_for_status_ref() { + Ok(_) => (), Err(err) => { return ( - ResolutionMetadata::from_error(&format!("HTTP Error: {}", err.to_string())), + ResolutionMetadata::from_error(&err.to_string()), Vec::new(), - None, + Some(DocumentMetadata::default()), ) } }; - if resp.status() == StatusCode::NOT_FOUND { - return ( - ResolutionMetadata::from_error(ERROR_NOT_FOUND), - Vec::new(), - Some(DocumentMetadata::default()), - ); - } - let doc_representation = match hyper::body::to_bytes(resp.body_mut()).await { + let doc_representation = match resp.bytes().await { Ok(bytes) => bytes.to_vec(), Err(err) => { return ( @@ -228,7 +228,7 @@ mod tests { } let (mut parts, body) = Response::::default().into_parts(); - parts.status = StatusCode::NOT_FOUND; + parts.status = hyper::StatusCode::NOT_FOUND; let response = Response::from_parts(parts, body); return Ok::<_, hyper::Error>(response); })) diff --git a/src/did.rs b/src/did.rs index 5a47abb36..56ec0eea2 100644 --- a/src/did.rs +++ b/src/did.rs @@ -197,7 +197,8 @@ pub struct DIDParameters { pub property_set: Option>, } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait DIDMethod: DIDResolver { /// Get the DID method name. /// @@ -261,7 +262,8 @@ impl<'a> DIDMethods<'a> { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl<'a> DIDResolver for DIDMethods<'a> { async fn resolve( &self, @@ -545,7 +547,8 @@ pub mod example { pub struct DIDExample; - #[async_trait] + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDMethod for DIDExample { fn name(&self) -> &'static str { return "example"; @@ -556,7 +559,8 @@ pub mod example { } } - #[async_trait] + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] + #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for DIDExample { async fn resolve( &self, diff --git a/src/did_resolve.rs b/src/did_resolve.rs index 80632fbde..8fa92cd3b 100644 --- a/src/did_resolve.rs +++ b/src/did_resolve.rs @@ -217,7 +217,8 @@ impl Default for ResolutionResult { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait DIDResolver: Sync { async fn resolve( &self, @@ -623,7 +624,8 @@ impl HTTPDIDResolver { } #[cfg(feature = "http")] -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl DIDResolver for HTTPDIDResolver { // https://w3c-ccg.github.io/did-resolution/#bindings-https async fn resolve( @@ -972,7 +974,8 @@ pub struct SeriesResolver<'a> { pub resolvers: Vec<&'a (dyn DIDResolver)>, } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl<'a> DIDResolver for SeriesResolver<'a> { async fn resolve( &self, diff --git a/src/ldp.rs b/src/ldp.rs index c36973736..0bb6b2e9d 100644 --- a/src/ldp.rs +++ b/src/ldp.rs @@ -51,7 +51,8 @@ pub fn now_ms() -> DateTime { datetime.with_nanosecond(ns).unwrap_or(datetime) } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait LinkedDataDocument { fn get_contexts(&self) -> Result, Error>; async fn to_dataset_for_signing( @@ -60,7 +61,8 @@ pub trait LinkedDataDocument { ) -> Result; } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] pub trait ProofSuite { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -485,7 +487,8 @@ async fn verify( } pub struct RsaSignature2018; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for RsaSignature2018 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -514,7 +517,8 @@ impl ProofSuite for RsaSignature2018 { } pub struct Ed25519Signature2018; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for Ed25519Signature2018 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -550,7 +554,8 @@ impl ProofSuite for Ed25519Signature2018 { } pub struct EcdsaSecp256k1Signature2019; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for EcdsaSecp256k1Signature2019 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -586,7 +591,8 @@ impl ProofSuite for EcdsaSecp256k1Signature2019 { } pub struct EcdsaSecp256k1RecoverySignature2020; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for EcdsaSecp256k1RecoverySignature2020 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -662,7 +668,8 @@ impl ProofSuite for EcdsaSecp256k1RecoverySignature2020 { /// Proof type used with [did:tz](https://github.com/spruceid/did-tezos/) `tz1` addresses. pub struct Ed25519BLAKE2BDigestSize20Base58CheckEncodedSignature2021; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for Ed25519BLAKE2BDigestSize20Base58CheckEncodedSignature2021 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -754,7 +761,8 @@ impl ProofSuite for Ed25519BLAKE2BDigestSize20Base58CheckEncodedSignature2021 { /// Proof type used with [did:tz](https://github.com/spruceid/did-tezos/) `tz3` addresses. pub struct P256BLAKE2BDigestSize20Base58CheckEncodedSignature2021; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for P256BLAKE2BDigestSize20Base58CheckEncodedSignature2021 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -846,7 +854,8 @@ impl ProofSuite for P256BLAKE2BDigestSize20Base58CheckEncodedSignature2021 { #[cfg(feature = "keccak-hash")] pub struct Eip712Signature2021; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg(feature = "keccak-hash")] impl ProofSuite for Eip712Signature2021 { async fn sign( @@ -956,7 +965,8 @@ impl ProofSuite for Eip712Signature2021 { } pub struct SolanaSignature2021; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for SolanaSignature2021 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -1039,7 +1049,8 @@ impl ProofSuite for SolanaSignature2021 { } pub struct EcdsaSecp256r1Signature2019; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for EcdsaSecp256r1Signature2019 { async fn sign( document: &(dyn LinkedDataDocument + Sync), @@ -1076,7 +1087,8 @@ impl ProofSuite for EcdsaSecp256r1Signature2019 { /// pub struct JsonWebSignature2020; -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl ProofSuite for JsonWebSignature2020 { async fn sign( document: &(dyn LinkedDataDocument + Sync), diff --git a/src/vc.rs b/src/vc.rs index 872281016..016bef82a 100644 --- a/src/vc.rs +++ b/src/vc.rs @@ -702,7 +702,8 @@ impl Credential { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl LinkedDataDocument for Credential { fn get_contexts(&self) -> Result, Error> { Ok(Some(serde_json::to_string(&self.context)?)) @@ -902,7 +903,8 @@ pub async fn get_verification_methods( Ok(vms) } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl LinkedDataDocument for Presentation { fn get_contexts(&self) -> Result, Error> { Ok(Some(serde_json::to_string(&self.context)?)) @@ -975,7 +977,8 @@ impl Proof { } } -#[async_trait] +#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl LinkedDataDocument for Proof { fn get_contexts(&self) -> Result, Error> { Ok(None)