From 30294062af4e0118ac2f767c2ce7d03874e9d62f Mon Sep 17 00:00:00 2001 From: Mark Sinclair Date: Thu, 5 Oct 2023 12:41:07 +0100 Subject: [PATCH] WASM client for credentials - wip --- Cargo.toml | 1 + wasm/credentials/Cargo.toml | 42 +++++++++++++ wasm/credentials/src/lib.rs | 114 ++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 wasm/credentials/Cargo.toml create mode 100644 wasm/credentials/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 1682869b0e..04e6c87c64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,7 @@ members = [ # "wasm/full-nym-wasm", "wasm/mix-fetch", "wasm/node-tester", + "wasm/credentials" ] default-members = [ diff --git a/wasm/credentials/Cargo.toml b/wasm/credentials/Cargo.toml new file mode 100644 index 0000000000..021f92771a --- /dev/null +++ b/wasm/credentials/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "nym-credential-client-wasm" +authors = [] +version = "1.2.0-rc.9" +edition = "2021" +keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"] +license = "Apache-2.0" +repository = "https://github.com/nymtech/nym" +description = "A webassembly client which can be used to issue Coconut credentials." +rust-version = "1.56" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +anyhow = "1.0" +futures = { workspace = true } +js-sys = { workspace = true } +rand = { version = "0.7.3", features = ["wasm-bindgen"] } +serde = { workspace = true, features = ["derive"] } +serde_json = "1.0" +serde-wasm-bindgen = { workspace = true } +wasm-bindgen = { workspace = true } +wasm-bindgen-futures = { workspace = true } +thiserror = { workspace = true } +tsify = { workspace = true, features = ["js"] } + +wasm-client-core = { path = "../../common/wasm/client-core" } +wasm-utils = { path = "../../common/wasm/utils" } + +nym-credentials = { path = "../../common/credentials" } +nym-bandwidth-controller = { path = "../../common/bandwidth-controller" } +nym-validator-client = { path = "../../common/client-libs/validator-client", features = ["http-client"] } +nym-crypto = { path = "../../common/crypto" } +nym-coconut-interface = { path = "../../common/coconut-interface" } +nym-network-defaults = { path = "../../common/network-defaults" } + +[dev-dependencies] +wasm-bindgen-test = "0.3.36" + +[package.metadata.wasm-pack.profile.release] +wasm-opt = false diff --git a/wasm/credentials/src/lib.rs b/wasm/credentials/src/lib.rs new file mode 100644 index 0000000000..20dc6dbe6c --- /dev/null +++ b/wasm/credentials/src/lib.rs @@ -0,0 +1,114 @@ +use std::str::FromStr; + +use js_sys::Promise; +use thiserror::Error; +use wasm_bindgen::prelude::wasm_bindgen; + +use nym_bandwidth_controller::acquire::deposit; +use nym_bandwidth_controller::acquire::state::State; +use nym_bandwidth_controller::error::BandwidthControllerError; +use nym_coconut_interface::Signature; +use nym_credentials::coconut::utils::obtain_aggregate_signature; +use nym_network_defaults::NymNetworkDetails; +use nym_validator_client::coconut::all_coconut_api_clients; +use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient; +use nym_validator_client::nyxd::contract_traits::DkgQueryClient; +use nym_validator_client::nyxd::error::NyxdError; +use nym_validator_client::nyxd::{Coin, CosmWasmCoin}; +use nym_validator_client::{nyxd, DirectSigningHttpRpcNyxdClient}; +use wasm_utils::wasm_error; + +#[wasm_bindgen] +struct WasmCredentialClient { + state: Option, + client: DirectSigningHttpRpcNyxdClient, +} + +#[wasm_bindgen] +impl WasmCredentialClient { + #[wasm_bindgen(constructor)] + pub fn new( + // network_details: NymNetworkDetails, + mnemonic: String, + ) -> Result { + let network_details = NymNetworkDetails::new_mainnet(); + let client = WasmCredentialClient::create_client(network_details, mnemonic)?; + Ok(WasmCredentialClient { + state: None, + client, + }) + } + + #[wasm_bindgen] + pub async fn deposit( + &mut self, + amount_with_denom: String, + ) -> Result<(), WasmCredentialClientError> { + match CosmWasmCoin::from_str(&amount_with_denom) { + Ok(coin) => { + let state = deposit(&self.client, Coin::from(coin)).await?; + self.state = Some(state); + Ok(()) + } + Err(_e) => Err(WasmCredentialClientError::CoinParseError), + } + } + + #[wasm_bindgen] + pub async fn get_credential(self) -> Result { + let epoch_id = self.client.get_current_epoch().await?.epoch_id; + let threshold = self + .client + .get_current_epoch_threshold() + .await? + .ok_or(BandwidthControllerError::NoThreshold)?; + + let coconut_api_clients = all_coconut_api_clients(&self.client, epoch_id).await?; + + match self.state { + Some(state) => { + let signature = obtain_aggregate_signature( + &self.state.params, + &self.state.voucher, + &coconut_api_clients, + threshold, + ) + .await?; + + Ok(signature) + } + None => Err(WasmCredentialClientError::StateError), + } + } + + fn create_client( + network_details: NymNetworkDetails, + mnemonic: String, + ) -> Result { + let nyxd_url = network_details.endpoints[0].nyxd_url.as_str(); + let config = nyxd::Config::try_from_nym_network_details(&network_details)?; + + let client = DirectSigningHttpRpcNyxdClient::connect_with_mnemonic( + config, + nyxd_url, + mnemonic.parse()?, + )?; + + Ok(client) + } +} + +#[derive(Debug, Error)] +pub enum WasmCredentialClientError { + #[error(transparent)] + BandwidthControllerError { + #[from] + source: BandwidthControllerError, + }, + #[error("Coin parse error")] + CoinParseError, + #[error("State error")] + StateError, +} + +wasm_error!(WasmCredentialClientError);