Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 954d2e7176 | |||
| 6ee8b655e4 | |||
| 0a1f861448 | |||
| d9f09e3b91 | |||
| d04307d074 | |||
| 1a4345dd78 |
+3
-1
@@ -48,4 +48,6 @@ foxyfox.env
|
||||
|
||||
.next
|
||||
ppa-private-key.b64
|
||||
ppa-private-key.asc
|
||||
ppa-private-key.asc
|
||||
|
||||
yarn-error.log
|
||||
Generated
+32
-2
@@ -4856,6 +4856,9 @@ dependencies = [
|
||||
"strum 0.25.0",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
"wasm-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5407,7 +5410,9 @@ dependencies = [
|
||||
"log",
|
||||
"schemars",
|
||||
"serde",
|
||||
"tsify",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10651,8 +10656,8 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zknym-lib"
|
||||
version = "0.1.0"
|
||||
name = "zk-nym-faucet-lib"
|
||||
version = "1.3.0-rc.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -10677,3 +10682,28 @@ dependencies = [
|
||||
"wasmtimer",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zk-nym-lib"
|
||||
version = "1.3.0-rc.0"
|
||||
dependencies = [
|
||||
"bip39",
|
||||
"js-sys",
|
||||
"nym-bandwidth-controller",
|
||||
"nym-bin-common",
|
||||
"nym-credential-storage",
|
||||
"nym-credential-utils",
|
||||
"nym-credentials",
|
||||
"nym-credentials-interface",
|
||||
"nym-network-defaults",
|
||||
"nym-validator-client",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"thiserror",
|
||||
"tsify",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-bindgen-test",
|
||||
"wasm-utils",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
+2
-1
@@ -125,7 +125,8 @@ members = [
|
||||
# "wasm/full-nym-wasm", # If we uncomment this again, remember to also uncomment the profile settings below
|
||||
"wasm/mix-fetch",
|
||||
"wasm/node-tester",
|
||||
"wasm/zknym-lib",
|
||||
"wasm/zk-nym-lib",
|
||||
"wasm/zk-nym-faucet-lib",
|
||||
"tools/internal/testnet-manager",
|
||||
"tools/internal/testnet-manager/dkg-bypass-contract",
|
||||
]
|
||||
|
||||
@@ -104,7 +104,8 @@ sdk-wasm-build:
|
||||
$(MAKE) -C wasm/client
|
||||
$(MAKE) -C wasm/node-tester
|
||||
$(MAKE) -C wasm/mix-fetch
|
||||
$(MAKE) -C wasm/zknym-lib
|
||||
$(MAKE) -C wasm/zk-nym-lib
|
||||
$(MAKE) -C wasm/zk-nym-faucet-lib
|
||||
#$(MAKE) -C wasm/full-nym-wasm
|
||||
|
||||
# run this from npm/yarn to ensure tools are in the path, e.g. yarn build:sdk from root of repo
|
||||
@@ -115,7 +116,7 @@ sdk-typescript-build:
|
||||
yarn --cwd sdk/typescript/codegen/contract-clients build
|
||||
|
||||
# NOTE: These targets are part of the main workspace (but not as wasm32-unknown-unknown)
|
||||
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zknym-lib
|
||||
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm zk-nym-lib
|
||||
|
||||
sdk-wasm-test:
|
||||
#cargo test $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::{
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
use nym_metrics::{inc, inc_by};
|
||||
use si_scale::helpers::bibytes2;
|
||||
|
||||
@@ -72,6 +73,14 @@ struct PacketStatistics {
|
||||
}
|
||||
|
||||
impl PacketStatistics {
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
_event: crate::client::packet_statistics_control::PacketStatisticsEvent,
|
||||
) {
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||
fn handle_event(&mut self, event: PacketStatisticsEvent) {
|
||||
match event {
|
||||
PacketStatisticsEvent::RealPacketSent(packet_size) => {
|
||||
@@ -330,6 +339,7 @@ impl PacketRates {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables, dead_code)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PacketStatisticsEvent {
|
||||
// The real packets sent. Recall that acks are sent by the gateway, so it's not included here.
|
||||
|
||||
@@ -50,7 +50,7 @@ pub enum NyxdError {
|
||||
#[error("{0} is not a valid tx hash")]
|
||||
InvalidTxHash(String),
|
||||
|
||||
#[error("Tendermint RPC request failed - {0}")]
|
||||
#[error("Tendermint RPC request failed: {0}")]
|
||||
TendermintErrorRpc(#[from] TendermintRpcError),
|
||||
|
||||
#[error("tendermint library failure: {0}")]
|
||||
@@ -62,22 +62,22 @@ pub enum NyxdError {
|
||||
#[error("Failed when attempting to deserialize data ({0})")]
|
||||
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),
|
||||
|
||||
#[error("Failed to decode our protobuf data - {0}")]
|
||||
#[error("Failed to decode our protobuf data: {0}")]
|
||||
ProtobufDecodingError(#[from] prost::DecodeError),
|
||||
|
||||
#[error("Account {0} does not exist on the chain")]
|
||||
#[error("Account '{0}' does not exist on the chain")]
|
||||
NonExistentAccountError(AccountId),
|
||||
|
||||
#[error("Failed on json serialization/deserialization - {0}")]
|
||||
#[error("Failed on json serialization/deserialization: {0}")]
|
||||
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),
|
||||
|
||||
#[error("Account {0} has an invalid associated public key")]
|
||||
#[error("Account '{0}' has an invalid associated public key")]
|
||||
InvalidPublicKey(AccountId),
|
||||
|
||||
#[error("Queried contract (code_id: {0}) did not have any code information attached")]
|
||||
@@ -92,7 +92,7 @@ pub enum NyxdError {
|
||||
#[error("Block has an invalid height (either negative or larger than i64::MAX")]
|
||||
InvalidHeight,
|
||||
|
||||
#[error("Failed to compress provided wasm code - {0}")]
|
||||
#[error("Failed to compress provided wasm code: {0}")]
|
||||
WasmCompressionError(io::Error),
|
||||
|
||||
#[error("Logs returned from the validator were malformed")]
|
||||
|
||||
@@ -109,7 +109,8 @@ trait TendermintRpcErrorMap {
|
||||
|
||||
impl TendermintRpcErrorMap for reqwest::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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ license.workspace = true
|
||||
[dependencies]
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
time.workspace = true
|
||||
|
||||
nym-bandwidth-controller = { path = "../../common/bandwidth-controller" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credentials-interface = { path = "../../common/credentials-interface" }
|
||||
nym-credential-storage = { path = "../../common/credential-storage", features = ["persistent-storage"] }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client" }
|
||||
nym-validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
nym-config = { path = "../../common/config" }
|
||||
nym-client-core = { path = "../../common/client-core" }
|
||||
nym-ecash-time = { path = "../../common/ecash-time" }
|
||||
nym-client-core = { path = "../../common/client-core", features = ["wasm"] }
|
||||
nym-ecash-time = { path = "../../common/ecash-time" }
|
||||
|
||||
@@ -6,8 +6,11 @@ use log::*;
|
||||
use nym_bandwidth_controller::acquire::{
|
||||
get_ticket_book, query_and_persist_required_global_signatures,
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_config::DEFAULT_DATA_DIR;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials_interface::TicketType;
|
||||
@@ -16,6 +19,7 @@ use nym_validator_client::coconut::all_ecash_api_clients;
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
dkg_query_client::EpochState, DkgQueryClient, EcashQueryClient, EcashSigningClient,
|
||||
};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
@@ -80,6 +84,7 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn setup_persistent_storage(client_home_directory: PathBuf) -> PersistentStorage {
|
||||
let data_dir = client_home_directory.join(DEFAULT_DATA_DIR);
|
||||
let paths = CommonClientPaths::new_base(data_dir);
|
||||
|
||||
@@ -18,6 +18,15 @@ strum = { workspace = true, features = ["derive"] }
|
||||
time = { workspace = true, features = ["serde"] }
|
||||
rand = { workspace = true }
|
||||
|
||||
# 'wasm-serde-types' feature
|
||||
wasm-utils = { path = "../wasm/utils", default-features = false, optional = true }
|
||||
tsify = { workspace = true, features = ["js"], optional = true }
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
nym-compact-ecash = { path = "../nym_offline_compact_ecash" }
|
||||
nym-ecash-time = { path = "../ecash-time" }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
wasm-serde-types = ["tsify", "wasm-bindgen", "wasm-utils"]
|
||||
|
||||
@@ -30,6 +30,12 @@ pub use nym_compact_ecash::{
|
||||
};
|
||||
use nym_ecash_time::{ecash_today, EcashTime};
|
||||
|
||||
#[cfg(feature = "wasm-serde-types")]
|
||||
use tsify::Tsify;
|
||||
|
||||
#[cfg(feature = "wasm-serde-types")]
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CredentialSigningData {
|
||||
pub withdrawal_request: WithdrawalRequest,
|
||||
@@ -233,6 +239,8 @@ impl From<PayInfo> for NymPayInfo {
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
#[cfg_attr(feature = "wasm-serde-types", derive(Tsify))]
|
||||
#[cfg_attr(feature = "wasm-serde-types", tsify(into_wasm_abi, from_wasm_abi))]
|
||||
pub enum TicketType {
|
||||
#[default]
|
||||
V1MixnetEntry,
|
||||
|
||||
@@ -14,10 +14,12 @@ schemars = { workspace = true, features = ["preserve_order"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"], optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
|
||||
# please be extremely careful when adding new dependencies because this crate is imported by the ecash contract,
|
||||
# so if anything new is added, consider feature-locking it and then just adding it to default feature
|
||||
# 'wasm-serde-types' feature
|
||||
tsify = { workspace = true, features = ["js"], optional = true }
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
wasm-serde-types = ["tsify", "wasm-bindgen"]
|
||||
default = ["env", "network"]
|
||||
env = ["dotenvy", "log"]
|
||||
network = ["schemars", "serde", "url"]
|
||||
@@ -44,4 +44,4 @@ wasm-storage = { path = "../storage" }
|
||||
console_error_panic_hook = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
default = ["console_error_panic_hook"]
|
||||
default = ["wasm-utils/console_error_panic_hook"]
|
||||
@@ -17,6 +17,10 @@ gloo-utils = { workspace = true }
|
||||
gloo-net = { workspace = true, 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
|
||||
|
||||
+1
-1
@@ -54,4 +54,4 @@
|
||||
"node-gyp": "^9.3.1",
|
||||
"tslog": "3.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,11 @@ where
|
||||
St: Storage,
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
pub fn new(
|
||||
network_details: NymNetworkDetails,
|
||||
mnemonic: String,
|
||||
storage: &'a St,
|
||||
client_id: String,
|
||||
client_id_private_key_base58: String,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<Self> {
|
||||
let nyxd_url = network_details.endpoints[0].nyxd_url.as_str();
|
||||
@@ -44,7 +44,7 @@ where
|
||||
Ok(Self {
|
||||
client,
|
||||
storage,
|
||||
client_id: client_id.into(),
|
||||
client_id: client_id_private_key_base58.into(),
|
||||
ticketbook_type,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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-sdk-zk-nyms-example-parcel",
|
||||
"version": "1.3.0-rc.0",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"build": "parcel build --no-cache --no-content-hash",
|
||||
"serve": "serve dist",
|
||||
"start": "parcel --no-cache"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/sdk": "1.3.0-rc.0",
|
||||
"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,4 @@
|
||||
src/mixnet/wasm/worker.js
|
||||
src/zk-nym/worker.js
|
||||
src/zk-nym-faucet/worker.js
|
||||
docs/
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
"scripts": {
|
||||
"build": "scripts/build-prod.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",
|
||||
"clean": "rimraf dist",
|
||||
"docs:dev": "run-p docs:watch docs:serve ",
|
||||
@@ -32,6 +33,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": ">=1.2.4-rc.2 || ^1",
|
||||
"@nymproject/zk-nym-faucet-lib": ">=1.3.0-rc.0 || ^1",
|
||||
"@nymproject/zk-nym-lib": ">=1.3.0-rc.0 || ^1",
|
||||
"comlink": "^4.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,6 +2,6 @@ import { getConfig } from './rollup/esm.mjs';
|
||||
|
||||
export default {
|
||||
...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 resolve from '@rollup/plugin-node-resolve';
|
||||
import { wasm } from '@rollup/plugin-wasm';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
|
||||
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
|
||||
import { getConfig } from './rollup/worker.mjs';
|
||||
|
||||
export default {
|
||||
input: 'src/mixnet/wasm/worker.ts',
|
||||
output: {
|
||||
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' } }),
|
||||
],
|
||||
...getConfig('src/mixnet/wasm/worker.ts', 'nym_client_wasm_bg.wasm', {
|
||||
inlineWasm: process.env.SDK_DEV_MODE === 'true',
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { getConfig } from './rollup/worker.mjs';
|
||||
|
||||
export default {
|
||||
...getConfig('src/zk-nym-faucet/worker.ts', 'zk_nym_faucet_lib_bg.wasm'),
|
||||
inlineWasm: true,
|
||||
format: 'cjs',
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { getConfig } from './rollup/worker.mjs';
|
||||
|
||||
export default {
|
||||
...getConfig('src/zk-nym-faucet/worker.ts', 'zk_nym_faucet_lib_bg.wasm', {
|
||||
inlineWasm: process.env.SDK_DEV_MODE === 'true',
|
||||
}),
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
import { getConfig } from './rollup/worker.mjs';
|
||||
|
||||
export default {
|
||||
...getConfig('src/zk-nym/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/zk-nym/worker.ts', 'nym_credential_client_wasm_bg.wasm', {
|
||||
inlineWasm: process.env.SDK_DEV_MODE === 'true',
|
||||
}),
|
||||
};
|
||||
@@ -21,7 +21,7 @@ export const getConfig = (opts) => ({
|
||||
webWorkerLoader({ targetPlatform: 'browser', inline: opts.inline }), // the inline param is used here
|
||||
resolve({ extensions }),
|
||||
typescript({
|
||||
exclude: ['mixnet/wasm/worker.ts'],
|
||||
exclude: ['mixnet/wasm/worker.ts', 'zk-nym/worker.ts'],
|
||||
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
|
||||
|
||||
# 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 (zk-nym 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-zk-nym-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/zk-nym/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
|
||||
mv dist/worker.js src/mixnet/wasm/worker.js
|
||||
|
||||
#-------------------------------------------------------
|
||||
# WEB WORKER (zk-nym 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-zk-nym-worker.config.mjs
|
||||
|
||||
# move it next to the Typescript `src/index.ts` so it can be inlined by rollup
|
||||
cp dist/worker.js src/zk-nym/worker.js || true
|
||||
rm dist/worker.js || true
|
||||
|
||||
#-------------------------------------------------------
|
||||
# ESM
|
||||
#-------------------------------------------------------
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @internal
|
||||
*/
|
||||
export const notImplementedYet = () => console.log('Not implement, coming soon...');
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './coconut';
|
||||
export * from './zk-nym';
|
||||
export * from './mixnet';
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as Comlink from 'comlink';
|
||||
import InlineWasmWebWorker from 'web-worker:./worker';
|
||||
import { EventKinds, INymZkNymFaucetClientWebWorker, NymZkNymFaucetClient } from './types';
|
||||
|
||||
export const createNymCredentialsClient = async (): Promise<NymZkNymFaucetClient> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
const worker = await createWorker();
|
||||
|
||||
// let comlink handle interop with the web worker
|
||||
const client = Comlink.wrap<INymZkNymFaucetClientWebWorker>(worker);
|
||||
|
||||
return { client };
|
||||
};
|
||||
|
||||
/**
|
||||
* Async method to create a web worker that runs the Nym credentials client on another thread. It will only return once the worker
|
||||
* has passed back a `Loaded` event to the calling thread.
|
||||
*
|
||||
* @return The instance of the web worker.
|
||||
*/
|
||||
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,29 @@
|
||||
/**
|
||||
* Enum representing various event kinds.
|
||||
* @enum
|
||||
*/
|
||||
export enum EventKinds {
|
||||
Loaded = 'Loaded',
|
||||
}
|
||||
|
||||
export interface LoadedEvent {
|
||||
kind: EventKinds.Loaded;
|
||||
args: {
|
||||
loaded: true;
|
||||
};
|
||||
}
|
||||
|
||||
export type ZkNym = any; // TODO
|
||||
|
||||
export interface ZkNymFaucetClientOpts {
|
||||
useSandbox?: boolean;
|
||||
networkDetails?: {};
|
||||
}
|
||||
|
||||
export interface INymZkNymFaucetClientWebWorker {
|
||||
acquireCredential: (faucetApiUrl: string, authToken: string) => Promise<ZkNym>;
|
||||
}
|
||||
|
||||
export interface NymZkNymFaucetClient {
|
||||
client: INymZkNymFaucetClientWebWorker;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/* 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/zk_nym_faucet_lib_bg.wasm';
|
||||
import init, { NymIssuanceBandwidthVoucher } from '@nymproject/zk-nym-faucet-lib/zk_nym_faucet_lib';
|
||||
import type { INymZkNymFaucetClientWebWorker, ZkNymFaucetClientOpts, 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 zk-nym faucets] Starting Nym WASM web worker...');
|
||||
|
||||
// load WASM binary
|
||||
async function main() {
|
||||
// rollup with provide a function to get the 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: INymZkNymFaucetClientWebWorker = {
|
||||
async acquireCredential(faucetApiUrl: string, authToken: string) {
|
||||
console.log('getting opts');
|
||||
const res = await fetch(`${faucetApiUrl}/api/v1/bandwidth-voucher/prehashed-public-attributes`, {
|
||||
headers: new Headers({ Authorization: `Bearer ${authToken}` }),
|
||||
});
|
||||
const opts = await res.json();
|
||||
|
||||
const issuanceVoucher = new NymIssuanceBandwidthVoucher(opts);
|
||||
const blindSignRequest = issuanceVoucher.getBlindSignRequest();
|
||||
|
||||
console.log('getting partial vks');
|
||||
const partialVksRes = await fetch(`${faucetApiUrl}/api/v1/bandwidth-voucher/partial-verification-keys`, {
|
||||
headers: new Headers({ Authorization: `Bearer ${authToken}` }),
|
||||
});
|
||||
const partialVks = await partialVksRes.json();
|
||||
|
||||
console.log('getting master vk');
|
||||
const masterVkRes = await fetch(`${faucetApiUrl}/api/v1/bandwidth-voucher/master-verification-key`, {
|
||||
headers: new Headers({ Authorization: `Bearer ${authToken}` }),
|
||||
});
|
||||
const masterVk = await masterVkRes.json();
|
||||
|
||||
console.log('getting blinded shares');
|
||||
const sharesRes = await fetch(`${faucetApiUrl}/api/v1/bandwidth-voucher/obtain`, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
blindSignRequest,
|
||||
}),
|
||||
});
|
||||
|
||||
const credentialShares = await sharesRes.json();
|
||||
|
||||
console.log('unblinding shares');
|
||||
const bandwidthVoucher = issuanceVoucher.unblindShares(credentialShares, partialVks);
|
||||
console.log('is valid: ', bandwidthVoucher.ensureIsValid(masterVk.bs58EncodedKey));
|
||||
|
||||
const serialised = bandwidthVoucher.serialise();
|
||||
console.log('serialised:\n', serialised);
|
||||
},
|
||||
};
|
||||
|
||||
// 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 zk-nym faucet worker', e));
|
||||
@@ -0,0 +1,41 @@
|
||||
import * as Comlink from 'comlink';
|
||||
import InlineWasmWebWorker from 'web-worker:./worker';
|
||||
import { EventKinds, INymZkNymClientWebWorker, NymZkNymClient } from './types';
|
||||
|
||||
export const createNymCredentialsClient = async (): Promise<NymZkNymClient> => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
const worker = await createWorker();
|
||||
|
||||
// let comlink handle interop with the web worker
|
||||
const client = Comlink.wrap<INymZkNymClientWebWorker>(worker);
|
||||
|
||||
return { client };
|
||||
};
|
||||
|
||||
/**
|
||||
* Async method to create a web worker that runs the Nym credentials client on another thread. It will only return once the worker
|
||||
* has passed back a `Loaded` event to the calling thread.
|
||||
*
|
||||
* @return The instance of the web worker.
|
||||
*/
|
||||
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,29 @@
|
||||
/**
|
||||
* Enum representing various event kinds.
|
||||
* @enum
|
||||
*/
|
||||
export enum EventKinds {
|
||||
Loaded = 'Loaded',
|
||||
}
|
||||
|
||||
export interface LoadedEvent {
|
||||
kind: EventKinds.Loaded;
|
||||
args: {
|
||||
loaded: true;
|
||||
};
|
||||
}
|
||||
|
||||
export type ZkNym = any; // TODO
|
||||
|
||||
export interface ZkNymClientOpts {
|
||||
useSandbox?: boolean;
|
||||
networkDetails?: {};
|
||||
}
|
||||
|
||||
export interface INymZkNymClientWebWorker {
|
||||
acquireCredential: (coin: string, mnemonic: string, opts: ZkNymClientOpts) => Promise<ZkNym>;
|
||||
}
|
||||
|
||||
export interface NymZkNymClient {
|
||||
client: INymZkNymClientWebWorker;
|
||||
}
|
||||
@@ -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/zk-nym-lib/zk_nym_lib_bg.wasm';
|
||||
import init, { acquireCredential } from '@nymproject/zk-nym-lib/zk_nym_lib';
|
||||
import type { INymZkNymClientWebWorker, ZkNymClientOpts, 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 zk-nyms] Starting Nym WASM web worker...');
|
||||
|
||||
// load WASM binary
|
||||
async function main() {
|
||||
// rollup with provide a function to get the 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: INymZkNymClientWebWorker = {
|
||||
async acquireCredential(coin: string, mnemonic: string, opts: ZkNymClientOpts) {
|
||||
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 zk-nym worker', e));
|
||||
@@ -229,6 +229,7 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
|
||||
packages.register_cargo("wasm/node-tester");
|
||||
// packages.register_cargo("wasm/full-nym-wasm");
|
||||
packages.register_cargo("nym-browser-extension/storage");
|
||||
packages.register_cargo("wasm/zknym-lib");
|
||||
|
||||
// js packages that will have their package.json modified
|
||||
packages.register_json("nym-wallet");
|
||||
@@ -242,6 +243,7 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
|
||||
packages.register_json("sdk/typescript/examples/node-tester/parcel");
|
||||
packages.register_json("sdk/typescript/examples/node-tester/plain-html");
|
||||
packages.register_json("sdk/typescript/examples/node-tester/react");
|
||||
packages.register_json("sdk/typescript/examples/zk-nyms/browser");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch-node");
|
||||
packages.register_json("sdk/typescript/packages/mix-fetch/internal-dev");
|
||||
@@ -276,6 +278,8 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
|
||||
packages.register_known_js_dependency("@nymproject/ts-sdk-docs");
|
||||
packages.register_known_js_dependency("@nymproject/contract-clients");
|
||||
|
||||
packages.register_known_js_dependency("@nymproject/zknym-lib");
|
||||
|
||||
packages
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zknym-lib"
|
||||
version = "0.1.0"
|
||||
name = "zk-nym-faucet-lib"
|
||||
version = "1.3.0-rc.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
@@ -2,15 +2,15 @@ all: build build-node
|
||||
|
||||
|
||||
build:
|
||||
wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/zknym-lib
|
||||
wasm-opt -Oz -o ../../dist/wasm/zknym-lib/zknym_lib_bg.wasm ../../dist/wasm/zknym-lib/zknym_lib_bg.wasm
|
||||
wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/zk-nym-faucet-lib
|
||||
wasm-opt -Oz -o ../../dist/wasm/zk-nym-faucet-lib/zk_nym_faucet_lib_bg.wasm ../../dist/wasm/zk-nym-faucet-lib/zk_nym_faucet_lib_bg.wasm
|
||||
|
||||
build-debug-dev:
|
||||
wasm-pack build --scope nymproject --target no-modules
|
||||
|
||||
build-rust-node:
|
||||
wasm-pack build --scope nymproject --target nodejs --out-dir ../../dist/node/wasm/zknym-lib
|
||||
wasm-opt -Oz -o ../../dist/node/wasm/zknym-lib/zknym_lib_bg.wasm ../../dist/node/wasm/zknym-lib/zknym_lib_bg.wasm
|
||||
wasm-pack build --scope nymproject --target nodejs --out-dir ../../dist/node/wasm/zk-nym-faucet-lib
|
||||
wasm-opt -Oz -o ../../dist/node/wasm/zk-nym-faucet-lib/zk_nym_faucet_lib_bg.wasm ../../dist/node/wasm/zk-nym-faucet-lib/zk_nym_faucet_lib_bg.wasm
|
||||
|
||||
#build-package-json-node:
|
||||
# node build-node.mjs
|
||||
Vendored
+1
-1
@@ -35,6 +35,6 @@
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-zknym-lib": "file:../pkg"
|
||||
"@nymproject/nym-zknym-faucet-lib": "file:../pkg"
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -12,9 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
const RUST_WASM_URL = "zknym_lib_bg.wasm"
|
||||
const RUST_WASM_URL = "zk_nym_faucet_lib_bg.wasm"
|
||||
|
||||
importScripts('zknym_lib.js');
|
||||
importScripts('zk_nym_faucet_lib.js');
|
||||
|
||||
console.log('Initializing worker');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::vpn_api_client::NymVpnApiClientError;
|
||||
use crate::zk_nym_faucet_client::NymZkNymFaucetClientError;
|
||||
use thiserror::Error;
|
||||
use wasm_utils::wasm_error;
|
||||
|
||||
@@ -22,7 +22,7 @@ pub enum ZkNymError {
|
||||
#[error("failed to contact the vpn api")]
|
||||
HttpClientFailure {
|
||||
#[from]
|
||||
source: NymVpnApiClientError,
|
||||
source: NymZkNymFaucetClientError,
|
||||
},
|
||||
#[error("the provided shares and issuers are not from the same epoch! {shares} and {issuers}")]
|
||||
InconsistentEpochId { shares: u64, issuers: u64 },
|
||||
@@ -20,7 +20,7 @@ pub mod types;
|
||||
|
||||
// keep in internal to the crate since I'm not sure how temporary this thing is going to be
|
||||
// I mostly got it, so I could test the whole thing end to end
|
||||
pub(crate) mod vpn_api_client;
|
||||
pub(crate) mod zk_nym_faucet_client;
|
||||
|
||||
pub(crate) static GLOBAL_COCONUT_PARAMS: OnceLock<Parameters> = OnceLock::new();
|
||||
|
||||
@@ -29,7 +29,7 @@ pub(crate) static GLOBAL_COCONUT_PARAMS: OnceLock<Parameters> = OnceLock::new();
|
||||
pub fn main() {
|
||||
wasm_utils::console_log!("[rust main]: rust module loaded");
|
||||
wasm_utils::console_log!(
|
||||
"wasm zk-nym version used: {}",
|
||||
"wasm zk-nym faucet client version used: {}",
|
||||
nym_bin_common::bin_info_owned!()
|
||||
);
|
||||
}
|
||||
+14
-14
@@ -1,9 +1,9 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::NymVpnApiClientError;
|
||||
use super::NymZkNymFaucetClientError;
|
||||
use crate::error::ZkNymError;
|
||||
use crate::vpn_api_client::types::{
|
||||
use crate::zk_nym_faucet_client::types::{
|
||||
AttributesResponse, BandwidthVoucherRequest, BandwidthVoucherResponse,
|
||||
MasterVerificationKeyResponse, PartialVerificationKeysResponse,
|
||||
};
|
||||
@@ -15,7 +15,7 @@ use reqwest::IntoUrl;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct VpnApiClient {
|
||||
pub struct NymZkNymFaucetClientErrorApiClient {
|
||||
inner: Client,
|
||||
bearer_token: String,
|
||||
}
|
||||
@@ -24,8 +24,8 @@ pub struct VpnApiClient {
|
||||
pub fn new_client(
|
||||
base_url: impl IntoUrl,
|
||||
bearer_token: impl Into<String>,
|
||||
) -> Result<VpnApiClient, ZkNymError> {
|
||||
Ok(VpnApiClient {
|
||||
) -> Result<NymZkNymFaucetClientErrorApiClient, ZkNymError> {
|
||||
Ok(NymZkNymFaucetClientErrorApiClient {
|
||||
inner: Client::builder(base_url)?
|
||||
.with_user_agent(format!("nym-wasm-znym-lib/{}", env!("CARGO_PKG_VERSION")))
|
||||
.build()?,
|
||||
@@ -36,14 +36,14 @@ pub fn new_client(
|
||||
// TODO: do it properly by implementing auth headers on `ApiClient` trait
|
||||
#[allow(dead_code)]
|
||||
#[async_trait(?Send)]
|
||||
pub trait NymVpnApiClient {
|
||||
async fn simple_get<T>(&self, path: PathSegments<'_>) -> Result<T, NymVpnApiClientError>
|
||||
pub trait NymNymZkNymFaucetClientErrorApiClient {
|
||||
async fn simple_get<T>(&self, path: PathSegments<'_>) -> Result<T, NymZkNymFaucetClientError>
|
||||
where
|
||||
T: DeserializeOwned;
|
||||
|
||||
async fn get_prehashed_public_attributes(
|
||||
&self,
|
||||
) -> Result<AttributesResponse, NymVpnApiClientError> {
|
||||
) -> Result<AttributesResponse, NymZkNymFaucetClientError> {
|
||||
self.simple_get(&[
|
||||
"/api",
|
||||
"/v1",
|
||||
@@ -55,7 +55,7 @@ pub trait NymVpnApiClient {
|
||||
|
||||
async fn get_partial_verification_keys(
|
||||
&self,
|
||||
) -> Result<PartialVerificationKeysResponse, NymVpnApiClientError> {
|
||||
) -> Result<PartialVerificationKeysResponse, NymZkNymFaucetClientError> {
|
||||
self.simple_get(&[
|
||||
"/api",
|
||||
"/v1",
|
||||
@@ -67,7 +67,7 @@ pub trait NymVpnApiClient {
|
||||
|
||||
async fn get_master_verification_key(
|
||||
&self,
|
||||
) -> Result<MasterVerificationKeyResponse, NymVpnApiClientError> {
|
||||
) -> Result<MasterVerificationKeyResponse, NymZkNymFaucetClientError> {
|
||||
self.simple_get(&[
|
||||
"/api",
|
||||
"/v1",
|
||||
@@ -80,12 +80,12 @@ pub trait NymVpnApiClient {
|
||||
async fn get_bandwidth_voucher_blinded_shares(
|
||||
&self,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
) -> Result<BandwidthVoucherResponse, NymVpnApiClientError>;
|
||||
) -> Result<BandwidthVoucherResponse, NymZkNymFaucetClientError>;
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl NymVpnApiClient for VpnApiClient {
|
||||
async fn simple_get<T>(&self, path: PathSegments<'_>) -> Result<T, NymVpnApiClientError>
|
||||
impl NymNymZkNymFaucetClientErrorApiClient for NymZkNymFaucetClientErrorApiClient {
|
||||
async fn simple_get<T>(&self, path: PathSegments<'_>) -> Result<T, NymZkNymFaucetClientError>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
@@ -112,7 +112,7 @@ impl NymVpnApiClient for VpnApiClient {
|
||||
async fn get_bandwidth_voucher_blinded_shares(
|
||||
&self,
|
||||
blind_sign_request: BlindSignRequest,
|
||||
) -> Result<BandwidthVoucherResponse, NymVpnApiClientError> {
|
||||
) -> Result<BandwidthVoucherResponse, NymZkNymFaucetClientError> {
|
||||
let req = self.inner.create_post_request(
|
||||
&["/api", "/v1", "/bandwidth-voucher", "/obtain"],
|
||||
NO_PARAMS,
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::vpn_api_client::types::ErrorResponse;
|
||||
use crate::zk_nym_faucet_client::types::ErrorResponse;
|
||||
use nym_http_api_client::HttpClientError;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -9,4 +9,4 @@ pub(crate) mod client;
|
||||
|
||||
pub mod types;
|
||||
|
||||
pub type NymVpnApiClientError = HttpClientError<ErrorResponse>;
|
||||
pub type NymZkNymFaucetClientError = HttpClientError<ErrorResponse>;
|
||||
@@ -0,0 +1,42 @@
|
||||
[package]
|
||||
name = "zk-nym-lib"
|
||||
version = "1.3.0-rc.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bip39 = { workspace = true }
|
||||
#futures = { workspace = true }
|
||||
js-sys = { workspace = true }
|
||||
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" }
|
||||
|
||||
nym-bin-common = { path = "../../common/bin-common" }
|
||||
nym-credentials = { path = "../../common/credentials" }
|
||||
nym-credential-utils = { path = "../../common/credential-utils" }
|
||||
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", features = ["wasm-serde-types"]}
|
||||
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,10 @@
|
||||
build:
|
||||
wasm-pack build --scope nymproject --target web --out-dir ../../dist/wasm/zk-nym-lib
|
||||
wasm-opt -Oz -o ../../dist/wasm/zk-nym-lib/zk_nym_lib_bg.wasm ../../dist/wasm/zk-nym-lib/zk_nym_lib_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
|
||||
|
||||
clippy:
|
||||
cargo clippy --target wasm32-unknown-unknown -- -Dwarnings
|
||||
@@ -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/zk-nym-lib": "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_zk_nym_lib_bg.wasm"
|
||||
|
||||
importScripts('nym_zk_nym_lib.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
@@ -0,0 +1,65 @@
|
||||
// Copyright 2022-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::WasmCredentialClientError;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credential_utils::utils::issue_credential;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_validator_client::{nyxd, DirectSigningReqwestRpcNyxdClient};
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
/// Represents a client that can be used to acquire bandwidth. You typically create one when you
|
||||
/// want to connect to the mixnet using paid coconut bandwidth credentials.
|
||||
/// The way to create this client is by calling
|
||||
/// [`crate::mixnet::DisconnectedMixnetClient::create_bandwidth_client`] on the associated mixnet
|
||||
/// client.
|
||||
pub struct BandwidthAcquireClient<'a, St: Storage> {
|
||||
client: DirectSigningReqwestRpcNyxdClient,
|
||||
storage: &'a St,
|
||||
client_id: Zeroizing<String>,
|
||||
ticketbook_type: TicketType,
|
||||
}
|
||||
|
||||
impl<'a, St> BandwidthAcquireClient<'a, St>
|
||||
where
|
||||
St: Storage,
|
||||
<St as Storage>::StorageError: Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(
|
||||
network_details: NymNetworkDetails,
|
||||
mnemonic: String,
|
||||
storage: &'a St,
|
||||
client_id_private_key_base58: String,
|
||||
ticketbook_type: TicketType,
|
||||
) -> Result<Self, WasmCredentialClientError> {
|
||||
let nyxd_url = network_details.endpoints[0].nyxd_url.as_str();
|
||||
let config = nyxd::Config::try_from_nym_network_details(&network_details)?;
|
||||
|
||||
let client = DirectSigningReqwestRpcNyxdClient::connect_reqwest_with_mnemonic(
|
||||
config,
|
||||
nyxd_url.parse().expect("TODO: MAKE SURE YOU HANDLE IT"),
|
||||
mnemonic.parse()?,
|
||||
);
|
||||
Ok(Self {
|
||||
client,
|
||||
storage,
|
||||
client_id: client_id_private_key_base58.into(),
|
||||
ticketbook_type,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn acquire(&self) -> Result<(), WasmCredentialClientError> {
|
||||
if let Err(err) = issue_credential(
|
||||
&self.client,
|
||||
self.storage,
|
||||
self.client_id.as_bytes(),
|
||||
self.ticketbook_type,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("unhandled error: {err}")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::bandwidth::BandwidthAcquireClient;
|
||||
use crate::error::WasmCredentialClientError;
|
||||
use crate::opts::CredentialClientOpts;
|
||||
use js_sys::Promise;
|
||||
use nym_credential_storage::ephemeral_storage::EphemeralCredentialStorage;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::ecash::bandwidth::serialiser::VersionedSerialise;
|
||||
use nym_credentials_interface::TicketType;
|
||||
use nym_network_defaults::NymNetworkDetails;
|
||||
use nym_validator_client::nyxd::CosmWasmCoin;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
use wasm_utils::error::PromisableResult;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
#[wasm_bindgen(js_name = acquireCredential)]
|
||||
pub fn acquire_credential(
|
||||
mnemonic: String,
|
||||
amount: String,
|
||||
client_id_private_key_base58: String,
|
||||
ticketbook_type: TicketType,
|
||||
opts: CredentialClientOpts,
|
||||
) -> Promise {
|
||||
future_to_promise(async move {
|
||||
acquire_credential_async(
|
||||
mnemonic,
|
||||
amount,
|
||||
client_id_private_key_base58,
|
||||
ticketbook_type,
|
||||
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,
|
||||
client_id_private_key_base58: String,
|
||||
ticketbook_type: TicketType,
|
||||
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 ephemeral_storage = EphemeralCredentialStorage::default();
|
||||
|
||||
let client = BandwidthAcquireClient::new(
|
||||
network,
|
||||
mnemonic,
|
||||
&ephemeral_storage,
|
||||
client_id_private_key_base58,
|
||||
ticketbook_type,
|
||||
)?;
|
||||
|
||||
client.acquire().await?;
|
||||
|
||||
// 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?;
|
||||
//
|
||||
|
||||
match ephemeral_storage
|
||||
.get_next_unspent_usable_ticketbook(1u32)
|
||||
.await?
|
||||
{
|
||||
Some(ticket_book) => {
|
||||
let serialized = ticket_book.ticketbook.pack();
|
||||
|
||||
Ok(WasmIssuedCredential {
|
||||
serialization_revision: serialized.revision,
|
||||
credential_data: serialized.data,
|
||||
ticketbook_type: format!("{}", ticketbook_type),
|
||||
})
|
||||
}
|
||||
None => Err(WasmCredentialClientError::TicketbookCredentialStoreIsNone),
|
||||
}
|
||||
}
|
||||
|
||||
#[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 ticketbook_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(),
|
||||
// ticketbook_type: value.ticketbook_type.clone(),
|
||||
// // epoch_id: value.epoch_id,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_bandwidth_controller::error::BandwidthControllerError;
|
||||
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,
|
||||
},
|
||||
|
||||
#[error("The ticket book cannot be retrieved from the credential store")]
|
||||
TicketbookCredentialStoreIsNone,
|
||||
}
|
||||
|
||||
wasm_error!(WasmCredentialClientError);
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_network_defaults::{NymContracts, NymNetworkDetails, ValidatorDetails};
|
||||
|
||||
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,
|
||||
nym_vpn_api_url: None,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod bandwidth;
|
||||
#[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