Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5c598729b | |||
| 7eb764cd5a | |||
| 854128ee21 | |||
| 6cd59124a2 | |||
| d5cc31b7f0 | |||
| ea6f009c01 | |||
| 5881f6b6aa | |||
| aafddc78d1 | |||
| 29a0e8620c | |||
| 800016f682 | |||
| 6151f15e7e | |||
| 5e6a945eef | |||
| aec23a4f83 | |||
| 3d563ece79 | |||
| f24a0dc8ae | |||
| 50fbdb4f0b | |||
| d445a26999 | |||
| 30294062af |
Generated
+27
-1
@@ -5520,6 +5520,30 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nym-credential-client-wasm"
|
||||||
|
version = "1.3.0-rc.0"
|
||||||
|
dependencies = [
|
||||||
|
"bip39",
|
||||||
|
"js-sys",
|
||||||
|
"nym-bandwidth-controller",
|
||||||
|
"nym-bin-common",
|
||||||
|
"nym-credential-storage",
|
||||||
|
"nym-credentials",
|
||||||
|
"nym-credentials-interface",
|
||||||
|
"nym-network-defaults",
|
||||||
|
"nym-validator-client",
|
||||||
|
"serde",
|
||||||
|
"serde-wasm-bindgen",
|
||||||
|
"thiserror",
|
||||||
|
"tsify",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-bindgen-test",
|
||||||
|
"wasm-utils",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nym-credential-storage"
|
name = "nym-credential-storage"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -6090,7 +6114,9 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tsify",
|
||||||
"url",
|
"url",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -11039,7 +11065,6 @@ name = "wasm-client-core"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"console_error_panic_hook",
|
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"nym-bandwidth-controller",
|
"nym-bandwidth-controller",
|
||||||
"nym-client-core",
|
"nym-client-core",
|
||||||
@@ -11114,6 +11139,7 @@ dependencies = [
|
|||||||
name = "wasm-utils"
|
name = "wasm-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
"futures",
|
"futures",
|
||||||
"getrandom 0.2.10",
|
"getrandom 0.2.10",
|
||||||
"gloo-net",
|
"gloo-net",
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ members = [
|
|||||||
# "wasm/full-nym-wasm",
|
# "wasm/full-nym-wasm",
|
||||||
"wasm/mix-fetch",
|
"wasm/mix-fetch",
|
||||||
"wasm/node-tester",
|
"wasm/node-tester",
|
||||||
|
"wasm/credentials"
|
||||||
]
|
]
|
||||||
|
|
||||||
default-members = [
|
default-members = [
|
||||||
@@ -235,6 +236,12 @@ tendermint-rpc = "0.34" # same version as used by cosmrs
|
|||||||
prost = "0.12"
|
prost = "0.12"
|
||||||
|
|
||||||
# wasm-related dependencies
|
# wasm-related dependencies
|
||||||
|
|
||||||
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
|
# code size when deploying.
|
||||||
|
console_error_panic_hook = "0.1.7"
|
||||||
gloo-utils = "0.1.7"
|
gloo-utils = "0.1.7"
|
||||||
js-sys = "0.3.63"
|
js-sys = "0.3.63"
|
||||||
serde-wasm-bindgen = "0.5.0"
|
serde-wasm-bindgen = "0.5.0"
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ sdk-wasm: sdk-wasm-build sdk-wasm-test sdk-wasm-lint
|
|||||||
sdk-wasm-build:
|
sdk-wasm-build:
|
||||||
$(MAKE) -C nym-browser-extension/storage wasm-pack
|
$(MAKE) -C nym-browser-extension/storage wasm-pack
|
||||||
$(MAKE) -C wasm/client
|
$(MAKE) -C wasm/client
|
||||||
|
$(MAKE) -C wasm/credentials
|
||||||
$(MAKE) -C wasm/node-tester
|
$(MAKE) -C wasm/node-tester
|
||||||
$(MAKE) -C wasm/mix-fetch
|
$(MAKE) -C wasm/mix-fetch
|
||||||
#$(MAKE) -C wasm/full-nym-wasm
|
#$(MAKE) -C wasm/full-nym-wasm
|
||||||
|
|||||||
@@ -17,13 +17,17 @@ use zeroize::Zeroizing;
|
|||||||
|
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
pub async fn deposit<C>(client: &C, amount: Coin) -> Result<State, BandwidthControllerError>
|
pub async fn deposit<C>(
|
||||||
|
client: &C,
|
||||||
|
amount: impl Into<Coin>,
|
||||||
|
) -> Result<State, BandwidthControllerError>
|
||||||
where
|
where
|
||||||
C: CoconutBandwidthSigningClient + Sync,
|
C: CoconutBandwidthSigningClient + Sync,
|
||||||
{
|
{
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let signing_key = identity::PrivateKey::new(&mut rng);
|
let signing_key = identity::PrivateKey::new(&mut rng);
|
||||||
let encryption_key = encryption::PrivateKey::new(&mut rng);
|
let encryption_key = encryption::PrivateKey::new(&mut rng);
|
||||||
|
let amount = amount.into();
|
||||||
|
|
||||||
let tx_hash = client
|
let tx_hash = client
|
||||||
.deposit(
|
.deposit(
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub enum NyxdError {
|
|||||||
#[error("{0} is not a valid tx hash")]
|
#[error("{0} is not a valid tx hash")]
|
||||||
InvalidTxHash(String),
|
InvalidTxHash(String),
|
||||||
|
|
||||||
#[error("Tendermint RPC request failed - {0}")]
|
#[error("Tendermint RPC request failed: {0}")]
|
||||||
TendermintErrorRpc(#[from] TendermintRpcError),
|
TendermintErrorRpc(#[from] TendermintRpcError),
|
||||||
|
|
||||||
#[error("tendermint library failure: {0}")]
|
#[error("tendermint library failure: {0}")]
|
||||||
@@ -56,22 +56,22 @@ pub enum NyxdError {
|
|||||||
#[error("Failed when attempting to deserialize data ({0})")]
|
#[error("Failed when attempting to deserialize data ({0})")]
|
||||||
DeserializationError(String),
|
DeserializationError(String),
|
||||||
|
|
||||||
#[error("Failed when attempting to encode our protobuf data - {0}")]
|
#[error("Failed when attempting to encode our protobuf data: {0}")]
|
||||||
ProtobufEncodingError(#[from] prost::EncodeError),
|
ProtobufEncodingError(#[from] prost::EncodeError),
|
||||||
|
|
||||||
#[error("Failed to decode our protobuf data - {0}")]
|
#[error("Failed to decode our protobuf data: {0}")]
|
||||||
ProtobufDecodingError(#[from] prost::DecodeError),
|
ProtobufDecodingError(#[from] prost::DecodeError),
|
||||||
|
|
||||||
#[error("Account {0} does not exist on the chain")]
|
#[error("Account '{0}' does not exist on the chain")]
|
||||||
NonExistentAccountError(AccountId),
|
NonExistentAccountError(AccountId),
|
||||||
|
|
||||||
#[error("Failed on json serialization/deserialization - {0}")]
|
#[error("Failed on json serialization/deserialization: {0}")]
|
||||||
SerdeJsonError(#[from] serde_json::Error),
|
SerdeJsonError(#[from] serde_json::Error),
|
||||||
|
|
||||||
#[error("Account {0} is not a valid account address")]
|
#[error("Account '{0}' is not a valid account address")]
|
||||||
MalformedAccountAddress(String),
|
MalformedAccountAddress(String),
|
||||||
|
|
||||||
#[error("Account {0} has an invalid associated public key")]
|
#[error("Account '{0}' has an invalid associated public key")]
|
||||||
InvalidPublicKey(AccountId),
|
InvalidPublicKey(AccountId),
|
||||||
|
|
||||||
#[error("Queried contract (code_id: {0}) did not have any code information attached")]
|
#[error("Queried contract (code_id: {0}) did not have any code information attached")]
|
||||||
@@ -86,7 +86,7 @@ pub enum NyxdError {
|
|||||||
#[error("Block has an invalid height (either negative or larger than i64::MAX")]
|
#[error("Block has an invalid height (either negative or larger than i64::MAX")]
|
||||||
InvalidHeight,
|
InvalidHeight,
|
||||||
|
|
||||||
#[error("Failed to compress provided wasm code - {0}")]
|
#[error("Failed to compress provided wasm code: {0}")]
|
||||||
WasmCompressionError(io::Error),
|
WasmCompressionError(io::Error),
|
||||||
|
|
||||||
#[error("Logs returned from the validator were malformed")]
|
#[error("Logs returned from the validator were malformed")]
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ trait TendermintRpcErrorMap {
|
|||||||
|
|
||||||
impl TendermintRpcErrorMap for reqwest::Error {
|
impl TendermintRpcErrorMap for reqwest::Error {
|
||||||
fn into_rpc_err(self) -> Error {
|
fn into_rpc_err(self) -> Error {
|
||||||
todo!()
|
// that's not the best error converion, but it's better than a panic
|
||||||
|
Error::client_internal(self.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ license.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
|
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["sync"]}
|
tokio = { workspace = true, features = ["sync"]}
|
||||||
@@ -26,4 +25,4 @@ features = [ "rt-multi-thread", "net", "signal", "fs" ]
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ impl CoconutCredentialManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn take_credentials(self) -> Vec<StoredIssuedCredential> {
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
inner.credential_usage = Vec::new();
|
||||||
|
std::mem::take(&mut inner.credentials)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn insert_issued_credential(
|
pub async fn insert_issued_credential(
|
||||||
&self,
|
&self,
|
||||||
credential_type: String,
|
credential_type: String,
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ pub struct EphemeralStorage {
|
|||||||
coconut_credential_manager: CoconutCredentialManager,
|
coconut_credential_manager: CoconutCredentialManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EphemeralStorage {
|
||||||
|
pub async fn take_credentials(self) -> Vec<StoredIssuedCredential> {
|
||||||
|
self.coconut_credential_manager.take_credentials().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for EphemeralStorage {
|
impl Default for EphemeralStorage {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
EphemeralStorage {
|
EphemeralStorage {
|
||||||
|
|||||||
@@ -3,19 +3,6 @@
|
|||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
// #[derive(Clone)]
|
|
||||||
// pub struct CoconutCredential {
|
|
||||||
// #[allow(dead_code)]
|
|
||||||
// pub id: i64,
|
|
||||||
// pub voucher_value: String,
|
|
||||||
// pub voucher_info: String,
|
|
||||||
// pub serial_number: String,
|
|
||||||
// pub binding_number: String,
|
|
||||||
// pub signature: String,
|
|
||||||
// pub epoch_id: String,
|
|
||||||
// pub consumed: bool,
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
|
#[cfg_attr(not(target_arch = "wasm32"), derive(sqlx::FromRow))]
|
||||||
#[derive(Zeroize, ZeroizeOnDrop, Clone)]
|
#[derive(Zeroize, ZeroizeOnDrop, Clone)]
|
||||||
pub struct StoredIssuedCredential {
|
pub struct StoredIssuedCredential {
|
||||||
|
|||||||
@@ -17,3 +17,11 @@ schemars = { workspace = true, features = ["preserve_order"] }
|
|||||||
serde = { workspace = true, features = ["derive"]}
|
serde = { workspace = true, features = ["derive"]}
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
|
||||||
|
# 'wasm-serde-types' feature
|
||||||
|
tsify = { workspace = true, features = ["js"], optional = true }
|
||||||
|
wasm-bindgen = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
wasm-serde-types = ["tsify", "wasm-bindgen"]
|
||||||
@@ -11,39 +11,96 @@ use std::{
|
|||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
ops::Not,
|
ops::Not,
|
||||||
};
|
};
|
||||||
use url::Url;
|
pub use url::{ParseError as UrlParseError, Url};
|
||||||
|
|
||||||
|
#[cfg(feature = "wasm-serde-types")]
|
||||||
|
use tsify::Tsify;
|
||||||
|
|
||||||
|
#[cfg(feature = "wasm-serde-types")]
|
||||||
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
pub mod mainnet;
|
pub mod mainnet;
|
||||||
pub mod var_names;
|
pub mod var_names;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ChainDetails {
|
pub struct ChainDetails {
|
||||||
|
#[serde(alias = "bech32_account_prefix")]
|
||||||
pub bech32_account_prefix: String,
|
pub bech32_account_prefix: String,
|
||||||
|
|
||||||
|
#[serde(alias = "mix_denom")]
|
||||||
pub mix_denom: DenomDetailsOwned,
|
pub mix_denom: DenomDetailsOwned,
|
||||||
|
|
||||||
|
#[serde(alias = "stake_denom")]
|
||||||
pub stake_denom: DenomDetailsOwned,
|
pub stake_denom: DenomDetailsOwned,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct NymContracts {
|
pub struct NymContracts {
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "mixnet_contract_address")]
|
||||||
pub mixnet_contract_address: Option<String>,
|
pub mixnet_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "vesting_contract_address")]
|
||||||
pub vesting_contract_address: Option<String>,
|
pub vesting_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "coconut_bandwidth_contract_address")]
|
||||||
pub coconut_bandwidth_contract_address: Option<String>,
|
pub coconut_bandwidth_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "group_contract_address")]
|
||||||
pub group_contract_address: Option<String>,
|
pub group_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "multisig_contract_address")]
|
||||||
pub multisig_contract_address: Option<String>,
|
pub multisig_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "coconut_dkg_contract_address")]
|
||||||
pub coconut_dkg_contract_address: Option<String>,
|
pub coconut_dkg_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "ephemera_contract_address")]
|
||||||
pub ephemera_contract_address: Option<String>,
|
pub ephemera_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "service_provider_directory_contract_address")]
|
||||||
pub service_provider_directory_contract_address: Option<String>,
|
pub service_provider_directory_contract_address: Option<String>,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "name_service_contract_address")]
|
||||||
pub name_service_contract_address: Option<String>,
|
pub name_service_contract_address: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
|
// I wanted to use the simpler `NetworkDetails` name, but there's a clash
|
||||||
// with `NetworkDetails` defined in all.rs...
|
// with `NetworkDetails` defined in all.rs...
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct NymNetworkDetails {
|
pub struct NymNetworkDetails {
|
||||||
|
#[serde(alias = "network_name")]
|
||||||
pub network_name: String,
|
pub network_name: String,
|
||||||
|
|
||||||
|
#[serde(alias = "chain_details")]
|
||||||
pub chain_details: ChainDetails,
|
pub chain_details: ChainDetails,
|
||||||
|
|
||||||
pub endpoints: Vec<ValidatorDetails>,
|
pub endpoints: Vec<ValidatorDetails>,
|
||||||
|
|
||||||
pub contracts: NymContracts,
|
pub contracts: NymContracts,
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "explorer_api")]
|
||||||
pub explorer_api: Option<String>,
|
pub explorer_api: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,10 +373,17 @@ impl DenomDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, Hash, Clone, PartialEq, Eq, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct DenomDetailsOwned {
|
pub struct DenomDetailsOwned {
|
||||||
pub base: String,
|
pub base: String,
|
||||||
|
|
||||||
pub display: String,
|
pub display: String,
|
||||||
|
|
||||||
// i.e. display_amount * 10^display_exponent = base_amount
|
// i.e. display_amount * 10^display_exponent = base_amount
|
||||||
|
#[serde(alias = "display_exponent")]
|
||||||
pub display_exponent: u32,
|
pub display_exponent: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,14 +408,24 @@ impl DenomDetailsOwned {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, JsonSchema)]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub struct ValidatorDetails {
|
pub struct ValidatorDetails {
|
||||||
// it is assumed those values are always valid since they're being provided in our defaults file
|
// it is assumed those values are always valid since they're being provided in our defaults file
|
||||||
|
#[serde(alias = "nyxd_url")]
|
||||||
pub nyxd_url: String,
|
pub nyxd_url: String,
|
||||||
//
|
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "websocket_url")]
|
||||||
pub websocket_url: Option<String>,
|
pub websocket_url: Option<String>,
|
||||||
|
|
||||||
// Right now api_url is optional as we are not running the api reliably on all validators
|
// Right now api_url is optional as we are not running the api reliably on all validators
|
||||||
// however, later on it should be a mandatory field
|
// however, later on it should be a mandatory field
|
||||||
|
#[cfg_attr(feature = "wasm-serde-types", tsify(optional))]
|
||||||
|
#[serde(alias = "api_url")]
|
||||||
pub api_url: Option<String>,
|
pub api_url: Option<String>,
|
||||||
// TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting
|
// TODO: I'd argue this one should also have a field like `gas_price` since its a validator-specific setting
|
||||||
}
|
}
|
||||||
@@ -373,16 +447,26 @@ impl ValidatorDetails {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_nyxd_url(&self) -> Result<Url, url::ParseError> {
|
||||||
|
self.nyxd_url.parse()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn nyxd_url(&self) -> Url {
|
pub fn nyxd_url(&self) -> Url {
|
||||||
self.nyxd_url
|
self.try_nyxd_url()
|
||||||
.parse()
|
|
||||||
.expect("the provided nyxd url is invalid!")
|
.expect("the provided nyxd url is invalid!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_api_url(&self) -> Option<Result<Url, url::ParseError>> {
|
||||||
|
self.api_url.as_ref().map(|url| url.parse())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn api_url(&self) -> Option<Url> {
|
pub fn api_url(&self) -> Option<Url> {
|
||||||
self.api_url
|
self.try_api_url()
|
||||||
.as_ref()
|
.map(|url| url.expect("the provided api url is invalid!"))
|
||||||
.map(|url| url.parse().expect("the provided api url is invalid!"))
|
}
|
||||||
|
|
||||||
|
pub fn try_websocket_url(&self) -> Option<Result<Url, url::ParseError>> {
|
||||||
|
self.websocket_url.as_ref().map(|url| url.parse())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn websocket_url(&self) -> Option<Url> {
|
pub fn websocket_url(&self) -> Option<Url> {
|
||||||
|
|||||||
@@ -37,11 +37,5 @@ wasm-utils = { path = "../utils" }
|
|||||||
wasm-storage = { path = "../storage" }
|
wasm-storage = { path = "../storage" }
|
||||||
|
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
|
||||||
# logging them with `console.error`. This is great for development, but requires
|
|
||||||
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
|
||||||
# code size when deploying.
|
|
||||||
console_error_panic_hook = { version = "0.1", optional = true }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["console_error_panic_hook"]
|
default = ["wasm-utils/console_error_panic_hook"]
|
||||||
@@ -17,6 +17,12 @@ gloo-utils = { workspace = true }
|
|||||||
gloo-net = { version = "0.3.1", features = ["websocket"], optional = true }
|
gloo-net = { version = "0.3.1", features = ["websocket"], optional = true }
|
||||||
#gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true }
|
#gloo-net = { path = "../../../../gloo/crates/net", features = ["websocket"], optional = true }
|
||||||
|
|
||||||
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
|
# code size when deploying.
|
||||||
|
console_error_panic_hook = { workspace = true, optional = true }
|
||||||
|
|
||||||
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
|
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
|
||||||
[dependencies.tungstenite]
|
[dependencies.tungstenite]
|
||||||
workspace = true
|
workspace = true
|
||||||
@@ -29,6 +35,7 @@ optional = true
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sleep"]
|
default = ["sleep"]
|
||||||
|
panic-hook = ["console_error_panic_hook"]
|
||||||
sleep = ["web-sys", "web-sys/Window"]
|
sleep = ["web-sys", "web-sys/Window"]
|
||||||
websocket = [
|
websocket = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
|||||||
@@ -70,3 +70,15 @@ pub async fn sleep(ms: i32) -> Result<(), wasm_bindgen::JsValue> {
|
|||||||
js_fut.await?;
|
js_fut.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
#[cfg(feature = "panic-hook")]
|
||||||
|
pub fn set_panic_hook() {
|
||||||
|
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||||
|
// `set_panic_hook` function at least once during initialization, and then
|
||||||
|
// we will get better error messages if our code ever panics.
|
||||||
|
//
|
||||||
|
// For more details see
|
||||||
|
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ wasm-bindgen = { workspace = true }
|
|||||||
wasm-bindgen-futures = { workspace = true }
|
wasm-bindgen-futures = { workspace = true }
|
||||||
zeroize = { workspace = true }
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
console_error_panic_hook = { version = "0.1", optional = true }
|
console_error_panic_hook = { workspace = true , optional = true }
|
||||||
|
|
||||||
wasm-utils = { path = "../../common/wasm/utils" }
|
wasm-utils = { path = "../../common/wasm/utils" }
|
||||||
wasm-storage = { path = "../../common/wasm/storage" }
|
wasm-storage = { path = "../../common/wasm/storage" }
|
||||||
|
|||||||
@@ -12,19 +12,3 @@ pub use error::ExtensionStorageError;
|
|||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub use storage::ExtensionStorage;
|
pub use storage::ExtensionStorage;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn set_panic_hook() {
|
|
||||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
|
||||||
// `set_panic_hook` function at least once during initialization, and then
|
|
||||||
// we will get better error messages if our code ever panics.
|
|
||||||
//
|
|
||||||
// For more details see
|
|
||||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
|
||||||
#[cfg(feature = "console_error_panic_hook")]
|
|
||||||
console_error_panic_hook::set_once();
|
|
||||||
}
|
|
||||||
|
|||||||
Generated
+11
@@ -962,6 +962,16 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_error_panic_hook"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -7988,6 +7998,7 @@ dependencies = [
|
|||||||
name = "wasm-utils"
|
name = "wasm-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
"futures",
|
"futures",
|
||||||
"getrandom 0.2.10",
|
"getrandom 0.2.10",
|
||||||
"gloo-net",
|
"gloo-net",
|
||||||
|
|||||||
+7
-1
@@ -15,7 +15,13 @@
|
|||||||
"nym-connect/**",
|
"nym-connect/**",
|
||||||
"explorer",
|
"explorer",
|
||||||
"types",
|
"types",
|
||||||
"clients/validator"
|
"clients/validator",
|
||||||
|
"sdk/typescript/packages/**",
|
||||||
|
"sdk/typescript/examples/**",
|
||||||
|
"sdk/typescript/codegen/**",
|
||||||
|
"sdk/typescript/packages/**",
|
||||||
|
"sdk/typescript/examples/**",
|
||||||
|
"sdk/typescript/codegen/**"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"nuke": "npx rimraf **/node_modules node_modules",
|
"nuke": "npx rimraf **/node_modules node_modules",
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Nym credential generation Usage Example
|
||||||
|
|
||||||
|
This is a simple project to show you how to use nym credential generation.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { mixFetch } from '@nymproject/mix-fetch';
|
||||||
|
|
||||||
|
// HTTP GET
|
||||||
|
const response = await mixFetch('https://nymtech.net');
|
||||||
|
const html = await response.text();
|
||||||
|
|
||||||
|
// HTTP POST
|
||||||
|
const apiResponse = await mixFetch('https://api.example.com', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ foo: 'bar' }),
|
||||||
|
headers: { [`Content-Type`]: 'application/json', Authorization: `Bearer ${AUTH_TOKEN}` }
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the example
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
Open a browser at http://localhost:1234 and as the example loads, a connection will be made to the Nym Mixnet
|
||||||
|
and a text file and image will be downloaded and displayed in the browser.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "@nymproject/nym-credentials-example-parcel",
|
||||||
|
"version": "1.0.4-rc.2",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "parcel build --no-cache --no-content-hash",
|
||||||
|
"serve": "serve dist",
|
||||||
|
"start": "parcel --no-cache"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nymproject/sdk": "1.2.4-rc.2",
|
||||||
|
"parcel": "^2.9.3"
|
||||||
|
},
|
||||||
|
"private": false,
|
||||||
|
"source": "src/index.html"
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Internal Credential Tester</title>
|
||||||
|
<script type="module" src="./index.ts"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<h1>Credential</h1>
|
||||||
|
<input id="mnemonic" placeholder="mnemonic"></input>
|
||||||
|
<input id="coin" placeholder="amount" value="1unym"></input>
|
||||||
|
<button id="button">Get Credential</button>
|
||||||
|
<div id="credential"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { createNymCredentialsClient } from '@nymproject/sdk';
|
||||||
|
import { appendOutput } from './utils';
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const mnemonic = document.getElementById('mnemonic') as HTMLInputElement;
|
||||||
|
if (process.env.MNEMONIC) {
|
||||||
|
mnemonic.defaultValue = process.env.MNEMONIC;
|
||||||
|
}
|
||||||
|
const coin = document.getElementById('coin') as HTMLInputElement;
|
||||||
|
const button = document.getElementById('button') as HTMLButtonElement;
|
||||||
|
|
||||||
|
const client = await createNymCredentialsClient();
|
||||||
|
|
||||||
|
const generateCredential = async () => {
|
||||||
|
const amount = coin.value;
|
||||||
|
const mnemonicString = mnemonic.value;
|
||||||
|
console.log({ amount, mnemonicString });
|
||||||
|
try {
|
||||||
|
appendOutput('About to get a credential... 🥁');
|
||||||
|
const credential = await client.comlink.acquireCredential(amount, mnemonicString, { useSandbox: true }); // options: {useSandbox?: boolean; networkDetails?: {}}
|
||||||
|
appendOutput('Success! 🎉');
|
||||||
|
appendOutput(JSON.stringify(credential, null, 2));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to get credential', e);
|
||||||
|
appendOutput((e as any).message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button.addEventListener('click', () => generateCredential());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the html to load
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// let's do this!
|
||||||
|
main();
|
||||||
|
});
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export function appendOutput(value: string) {
|
||||||
|
const el = document.getElementById('credential') as HTMLPreElement;
|
||||||
|
const text = document.createTextNode(`${value}\n`);
|
||||||
|
el.appendChild(text);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
src/mixnet/wasm/worker.js
|
src/mixnet/wasm/worker.js
|
||||||
|
src/coconut/worker.js
|
||||||
docs/
|
docs/
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "scripts/build-prod.sh",
|
"build": "scripts/build-prod.sh",
|
||||||
"build:dev": "scripts/build.sh",
|
"build:dev": "scripts/build.sh",
|
||||||
"build:dev:esm": "scripts/build-dev-esm.sh",
|
"build:dev:esm": "SDK_DEV_MODE=true scripts/build-dev-esm.sh",
|
||||||
|
"build:dev:esm:no-inline": "scripts/build-dev-esm.sh",
|
||||||
"build:worker": "rollup -c rollup-worker.config.mjs",
|
"build:worker": "rollup -c rollup-worker.config.mjs",
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"docs:dev": "run-p docs:watch docs:serve ",
|
"docs:dev": "run-p docs:watch docs:serve ",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nymproject/nym-client-wasm": ">=1.2.4-rc.2 || ^1",
|
"@nymproject/nym-client-wasm": ">=1.2.4-rc.2 || ^1",
|
||||||
|
"@nymproject/nym-credential-client-wasm": ">=1.2.0-rc.9 || ^1",
|
||||||
"comlink": "^4.3.1"
|
"comlink": "^4.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { getConfig } from './rollup/worker.mjs';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...getConfig('src/coconut/worker.ts', 'nym_credential_client_wasm_bg.wasm'),
|
||||||
|
inlineWasm: true,
|
||||||
|
format: 'cjs',
|
||||||
|
};
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { getConfig } from './rollup/worker.mjs';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...getConfig('src/coconut/worker.ts', 'nym_credential_client_wasm_bg.wasm', {
|
||||||
|
inlineWasm: process.env.SDK_DEV_MODE === 'true',
|
||||||
|
}),
|
||||||
|
};
|
||||||
@@ -2,6 +2,6 @@ import { getConfig } from './rollup/esm.mjs';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
...getConfig({
|
...getConfig({
|
||||||
inline: false,
|
inline: process.env.SDK_DEV_MODE === 'true',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { getConfig } from './rollup/worker.mjs';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...getConfig('src/mixnet/wasm/worker.ts', 'nym_client_wasm_bg.wasm', {
|
||||||
|
inlineWasm: true,
|
||||||
|
format: 'cjs',
|
||||||
|
}),
|
||||||
|
};
|
||||||
@@ -1,26 +1,7 @@
|
|||||||
import typescript from '@rollup/plugin-typescript';
|
import { getConfig } from './rollup/worker.mjs';
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
|
||||||
import { wasm } from '@rollup/plugin-wasm';
|
|
||||||
import replace from '@rollup/plugin-replace';
|
|
||||||
|
|
||||||
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'src/mixnet/wasm/worker.ts',
|
...getConfig('src/mixnet/wasm/worker.ts', 'nym_client_wasm_bg.wasm', {
|
||||||
output: {
|
inlineWasm: process.env.SDK_DEV_MODE === 'true',
|
||||||
dir: 'dist',
|
}),
|
||||||
format: 'cjs',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
resolve({ extensions }),
|
|
||||||
// this is some nasty monkey patching that removes the WASM URL (because it is handled by the `wasm` plugin)
|
|
||||||
replace({
|
|
||||||
values: { "input = new URL('nym_client_wasm_bg.wasm', import.meta.url);": 'input = undefined;' },
|
|
||||||
delimiters: ['', ''],
|
|
||||||
preventAssignment: true,
|
|
||||||
}),
|
|
||||||
// force the wasm plugin to embed the wasm bundle - this means no downstream bundlers have to worry about handling it
|
|
||||||
wasm({ maxFileSize: 10000000, targetEnv: 'browser' }),
|
|
||||||
typescript({ compilerOptions: { declaration: false, target: 'es5' } }),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const getConfig = (opts) => ({
|
|||||||
webWorkerLoader({ targetPlatform: 'browser', inline: opts.inline }), // the inline param is used here
|
webWorkerLoader({ targetPlatform: 'browser', inline: opts.inline }), // the inline param is used here
|
||||||
resolve({ extensions }),
|
resolve({ extensions }),
|
||||||
typescript({
|
typescript({
|
||||||
exclude: ['mixnet/wasm/worker.ts'],
|
exclude: ['mixnet/wasm/worker.ts', 'coconut/worker.ts'],
|
||||||
compilerOptions: { outDir: opts.outputDir || 'dist/esm' },
|
compilerOptions: { outDir: opts.outputDir || 'dist/esm' },
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
import { wasm } from '@rollup/plugin-wasm';
|
||||||
|
import replace from '@rollup/plugin-replace';
|
||||||
|
|
||||||
|
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure worker output
|
||||||
|
*
|
||||||
|
* @param opts
|
||||||
|
* `format`: `es` or `cjs`,
|
||||||
|
* `inlineWasm`: true or false,
|
||||||
|
* `tsTarget`: `es5` or `es6`
|
||||||
|
*/
|
||||||
|
export const getConfig = (input, wasmFilename, opts) => ({
|
||||||
|
input,
|
||||||
|
output: {
|
||||||
|
dir: 'dist',
|
||||||
|
format: opts?.format || 'es',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
resolve({ extensions }),
|
||||||
|
// this is some nasty monkey patching that removes the WASM URL (because it is handled by the `wasm` plugin)
|
||||||
|
replace({
|
||||||
|
values: { [`input = new URL('${wasmFilename}', import.meta.url);`]: 'input = undefined;' },
|
||||||
|
delimiters: ['', ''],
|
||||||
|
preventAssignment: true,
|
||||||
|
}),
|
||||||
|
opts?.inlineWasm === true
|
||||||
|
? wasm({ maxFileSize: 10_000_000, targetEnv: 'browser' }) // force the wasm plugin to embed the wasm bundle - this means no downstream bundlers have to worry about handling it
|
||||||
|
: wasm({
|
||||||
|
targetEnv: 'browser',
|
||||||
|
fileName: '[name].wasm',
|
||||||
|
}),
|
||||||
|
typescript({
|
||||||
|
compilerOptions: {
|
||||||
|
declaration: false,
|
||||||
|
target: opts?.tsTarget || 'es6',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
@@ -14,7 +14,21 @@ set -o pipefail
|
|||||||
rollup -c rollup-worker.config.mjs
|
rollup -c rollup-worker.config.mjs
|
||||||
|
|
||||||
# move it next to the Typescript `src/index.ts` so it can be inlined by rollup
|
# move it next to the Typescript `src/index.ts` so it can be inlined by rollup
|
||||||
cp dist/worker.js src/worker/worker.js || true
|
cp dist/worker.js src/mixnet/wasm/worker.js || true
|
||||||
|
rm dist/worker.js || true
|
||||||
|
|
||||||
|
#-------------------------------------------------------
|
||||||
|
# WEB WORKER (COCONUT WASM)
|
||||||
|
#-------------------------------------------------------
|
||||||
|
# The web worker needs to be bundled because the WASM bundle needs to be loaded synchronously and all dependencies
|
||||||
|
# must be included in the worker script (because it is not loaded as an ES Module)
|
||||||
|
|
||||||
|
# build the worker
|
||||||
|
rollup -c rollup-coconut-worker.config.mjs
|
||||||
|
|
||||||
|
# move it next to the Typescript `src/index.ts` so it can be inlined by rollup
|
||||||
|
mkdir dist/esm || true
|
||||||
|
cp dist/worker.js src/coconut/worker.js || true
|
||||||
rm dist/worker.js || true
|
rm dist/worker.js || true
|
||||||
|
|
||||||
#-------------------------------------------------------
|
#-------------------------------------------------------
|
||||||
|
|||||||
@@ -19,6 +19,19 @@ rollup -c rollup-worker.config.mjs
|
|||||||
rm -f src/mixnet/wasm/worker.js
|
rm -f src/mixnet/wasm/worker.js
|
||||||
mv dist/worker.js src/mixnet/wasm/worker.js
|
mv dist/worker.js src/mixnet/wasm/worker.js
|
||||||
|
|
||||||
|
#-------------------------------------------------------
|
||||||
|
# WEB WORKER (COCONUT WASM)
|
||||||
|
#-------------------------------------------------------
|
||||||
|
# The web worker needs to be bundled because the WASM bundle needs to be loaded synchronously and all dependencies
|
||||||
|
# must be included in the worker script (because it is not loaded as an ES Module)
|
||||||
|
|
||||||
|
# build the worker
|
||||||
|
rollup -c rollup-coconut-worker.config.mjs
|
||||||
|
|
||||||
|
# move it next to the Typescript `src/index.ts` so it can be inlined by rollup
|
||||||
|
cp dist/worker.js src/coconut/worker.js || true
|
||||||
|
rm dist/worker.js || true
|
||||||
|
|
||||||
#-------------------------------------------------------
|
#-------------------------------------------------------
|
||||||
# ESM
|
# ESM
|
||||||
#-------------------------------------------------------
|
#-------------------------------------------------------
|
||||||
|
|||||||
@@ -1,7 +1,41 @@
|
|||||||
// eslint-disable-next-line no-console
|
import * as Comlink from 'comlink';
|
||||||
|
import InlineWasmWebWorker from 'web-worker:./worker';
|
||||||
|
import { EventKinds, INymCredentialClientWebWorker, NymCredentialsClient } from './types';
|
||||||
|
|
||||||
|
export const createNymCredentialsClient = async (): Promise<NymCredentialsClient> => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||||
|
const worker = await createWorker();
|
||||||
|
|
||||||
|
// let comlink handle interop with the web worker
|
||||||
|
const comlink = Comlink.wrap<INymCredentialClientWebWorker>(worker);
|
||||||
|
|
||||||
|
return { comlink };
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* Async method to create a web worker that runs the Nym credentials client on another thread. It will only return once the worker
|
||||||
* @internal
|
* has passed back a `Loaded` event to the calling thread.
|
||||||
|
*
|
||||||
|
* @return The instance of the web worker.
|
||||||
*/
|
*/
|
||||||
export const notImplementedYet = () => console.log('Not implement, coming soon...');
|
const createWorker = async () =>
|
||||||
|
new Promise<Worker>((resolve, reject) => {
|
||||||
|
// rollup will inline the built worker script, so that when the SDK is used in
|
||||||
|
// other projects, they will not need to mess around trying to bundle it
|
||||||
|
// however, it will make this SDK bundle bigger because of Base64 inline data
|
||||||
|
const worker = new InlineWasmWebWorker();
|
||||||
|
|
||||||
|
worker.addEventListener('error', reject);
|
||||||
|
worker.addEventListener(
|
||||||
|
'message',
|
||||||
|
(msg) => {
|
||||||
|
worker.removeEventListener('error', reject);
|
||||||
|
if (msg.data?.kind === EventKinds.Loaded) {
|
||||||
|
resolve(worker);
|
||||||
|
} else {
|
||||||
|
reject(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Enum representing various event kinds.
|
||||||
|
* @enum
|
||||||
|
*/
|
||||||
|
export enum EventKinds {
|
||||||
|
Loaded = 'Loaded',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadedEvent {
|
||||||
|
kind: EventKinds.Loaded;
|
||||||
|
args: {
|
||||||
|
loaded: true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Credential = any; // TODO
|
||||||
|
|
||||||
|
export interface CredentialClientOpts {
|
||||||
|
useSandbox?: boolean;
|
||||||
|
networkDetails?: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface INymCredentialClientWebWorker {
|
||||||
|
acquireCredential: (coin: string, mnemonic: string, opts: CredentialClientOpts) => Promise<Credential>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// export interface NymCredentialsClient {
|
||||||
|
// init: (mnemonic: string) => void;
|
||||||
|
// acquireCredential: (coin: Coin, mnemonic: string, options?: CredentialsClientOpts) => Promise<Credentials>;
|
||||||
|
// }
|
||||||
|
|
||||||
|
export interface NymCredentialsClient {
|
||||||
|
comlink: INymCredentialClientWebWorker;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
|
import * as Comlink from 'comlink';
|
||||||
|
//
|
||||||
|
// Rollup will replace wasmBytes with a function that loads the WASM bundle from a base64 string embedded in the output.
|
||||||
|
//
|
||||||
|
// Doing it this way, saves having to support a large variety of bundlers and their quirks.
|
||||||
|
//
|
||||||
|
// @ts-ignore
|
||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
import wasmBytes from '@nymproject/nym-credential-client-wasm/nym_credential_client_wasm_bg.wasm';
|
||||||
|
import init, { acquireCredential } from '@nymproject/nym-credential-client-wasm/nym_credential_client_wasm';
|
||||||
|
import type { INymCredentialClientWebWorker, CredentialClientOpts, LoadedEvent } from './types';
|
||||||
|
import { EventKinds } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to send typed messages.
|
||||||
|
* @param event The strongly typed message to send back to the calling thread.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-restricted-globals
|
||||||
|
const postMessageWithType = <E>(event: E) => self.postMessage(event);
|
||||||
|
|
||||||
|
console.log('[Nym WASM client for Credentials] Starting Nym WASM web worker...');
|
||||||
|
|
||||||
|
// load WASM binary
|
||||||
|
async function main() {
|
||||||
|
// rollup with provide a function to get the mixFetch WASM bytes
|
||||||
|
const bytes = await wasmBytes();
|
||||||
|
|
||||||
|
// load rust WASM package
|
||||||
|
const wasmPackage = await init(bytes);
|
||||||
|
|
||||||
|
console.log('Loaded RUST WASM');
|
||||||
|
|
||||||
|
wasmPackage.set_panic_hook();
|
||||||
|
|
||||||
|
const webWorker: INymCredentialClientWebWorker = {
|
||||||
|
async acquireCredential(coin: string, mnemonic: string, opts: CredentialClientOpts) {
|
||||||
|
console.log('[Worker] --- acquireCredential ---', { coin, mnemonic, opts });
|
||||||
|
return acquireCredential(mnemonic, coin, opts);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// start comlink listening for messages and handle them above
|
||||||
|
Comlink.expose(webWorker);
|
||||||
|
|
||||||
|
// notify any listeners that the web worker has loaded and is ready for testing
|
||||||
|
postMessageWithType<LoadedEvent>({ kind: EventKinds.Loaded, args: { loaded: true } });
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((e: any) => console.error('Unhandled exception in credential worker', e));
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
Arguments:
|
||||||
|
/Users/galacalero/.nvm/versions/node/v16.19.0/bin/node /Users/galacalero/.yarn/bin/yarn.js install
|
||||||
|
|
||||||
|
PATH:
|
||||||
|
/Users/galacalero/.rvm/gems/ruby-2.7.5/bin:/Users/galacalero/.rvm/gems/ruby-2.7.5@global/bin:/Users/galacalero/.rvm/rubies/ruby-2.7.5/bin:/Users/galacalero/.rvm/bin:/Users/galacalero/.pyenv/shims:/Users/galacalero/.nvm/versions/node/v16.19.0/bin:/Users/galacalero/.yarn/bin:/Users/galacalero/.config/yarn/global/node_modules/.bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/Applications/Keybase.app/Contents/SharedSupport/bin:/Library/Apple/usr/bin:/Applications/Postgres.app/Contents/Versions/latest/bin:/Users/galacalero/.cargo/bin
|
||||||
|
|
||||||
|
Yarn version:
|
||||||
|
1.22.18
|
||||||
|
|
||||||
|
Node version:
|
||||||
|
16.19.0
|
||||||
|
|
||||||
|
Platform:
|
||||||
|
darwin arm64
|
||||||
|
|
||||||
|
Trace:
|
||||||
|
Error: https://registry.yarnpkg.com/@nymproject%2feslint-config-react-typescript: Not found
|
||||||
|
at Request.params.callback [as _callback] (/Users/galacalero/.yarn/lib/cli.js:66138:18)
|
||||||
|
at Request.self.callback (/Users/galacalero/.yarn/lib/cli.js:140883:22)
|
||||||
|
at Request.emit (node:events:513:28)
|
||||||
|
at Request.<anonymous> (/Users/galacalero/.yarn/lib/cli.js:141855:10)
|
||||||
|
at Request.emit (node:events:513:28)
|
||||||
|
at IncomingMessage.<anonymous> (/Users/galacalero/.yarn/lib/cli.js:141777:12)
|
||||||
|
at Object.onceWrapper (node:events:627:28)
|
||||||
|
at IncomingMessage.emit (node:events:525:35)
|
||||||
|
at endReadableNT (node:internal/streams/readable:1358:12)
|
||||||
|
at processTicksAndRejections (node:internal/process/task_queues:83:21)
|
||||||
|
|
||||||
|
npm manifest:
|
||||||
|
{
|
||||||
|
"name": "@nymproject/sdk",
|
||||||
|
"version": "1.2.4-rc.2",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"author": "Nym Technologies SA",
|
||||||
|
"files": [
|
||||||
|
"dist/esm/worker.js",
|
||||||
|
"dist/cjs/worker.js",
|
||||||
|
"dist/**/*"
|
||||||
|
],
|
||||||
|
"main": "dist/cjs/index.js",
|
||||||
|
"browser": "dist/esm/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "scripts/build-prod.sh",
|
||||||
|
"build:dev": "scripts/build.sh",
|
||||||
|
"build:dev:esm": "scripts/build-dev-esm.sh",
|
||||||
|
"build:worker": "rollup -c rollup-worker.config.mjs",
|
||||||
|
"clean": "rimraf dist",
|
||||||
|
"docs:dev": "run-p docs:watch docs:serve ",
|
||||||
|
"docs:generate": "typedoc",
|
||||||
|
"docs:generate:prod": "typedoc --basePath ./docs/tsdoc/nymproject/sdk/",
|
||||||
|
"docs:prod:build": "scripts/build-prod-docs-collect.sh",
|
||||||
|
"docs:serve": "reload -b -d ./docs -p 3000",
|
||||||
|
"docs:watch": "nodemon --ext ts --watch './src/**/*' --watch './typedoc.json' --exec \"yarn docs:generate\"",
|
||||||
|
"lint": "eslint src",
|
||||||
|
"lint:fix": "eslint src --fix",
|
||||||
|
"prebuild": "node scripts/showDependencyLocation.cjs",
|
||||||
|
"start": "tsc -w",
|
||||||
|
"start:dev": "nodemon --watch src -e ts,json --exec 'yarn build:dev:esm'",
|
||||||
|
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache",
|
||||||
|
"tsc": "tsc --noEmit true"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nymproject/nym-client-wasm": ">=1.2.4-rc.2 || ^1",
|
||||||
|
"comlink": "^4.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.15.0",
|
||||||
|
"@babel/plugin-transform-async-to-generator": "^7.14.5",
|
||||||
|
"@babel/preset-env": "^7.15.0",
|
||||||
|
"@babel/preset-react": "^7.14.5",
|
||||||
|
"@babel/preset-typescript": "^7.15.0",
|
||||||
|
"@nymproject/eslint-config-react-typescript": "^1.0.0",
|
||||||
|
"@rollup/plugin-commonjs": "^24.0.1",
|
||||||
|
"@rollup/plugin-inject": "^5.0.3",
|
||||||
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
|
"@rollup/plugin-replace": "^5.0.2",
|
||||||
|
"@rollup/plugin-terser": "^0.2.1",
|
||||||
|
"@rollup/plugin-typescript": "^10.0.1",
|
||||||
|
"@rollup/plugin-url": "^8.0.1",
|
||||||
|
"@rollup/plugin-wasm": "^6.1.1",
|
||||||
|
"@types/jest": "^27.0.1",
|
||||||
|
"@types/node": "^16.7.13",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||||
|
"@typescript-eslint/parser": "^5.13.0",
|
||||||
|
"eslint": "^8.10.0",
|
||||||
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
|
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-import-resolver-root-import": "^1.0.4",
|
||||||
|
"eslint-plugin-import": "^2.25.4",
|
||||||
|
"eslint-plugin-jest": "^26.1.1",
|
||||||
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"eslint-plugin-react": "^7.29.2",
|
||||||
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
|
"handlebars": "^4.7.8",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"nodemon": "3.0.1",
|
||||||
|
"reload": "^3.2.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"rollup": "^3.9.1",
|
||||||
|
"rollup-plugin-base64": "^1.0.1",
|
||||||
|
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-loader": "^9.4.2",
|
||||||
|
"typedoc": "^0.24.8",
|
||||||
|
"typescript": "^4.8.4"
|
||||||
|
},
|
||||||
|
"private": false,
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/esm/index.d.ts"
|
||||||
|
}
|
||||||
|
|
||||||
|
yarn manifest:
|
||||||
|
No manifest
|
||||||
|
|
||||||
|
Lockfile:
|
||||||
|
No lockfile
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
[package]
|
||||||
|
name = "nym-credential-client-wasm"
|
||||||
|
authors = []
|
||||||
|
version = "1.3.0-rc.0"
|
||||||
|
edition = "2021"
|
||||||
|
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
|
||||||
|
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]
|
||||||
|
bip39 = { workspace = true }
|
||||||
|
#futures = { workspace = true }
|
||||||
|
js-sys = { workspace = true }
|
||||||
|
#rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
#serde_json = { workspace = true }
|
||||||
|
serde-wasm-bindgen = { workspace = true }
|
||||||
|
wasm-bindgen = { workspace = true }
|
||||||
|
wasm-bindgen-futures = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
tsify = { workspace = true, features = ["js"] }
|
||||||
|
zeroize = { workspace = true }
|
||||||
|
|
||||||
|
wasm-utils = { path = "../../common/wasm/utils", features = ["panic-hook"] }
|
||||||
|
|
||||||
|
nym-bin-common = { path = "../../common/bin-common" }
|
||||||
|
nym-credentials = { path = "../../common/credentials" }
|
||||||
|
nym-credential-storage = { path = "../../common/credential-storage" }
|
||||||
|
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||||
|
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||||
|
nym-credentials-interface = { path = "../../common/credentials-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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
build:
|
||||||
|
wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/credentials
|
||||||
|
wasm-opt -Oz -o ../../dist/wasm/credentials/nym_credential_client_wasm_bg.wasm ../../dist/wasm/credentials/nym_credential_client_wasm_bg.wasm
|
||||||
|
|
||||||
|
# make my life easier so I wouldn't need to deal with any bundlers. sorry @MS : )
|
||||||
|
build-debug-dev:
|
||||||
|
wasm-pack build --scope nymproject --target no-modules
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// A dependency graph that contains any wasm must all be imported
|
||||||
|
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||||
|
// that no one else needs to worry about it again.
|
||||||
|
import('./index.js')
|
||||||
|
.catch(e => console.error('Error importing `index.js`:', e));
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Nym Credentials WebAssembly Demo</title>
|
||||||
|
<script src="bootstrap.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1> Yet another coconut demo </h1>
|
||||||
|
<div>
|
||||||
|
<label>mnemonic: </label>
|
||||||
|
<input type="text" size = "120" id="mnemonic" value="...">
|
||||||
|
|
||||||
|
</br>
|
||||||
|
</br>
|
||||||
|
|
||||||
|
<label>amount (in <b>unym</b>): </label>
|
||||||
|
<input type="number" size = "60" id="credential-amount" value=0>
|
||||||
|
|
||||||
|
<button id="coconut-button"> Get Coconut Credential </button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<p>
|
||||||
|
<span id="output"></span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2020-2023 Nym Technologies SA
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
class WebWorkerClient {
|
||||||
|
worker = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.worker = new Worker('./worker.js');
|
||||||
|
|
||||||
|
this.worker.onmessage = (ev) => {
|
||||||
|
if (ev.data && ev.data.kind) {
|
||||||
|
switch (ev.data.kind) {
|
||||||
|
case 'ReceivedCredential':
|
||||||
|
const { credential } = ev.data.args;
|
||||||
|
displayCredential(credential)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getCredential = (amount, mnemonic) => {
|
||||||
|
if (!this.worker) {
|
||||||
|
console.error('Could not get credential because worker does not exist');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker.postMessage({
|
||||||
|
kind: 'GetCredential',
|
||||||
|
args: {
|
||||||
|
amount,
|
||||||
|
mnemonic
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let client = null;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
client = new WebWorkerClient();
|
||||||
|
|
||||||
|
const coconutButton = document.querySelector('#coconut-button');
|
||||||
|
coconutButton.onclick = function () {
|
||||||
|
getCredential();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCredential() {
|
||||||
|
const amount = document.getElementById('credential-amount').value;
|
||||||
|
const mnemonic = document.getElementById('mnemonic').value;
|
||||||
|
|
||||||
|
await client.getCredential(amount, mnemonic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function displayCredential(credential) {
|
||||||
|
console.log("got credential", credential)
|
||||||
|
|
||||||
|
let timestamp = new Date().toISOString().substr(11, 12);
|
||||||
|
|
||||||
|
let credentialDiv = document.createElement('div');
|
||||||
|
let paragraph = document.createElement('p');
|
||||||
|
paragraph.setAttribute('style', 'color: blue');
|
||||||
|
let paragraphContent = document.createTextNode(timestamp + ' 🥥🥥🥥 >>> ' + JSON.stringify(credential));
|
||||||
|
paragraph.appendChild(paragraphContent);
|
||||||
|
|
||||||
|
credentialDiv.appendChild(paragraph);
|
||||||
|
document.getElementById('output').appendChild(credentialDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "create-wasm-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "create an app to consume rust-generated wasm packages",
|
||||||
|
"main": "index.js",
|
||||||
|
"bin": {
|
||||||
|
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config webpack.config.js",
|
||||||
|
"build:wasm": "cd ../ && make wasm-build",
|
||||||
|
"start": "webpack-dev-server --port 8001"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"webassembly",
|
||||||
|
"wasm",
|
||||||
|
"rust",
|
||||||
|
"webpack"
|
||||||
|
],
|
||||||
|
"author": "Gala Calero <https://github.com/gala1234>",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/nymtech/nym/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://nymtech.net/docs",
|
||||||
|
"devDependencies": {
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"hello-wasm-pack": "^0.1.0",
|
||||||
|
"webpack": "^5.70.0",
|
||||||
|
"webpack-cli": "^4.9.2",
|
||||||
|
"webpack-dev-server": "^4.7.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nymproject/nym-credential-client-wasm": "file:../pkg"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
performance: {
|
||||||
|
hints: false,
|
||||||
|
maxEntrypointSize: 512000,
|
||||||
|
maxAssetSize: 512000,
|
||||||
|
},
|
||||||
|
entry: {
|
||||||
|
bootstrap: "./bootstrap.js",
|
||||||
|
worker: "./worker.js",
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, "dist"),
|
||||||
|
filename: "[name].js",
|
||||||
|
},
|
||||||
|
mode: "development",
|
||||||
|
// mode: 'production',
|
||||||
|
plugins: [
|
||||||
|
new CopyWebpackPlugin({
|
||||||
|
patterns: [
|
||||||
|
"index.html",
|
||||||
|
{
|
||||||
|
from: "../pkg/*.(js|wasm)",
|
||||||
|
to: "[name][ext]",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
devServer: {
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'https://sandbox-nym-api1.nymtech.net/',
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
||||||
|
"Access-Control-Allow-Headers":
|
||||||
|
"X-Requested-With, content-type, Authorization",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
experiments: { syncWebAssembly: true },
|
||||||
|
};
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2020-2023 Nym Technologies SA
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const RUST_WASM_URL = "nym_credential_client_wasm_bg.wasm"
|
||||||
|
|
||||||
|
importScripts('nym_credential_client_wasm.js');
|
||||||
|
|
||||||
|
console.log('Initializing worker');
|
||||||
|
|
||||||
|
// wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts`
|
||||||
|
const {
|
||||||
|
acquireCredential,
|
||||||
|
} = wasm_bindgen;
|
||||||
|
|
||||||
|
async function testGetCredential() {
|
||||||
|
self.onmessage = async event => {
|
||||||
|
if (event.data && event.data.kind) {
|
||||||
|
switch (event.data.kind) {
|
||||||
|
case 'GetCredential': {
|
||||||
|
const { amount, mnemonic } = event.data.args;
|
||||||
|
|
||||||
|
// TODO: this should just use cosmjs' coin
|
||||||
|
let coin = `${amount}unym`
|
||||||
|
console.log(`getting credential for ${coin}`);
|
||||||
|
|
||||||
|
let credential = await acquireCredential(mnemonic, coin, { useSandbox: true })
|
||||||
|
|
||||||
|
self.postMessage({
|
||||||
|
kind : 'ReceivedCredential',
|
||||||
|
args: {
|
||||||
|
credential
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log(">>>>>>>>>>>>>>>>>>>>> JS WORKER MAIN START");
|
||||||
|
|
||||||
|
// load rust WASM package
|
||||||
|
await wasm_bindgen(RUST_WASM_URL);
|
||||||
|
console.log('Loaded RUST WASM');
|
||||||
|
|
||||||
|
// run test on simplified and dedicated tester:
|
||||||
|
await testGetCredential();
|
||||||
|
//
|
||||||
|
console.log(">>>>>>>>>>>>>>>>>>>>> JS WORKER MAIN END")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's get started!
|
||||||
|
main();
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::error::WasmCredentialClientError;
|
||||||
|
use crate::opts::CredentialClientOpts;
|
||||||
|
use js_sys::Promise;
|
||||||
|
use nym_credential_storage::ephemeral_storage::EphemeralCredentialStorage;
|
||||||
|
use nym_credential_storage::models::StoredIssuedCredential;
|
||||||
|
use nym_network_defaults::NymNetworkDetails;
|
||||||
|
use nym_validator_client::nyxd::{Config, CosmWasmCoin};
|
||||||
|
use nym_validator_client::DirectSigningReqwestRpcNyxdClient;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tsify::Tsify;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen_futures::future_to_promise;
|
||||||
|
use wasm_utils::console_log;
|
||||||
|
use wasm_utils::error::PromisableResult;
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = acquireCredential)]
|
||||||
|
pub fn acquire_credential(mnemonic: String, amount: String, opts: CredentialClientOpts) -> Promise {
|
||||||
|
future_to_promise(async move {
|
||||||
|
acquire_credential_async(mnemonic, amount, opts)
|
||||||
|
.await
|
||||||
|
.map(|credential| {
|
||||||
|
serde_wasm_bindgen::to_value(&credential).expect("this serialization can't fail")
|
||||||
|
})
|
||||||
|
.into_promise_result()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn acquire_credential_async(
|
||||||
|
mnemonic: String,
|
||||||
|
amount: String,
|
||||||
|
opts: CredentialClientOpts,
|
||||||
|
) -> Result<WasmIssuedCredential, WasmCredentialClientError> {
|
||||||
|
// start by parsing mnemonic so that we could immediately move it into a Zeroizing wrapper
|
||||||
|
let mnemonic = crate::helpers::parse_mnemonic(mnemonic)?;
|
||||||
|
|
||||||
|
// why are we parsing into CosmWasmCoin and not "our" Coin?
|
||||||
|
// simple. because it has the nicest 'FromStr' impl
|
||||||
|
let amount: CosmWasmCoin =
|
||||||
|
amount
|
||||||
|
.parse()
|
||||||
|
.map_err(|source| WasmCredentialClientError::MalformedCoin {
|
||||||
|
source: Box::new(source),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if amount.amount.is_zero() {
|
||||||
|
return Err(WasmCredentialClientError::ZeroCoinValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let network = match opts.network_details {
|
||||||
|
Some(specified) => specified,
|
||||||
|
None => {
|
||||||
|
if let Some(true) = opts.use_sandbox {
|
||||||
|
crate::helpers::minimal_coconut_sandbox()
|
||||||
|
} else {
|
||||||
|
NymNetworkDetails::new_mainnet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = Config::try_from_nym_network_details(&network)?;
|
||||||
|
|
||||||
|
// just get the first nyxd endpoint
|
||||||
|
let nyxd_endpoint = network
|
||||||
|
.endpoints
|
||||||
|
.get(0)
|
||||||
|
.ok_or(WasmCredentialClientError::NoNyxdEndpoints)?
|
||||||
|
.try_nyxd_url()?;
|
||||||
|
|
||||||
|
let client = DirectSigningReqwestRpcNyxdClient::connect_reqwest_with_mnemonic(
|
||||||
|
config,
|
||||||
|
nyxd_endpoint,
|
||||||
|
mnemonic,
|
||||||
|
);
|
||||||
|
|
||||||
|
console_log!("starting the deposit...");
|
||||||
|
let deposit_state = nym_bandwidth_controller::acquire::deposit(&client, amount).await?;
|
||||||
|
let blinded_serial = deposit_state.voucher.blinded_serial_number_bs58();
|
||||||
|
console_log!(
|
||||||
|
"obtained bandwidth voucher with the following blinded serial number: {blinded_serial}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: use proper persistent storage here. probably indexeddb like we have for our 'normal' wasm client
|
||||||
|
let ephemeral_storage = EphemeralCredentialStorage::default();
|
||||||
|
|
||||||
|
// store credential in the ephemeral storage...
|
||||||
|
nym_bandwidth_controller::acquire::get_bandwidth_voucher(
|
||||||
|
&deposit_state,
|
||||||
|
&client,
|
||||||
|
&ephemeral_storage,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// and immediately get it out!
|
||||||
|
let mut credentials = ephemeral_storage.take_credentials().await;
|
||||||
|
let cred = credentials.pop().expect("we just got a credential issued");
|
||||||
|
|
||||||
|
Ok(cred.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Tsify, Debug, Clone, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
|
||||||
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct WasmIssuedCredential {
|
||||||
|
pub serialization_revision: u8,
|
||||||
|
pub credential_data: Vec<u8>,
|
||||||
|
pub credential_type: String,
|
||||||
|
pub epoch_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StoredIssuedCredential> for WasmIssuedCredential {
|
||||||
|
fn from(value: StoredIssuedCredential) -> Self {
|
||||||
|
WasmIssuedCredential {
|
||||||
|
serialization_revision: value.serialization_revision,
|
||||||
|
credential_data: value.credential_data.clone(),
|
||||||
|
credential_type: value.credential_type.clone(),
|
||||||
|
epoch_id: value.epoch_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use js_sys::Promise;
|
||||||
|
use nym_bandwidth_controller::error::BandwidthControllerError;
|
||||||
|
use nym_network_defaults::UrlParseError;
|
||||||
|
use nym_validator_client::nyxd::error::NyxdError;
|
||||||
|
use thiserror::Error;
|
||||||
|
use wasm_utils::wasm_error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum WasmCredentialClientError {
|
||||||
|
#[error(transparent)]
|
||||||
|
BandwidthControllerError {
|
||||||
|
#[from]
|
||||||
|
source: BandwidthControllerError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("the passed credential value had a value of zero")]
|
||||||
|
ZeroCoinValue,
|
||||||
|
|
||||||
|
#[error("failed to use credential storage: {source}")]
|
||||||
|
StorageError {
|
||||||
|
#[from]
|
||||||
|
source: nym_credential_storage::error::StorageError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error(transparent)]
|
||||||
|
NyxdFailure {
|
||||||
|
#[from]
|
||||||
|
source: NyxdError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[error("no nyxd endpoints have been provided - we can't interact with the chain")]
|
||||||
|
NoNyxdEndpoints,
|
||||||
|
|
||||||
|
#[error("the provided nyxd endpoint is malformed: {source}")]
|
||||||
|
MalformedNyxdEndpoint {
|
||||||
|
#[from]
|
||||||
|
source: UrlParseError,
|
||||||
|
},
|
||||||
|
|
||||||
|
// #[error("The provided deposit value was malformed: {source}")]
|
||||||
|
// MalformedCoin { source: serde_wasm_bindgen::Error },
|
||||||
|
#[error("The provided deposit value was malformed: {source}")]
|
||||||
|
// annoyingly cosmwasm hasn't exposed CoinFromStrError directly
|
||||||
|
// so we have to rely on the dynamic dispatch here
|
||||||
|
MalformedCoin { source: Box<dyn std::error::Error> },
|
||||||
|
|
||||||
|
// #[error("Coin parse error")]
|
||||||
|
// CoinParseError,
|
||||||
|
// #[error("State error")]
|
||||||
|
// StateError,
|
||||||
|
#[error("The provided mnemonic was malformed: {source}")]
|
||||||
|
MalformedMnemonic {
|
||||||
|
#[from]
|
||||||
|
source: bip39::Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_error!(WasmCredentialClientError);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::error::WasmCredentialClientError;
|
||||||
|
use nym_network_defaults::{NymContracts, NymNetworkDetails, ValidatorDetails};
|
||||||
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
|
pub(crate) fn parse_mnemonic(raw: String) -> Result<bip39::Mnemonic, WasmCredentialClientError> {
|
||||||
|
// make sure that whatever happens, the raw value gets zeroized
|
||||||
|
let wrapped = Zeroizing::new(raw);
|
||||||
|
Ok(bip39::Mnemonic::parse(&*wrapped)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn minimal_coconut_sandbox() -> NymNetworkDetails {
|
||||||
|
// we can piggyback on mainnet defaults for certain things,
|
||||||
|
// since sandbox uses the same network name, denoms, etc.
|
||||||
|
let default_mainnet = NymNetworkDetails::new_mainnet();
|
||||||
|
|
||||||
|
NymNetworkDetails {
|
||||||
|
network_name: default_mainnet.network_name,
|
||||||
|
chain_details: default_mainnet.chain_details,
|
||||||
|
endpoints: vec![ValidatorDetails::new(
|
||||||
|
"https://sandbox-validator1.nymtech.net",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)],
|
||||||
|
contracts: NymContracts {
|
||||||
|
coconut_bandwidth_contract_address: Some(
|
||||||
|
"n16a32stm6kknhq5cc8rx77elr66pygf2hfszw7wvpq746x3uffylqkjar4l".into(),
|
||||||
|
),
|
||||||
|
coconut_dkg_contract_address: Some(
|
||||||
|
"n1ahg0erc2fs6xx3j5m8sfx3ryuzdjh6kf6qm9plsf865fltekyrfsesac6a".into(),
|
||||||
|
),
|
||||||
|
// we don't need other contracts for getting credential
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
explorer_api: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod credential;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod error;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod helpers;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
mod opts;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(start)]
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
pub fn main() {
|
||||||
|
wasm_utils::console_log!("[rust main]: rust module loaded");
|
||||||
|
wasm_utils::console_log!(
|
||||||
|
"credential client version used: {:#?}",
|
||||||
|
nym_bin_common::bin_info!()
|
||||||
|
);
|
||||||
|
wasm_utils::console_log!("[rust main]: setting panic hook");
|
||||||
|
wasm_utils::set_panic_hook();
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use nym_network_defaults::NymNetworkDetails;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tsify::Tsify;
|
||||||
|
|
||||||
|
#[derive(Tsify, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CredentialClientOpts {
|
||||||
|
#[tsify(optional)]
|
||||||
|
pub network_details: Option<NymNetworkDetails>,
|
||||||
|
|
||||||
|
#[tsify(optional)]
|
||||||
|
pub use_sandbox: Option<bool>,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user