Merge remote-tracking branch 'origin/develop' into feature/nym-node-api-tests

This commit is contained in:
benedettadavico
2023-11-09 11:22:22 +01:00
69 changed files with 624 additions and 1646 deletions
-1
View File
@@ -3,7 +3,6 @@ name: cd-docs
on:
workflow_dispatch:
push:
branches: master
paths:
- 'documentation/docs/**'
+14
View File
@@ -4,6 +4,18 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
## [Unreleased]
## [2023.4-galaxy] (2023-11-07)
- DRY up client cli ([#4077])
- [mixnode] replace rocket with axum ([#4071])
- incorporate the nym node HTTP api into the mixnode ([#4070])
- replaced '--disable-sign-ext' with '--signext-lowering' when running wasm-opt ([#3896])
[#4077]: https://github.com/nymtech/nym/pull/4077
[#4071]: https://github.com/nymtech/nym/pull/4071
[#4070]: https://github.com/nymtech/nym/issues/4070
[#3896]: https://github.com/nymtech/nym/pull/3896
## [2023.3-kinder] (2023-10-31)
- suppress error output ([#4056])
@@ -15,6 +27,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
- use saturating sub in case outfox is not enabled ([#3986])
- Fix sorting for mixnodes and gateways ([#3985])
- Gateway client registry and api routes ([#3955])
- Feature/configurable socks5 bind address ([#3992])
[#4056]: https://github.com/nymtech/nym/pull/4056
[#4042]: https://github.com/nymtech/nym/pull/4042
@@ -25,6 +38,7 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
[#3986]: https://github.com/nymtech/nym/pull/3986
[#3985]: https://github.com/nymtech/nym/pull/3985
[#3955]: https://github.com/nymtech/nym/pull/3955
[#3992]: https://github.com/nymtech/nym/pull/3992
## [2023.1-milka] (2023-09-24)
Generated
+7 -14
View File
@@ -2994,7 +2994,7 @@ dependencies = [
[[package]]
name = "extension-storage"
version = "1.2.1"
version = "1.2.4-rc.1"
dependencies = [
"bip39",
"console_error_panic_hook",
@@ -5559,12 +5559,13 @@ dependencies = [
[[package]]
name = "mix-fetch-wasm"
version = "1.2.1"
version = "1.2.4-rc.1"
dependencies = [
"async-trait",
"futures",
"http-api-client",
"js-sys",
"nym-bin-common",
"nym-ordered-buffer",
"nym-service-providers-common",
"nym-socks5-requests",
@@ -6260,11 +6261,12 @@ dependencies = [
[[package]]
name = "nym-client-wasm"
version = "1.2.1"
version = "1.2.4-rc.1"
dependencies = [
"anyhow",
"futures",
"js-sys",
"nym-bin-common",
"nym-node-tester-utils",
"nym-node-tester-wasm",
"rand 0.7.3",
@@ -6703,7 +6705,7 @@ dependencies = [
[[package]]
name = "nym-mixnode"
version = "1.1.32"
version = "1.1.33"
dependencies = [
"anyhow",
"axum",
@@ -6958,7 +6960,7 @@ dependencies = [
[[package]]
name = "nym-node-tester-wasm"
version = "1.2.1"
version = "1.2.4-rc.1"
dependencies = [
"futures",
"js-sys",
@@ -7558,15 +7560,6 @@ dependencies = [
"ts-rs",
]
[[package]]
name = "nym-wasm-sdk"
version = "1.2.1"
dependencies = [
"mix-fetch-wasm",
"nym-client-wasm",
"nym-node-tester-wasm",
]
[[package]]
name = "nym-wireguard"
version = "0.1.0"
+1 -1
View File
@@ -105,7 +105,7 @@ members = [
"tools/nym-nr-query",
"tools/ts-rs-cli",
"wasm/client",
"wasm/full-nym-wasm",
# "wasm/full-nym-wasm",
"wasm/mix-fetch",
"wasm/node-tester",
]
+2 -2
View File
@@ -104,7 +104,7 @@ sdk-wasm-build:
$(MAKE) -C wasm/client
$(MAKE) -C wasm/node-tester
$(MAKE) -C wasm/mix-fetch
$(MAKE) -C wasm/full-nym-wasm
#$(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
sdk-typescript-build:
@@ -114,7 +114,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 nym-wasm-sdk
WASM_CRATES = extension-storage nym-client-wasm nym-node-tester-wasm
sdk-wasm-test:
#cargo test $(addprefix -p , $(WASM_CRATES)) --target wasm32-unknown-unknown -- -Dwarnings
+1 -1
View File
@@ -11,7 +11,7 @@ use thiserror::Error;
use tracing::warn;
use url::Url;
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(3);
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
pub type PathSegments<'a> = &'a [&'a str];
pub type Params<'a, K, V> = &'a [(K, V)];
+6 -1
View File
@@ -44,7 +44,12 @@ pub async fn start_wireguard(
//let routing_mode = tun_device::RoutingMode::new_nat();
// Start the tun device that is used to relay traffic outbound
let (tun, tun_task_tx, tun_task_response_rx) = tun_device::TunDevice::new(routing_mode);
let config = tun_device::TunDeviceConfig {
base_name: setup::TUN_BASE_NAME.to_string(),
ip: setup::TUN_DEVICE_ADDRESS.parse().unwrap(),
netmask: setup::TUN_DEVICE_NETMASK.parse().unwrap(),
};
let (tun, tun_task_tx, tun_task_response_rx) = tun_device::TunDevice::new(routing_mode, config);
tun.start();
// We also index peers by a tag
@@ -10,7 +10,6 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use crate::{
event::Event,
setup::{TUN_BASE_NAME, TUN_DEVICE_ADDRESS, TUN_DEVICE_NETMASK},
tun_task_channel::{
tun_task_channel, tun_task_response_channel, TunTaskPayload, TunTaskResponseRx,
TunTaskResponseTx, TunTaskRx, TunTaskTx,
@@ -20,11 +19,16 @@ use crate::{
fn setup_tokio_tun_device(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> tokio_tun::Tun {
log::info!("Creating TUN device with: address={address}, netmask={netmask}");
// Read MTU size from env variable NYM_MTU_SIZE, else default to 1420.
let mtu = std::env::var("NYM_MTU_SIZE")
.map(|mtu| mtu.parse().expect("NYM_MTU_SIZE must be a valid integer"))
.unwrap_or(1420);
log::info!("Using MTU size: {mtu}");
tokio_tun::Tun::builder()
.name(name)
.tap(false)
.packet_info(false)
.mtu(1350)
.mtu(mtu)
.up()
.address(address)
.netmask(netmask)
@@ -74,16 +78,25 @@ pub struct NatInner {
nat_table: HashMap<IpAddr, u64>,
}
pub struct TunDeviceConfig {
pub base_name: String,
pub ip: Ipv4Addr,
pub netmask: Ipv4Addr,
}
impl TunDevice {
pub fn new(
routing_mode: RoutingMode,
// peers_by_ip: Option<Arc<tokio::sync::Mutex<PeersByIp>>>,
config: TunDeviceConfig,
) -> (Self, TunTaskTx, TunTaskResponseRx) {
let tun = setup_tokio_tun_device(
format!("{TUN_BASE_NAME}%d").as_str(),
TUN_DEVICE_ADDRESS.parse().unwrap(),
TUN_DEVICE_NETMASK.parse().unwrap(),
);
let TunDeviceConfig {
base_name,
ip,
netmask,
} = config;
let name = format!("{base_name}%d");
let tun = setup_tokio_tun_device(&name, ip, netmask);
log::info!("Created TUN device: {}", tun.name());
// Channels to communicate with the other tasks
@@ -162,10 +175,10 @@ impl TunDevice {
}
}
// But we do it by consulting the NAT table.
// But we can also do it by consulting the NAT table.
RoutingMode::Nat(ref nat_table) => {
if let Some(tag) = nat_table.nat_table.get(&dst_addr) {
log::info!("Forward packet to wg tunnel with tag: {tag}");
log::info!("Forward packet with tag: {tag}");
self.tun_task_response_tx
.send((*tag, packet.to_vec()))
.await
@@ -180,7 +193,7 @@ impl TunDevice {
}
pub async fn run(mut self) {
let mut buf = [0u8; 1024];
let mut buf = [0u8; 65535];
loop {
tokio::select! {
+8 -8
View File
@@ -8,21 +8,17 @@ use log::info;
pub const WG_ADDRESS: &str = "0.0.0.0";
// The interface used to route traffic
pub const TUN_BASE_NAME: &str = "nymtun";
pub const TUN_DEVICE_ADDRESS: &str = "10.0.0.1";
pub const TUN_BASE_NAME: &str = "nymwg";
pub const TUN_DEVICE_ADDRESS: &str = "10.1.0.1";
pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
// The private key of the listener
// Corresponding public key: "WM8s8bYegwMa0TJ+xIwhk+dImk2IpDUKslDBCZPizlE="
const PRIVATE_KEY: &str = "AEqXrLFT4qjYq3wmX0456iv94uM6nDj5ugp6Jedcflg=";
// The public keys of the registered peer (clients)
// Corresponding private key: "ILeN6gEh6vJ3Ju8RJ3HVswz+sPgkcKtAYTqzQRhTtlo="
const PEER: &str = "NCIhkgiqxFx1ckKl3Zuh595DzIFl8mxju1Vg995EZhI=";
// The AllowedIPs for the connected peer, which is one a single IP and the same as the IP that the
// peer has configured on their side.
const ALLOWED_IPS: &str = "10.0.0.2";
const ALLOWED_IPS: &str = "10.1.0.2";
fn decode_base64_key(base64_key: &str) -> [u8; 32] {
general_purpose::STANDARD
@@ -46,7 +42,11 @@ pub fn server_static_private_key() -> x25519::StaticSecret {
pub fn peer_static_public_key() -> x25519::PublicKey {
// A single static public key is used during development
let peer_static_public_bytes: [u8; 32] = decode_base64_key(PEER);
// Read from NYM_PEER_PUBLIC_KEY env variable
let peer = std::env::var("NYM_PEER_PUBLIC_KEY").expect("NYM_PEER_PUBLIC_KEY must be set");
let peer_static_public_bytes: [u8; 32] = decode_base64_key(&peer);
let peer_static_public = x25519::PublicKey::try_from(peer_static_public_bytes).unwrap();
info!(
"Adding wg peer public key: {}",
+8 -4
View File
@@ -4,7 +4,7 @@
# Architecture
- [Network Overview](architecture/network-overview.md)
- [Mixnet Traffic Flow](architecture/traffic-flow.md)
<!-- todo reintroduce this with themed images -->
<!-- TODO reintroduce this with themed images -->
<!-- - [Network Rewards](architecture/network-rewards.md) -->
# Binaries
@@ -22,9 +22,13 @@
# Clients
- [Clients Overview](clients/overview.md)
- [Websocket](clients/websocket-client.md)
- [Socks5](clients/socks5-client.md)
- [Webassembly](clients/webassembly-client.md)
- [Websocket Client](clients/websocket-client.md)
- [Setup & Run](clients/websocket/setup.md)
- [Configuration](clients/websocket/config.md)
- [Using Your Client](clients/websocket/usage.md)
- [Examples](clients/websocket/examples.md)
- [Socks5 Client](clients/socks5-client.md)
- [Webassembly Client](clients/webassembly-client.md)
- [Addressing System](clients/addressing-system.md)
# SDK
@@ -160,7 +160,7 @@ Create a service file for the socks5 client at `/etc/systemd/system/nym-socks5-c
```ini
[Unit]
Description=Nym Socks5 Client
Description=Nym Socks5 Client
StartLimitInterval=350
StartLimitBurst=10
@@ -2,13 +2,15 @@
The Nym webassembly client allows any webassembly-capable runtime to build and send Sphinx packets to the Nym network, for uses in edge computing and browser-based applications.
This is currently packaged and distributed for ease of use via the [Nym Typescript SDK library](../sdk/typescript.md).
This is currently packaged and distributed for ease of use via the [Nym Typescript SDK library](../sdk/typescript.md). **We imagine most developers will use this client via the SDK for ease.**
The webassembly client allows for the easy creation of Sphinx packets from within mobile apps and browser-based client-side apps (including Electron or similar).
## Building apps with nym-client-wasm
## Building apps with Webassembly Client
Check out the [examples section](../sdk/typescript.md#using-the-sdk) of the SDK docs for examples of simple application framework setups. There are also two example applications located in the `clients/webassembly` directory in the main Nym platform codebase. The `js-example` is a simple, bare-bones JavaScript app.
Check out the [Typescript SDK docs](https://sdk.nymtech.net) for examples of usage.
There are also example applications located in the `clients/webassembly` directory in the main Nym platform codebase.
## Think about what you're sending!
```admonish caution
@@ -7,208 +7,7 @@
<!-- cmdrun ../../../../target/release/nym-client --version | grep "Build Version" | cut -b 21-26 -->
```
## Client setup
### Viewing command help
You can run this client as a standalone process and pipe traffic into it to be sent through the mixnet. This is useful if you're building an application in a language other than Typescript or Rust and cannot utilise one of the SDKs.
You can check that your binaries are properly compiled with:
You can find the code for this client [here](https://github.com/nymtech/nym/tree/develop/clients/native).
```
./nym-client --help
```
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-client --help -->
```
~~~
The two most important commands you will issue to the client are:
* `init` - initalise a new client instance.
* `run` - run a mixnet client process.
You can check the necessary parameters for the available commands by running:
```
./nym-client <command> --help
```
### Initialising your client
Before you can use the client, you need to initalise a new instance of it. Each instance of the client has its own public/private keypair, and connects to its own gateway node. Taken together, these 3 things (public/private keypair + gateway node identity key) make up an app's identity.
Initialising a new client instance can be done with the following command:
```
./nym-client init --id example-client
```
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../target/release/nym-client init --id example-client -->
```
~~~
The `--id` in the example above is a local identifier so that you can name your clients; it is **never** transmitted over the network.
There is an optional `--gateway` flag that you can use if you want to use a specific gateway. The supplied argument is the `Identity Key` of the gateway you wish to use, which can be found on the [mainnet Network Explorer](https://explorer.nymtech.net/network-components/gateways) or [Sandbox Testnet Explorer](https://sandbox-explorer.nymtech.net/network-components/gateways) depending on which network you are on.
Not passing this argument will randomly select a gateway for your client.
#### Choosing a Gateway
By default - as in the example above - your client will choose a random gateway to connect to.
However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client:
* If you wish to connect to a specific gateway, you can specify this with the `--gateway` flag when running `init`.
* You can also choose a gateway based on its location relative to your client. This can be done by appending the `--latency-based-routing` flag to your `init` command. This command means that to select a gateway, your client will:
* fetch a list of all availiable gateways
* send few ping messages to all of them, and measure response times.
* create a weighted distribution to randomly choose one, favouring ones with lower latency.
> Note this doesn't mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms
### Running your client
You can run the initalised client by doing this:
```
./nym-client run --id example-client
```
When you run the client, it immediately starts generating (fake) cover traffic and sending it to the mixnet.
When the client is first started, it will reach out to the Nym network's validators, and get a list of available Nym nodes (gateways, mixnodes, and validators). We call this list of nodes the network _topology_. The client does this so that it knows how to connect, register itself with the network, and know which mixnodes it can route Sphinx packets through.
### Configuring your client
When you initalise a client instance, a configuration directory will be generated and stored in `$HOME_DIR/.nym/clients/<client-name>/`.
```
tree $HOME/<user>/.nym/clients/example-client
├── config
│   └── config.toml
└── data
├── ack_key.pem
├── gateway_shared.pem
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
```
The `config.toml` file contains client configuration options, while the two `pem` files contain client key information.
The generated files contain the client name, public/private keypairs, and gateway address. The name `<client_id>` in the example above is just a local identifier so that you can name your clients.
#### Configuring your client for Docker
By default, the native client listens to host `127.0.0.1`. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as `0.0.0.0`.
You can set this via the `--host` flag during either the `init` or `run` commands.
Alternatively, a custom host can be set in the `config.toml` file under the `socket` section. If you do this, remember to restart your client process.
## Using your client
### Connecting to the local websocket
The Nym native client exposes a websocket interface that your code connects to. To program your app, choose a websocket library for whatever language you're using. The **default** websocket port is `1977`, you can override that in the client config if you want.
The Nym monorepo includes websocket client example code for Rust, Go, Javacript, and Python, all of which can be found [here](https://github.com/nymtech/nym/tree/master/clients/native/examples).
> Rust users can run the examples with `cargo run --example <rust_file>.rs`, as the examples are not organised in the same way as the other examples, due to already being inside a Cargo project.
All of these code examples will do the following:
* connect to a running websocket client on port `1977`
* format a message to send in either JSON or Binary format. Nym messages have defined JSON formats.
* send the message into the websocket. The native client packages the message into a Sphinx packet and sends it to the mixnet
* wait for confirmation that the message hit the native client
* wait to receive messages from other Nym apps
By varying the message content, you can easily build sophisticated service provider apps. For example, instead of printing the response received from the mixnet, your service provider might take some action on behalf of the user - perhaps initiating a network request, a blockchain transaction, or writing to a local data store.
> You can find an example of building both frontend and service provider code with the websocket client in the [Simple Service Provider Tutorial](https://nymtech.net/developers/tutorials/simple-service-provider/simple-service-provider.html) in the Developer Portal.
### Message Types
There are a small number of messages that your application sends up the websocket to interact with the native client, as follows.
#### Sending text
If you want to send text information through the mixnet, format a message like this one and poke it into the websocket:
```json
{
"type": "send",
"message": "the message",
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
In some applications, e.g. where people are chatting with friends who they know, you might want to include unencrypted reply information in the message field. This provides an easy way for the receiving chat to then turn around and send a reply message:
```json
{
"type": "send",
"message": {
"sender": "198427b63ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm",
"chatMessage": "hi julia!"
},
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
If that fits your security model, good. However, will probably be the case that you want to send **anonymous replies using Single Use Reply Blocks (SURBs)**.
You can read more about SURBs [here](../architecture/traffic-flow.md#private-replies-using-surbs) but in short they are ways for the receiver of this message to anonymously reply to you - the sender - without them having to know your nym address.
Your client will send along a number of `replySurbs` to the recipient of the message. These are pre-addressed Sphinx packets that the recipient can write to the payload of (i.e. write response data to), but not view the address. If the recipient is unable to fit the response data into the bucket of SURBs sent to it, it will use a SURB to request more SURBs be sent to it from your client.
```json
{
"type": "sendAnonymous",
"message": "something you want to keep secret"
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
"replySurbs": 100 // however many reply SURBs to send along with your message
}
```
Each bucket of replySURBs, when received as part of an incoming message, has a unique session identifier, which **only identifies the bucket of pre-addressed packets**. This is necessary to make sure that your app is replying to the correct people with the information meant for them! Constructing a reply with SURBs looks something like this (where `senderTag` was parsed from the incoming message)
```json
{
"type": "reply",
"message": "reply you also want to keep secret",
"senderTag": "the sender tag you parsed from the incoming message"
}
```
#### Sending binary data
You can also send bytes instead of JSON. For that you have to send a binary websocket frame containing a binary encoded
Nym [`ClientRequest`](https://github.com/nymtech/nym/blob/develop/clients/native/websocket-requests/src/requests.rs#L25) containing the same information.
As a response the `native-client` will send a `ServerResponse` to be decoded.
You can find examples of sending and receiving binary data in the Rust, Python and Go [code examples](https://github.com/nymtech/nym/tree/master/clients/native/examples), and an example project from the Nym community [BTC-BC](https://github.com/sgeisler/btcbc-rs/): Bitcoin transaction transmission via Nym, a client and service provider written in Rust.
#### Getting your own address
Sometimes, when you start your app, it can be convenient to ask the native client to tell you what your own address is (from the saved configuration files). To do this, send:
```json
{
"type": "selfAddress"
}
```
You'll get back:
```json
{
"type": "selfAddress",
"address": "the-address" // e.g. "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
#### Error messages
Errors from the app's client, or from the gateway, will be sent down the websocket to your code in the following format:
```json
{
"type": "error",
"message": "string message"
}
```
@@ -0,0 +1,47 @@
# Configuration
## Default listening port
The Nym native client exposes a websocket interface that your code connects to. To program your app, choose a websocket library for whatever language you're using. The **default** websocket port is `1977`, you can override that in the client config if you want.
You can either set this via the `--port` flag at `init` or `run`, or you can manually edit `~/.nym/clients/<CLIENT-ID>/config/config.toml`.
> Remember to restart your client if you change your listening port via editing your config file.
## Choosing a Gateway
By default your client will choose a random gateway to connect to.
However, there are several options for choosing a gateway, if you do not want one that is randomly assigned to your client:
* If you wish to connect to a specific gateway, you can specify this with the `--gateway` flag when running `init`.
* You can also choose a gateway based on its location relative to your client. This can be done by appending the `--latency-based-routing` flag to your `init` command. This command means that to select a gateway, your client will:
* fetch a list of all available gateways
* send few ping messages to all of them, and measure response times.
* create a weighted distribution to randomly choose one, favouring ones with lower latency.
> Note this doesn't mean that your client will pick the closest gateway to you, but it will be far more likely to connect to gateway with a 20ms ping rather than 200ms
## Configuring your client
When you initalise a client instance, a configuration directory will be generated and stored in `$HOME_DIR/.nym/clients/<client-name>/`.
```
tree $HOME/<user>/.nym/clients/example-client
├── config
│   └── config.toml
└── data
├── ack_key.pem
├── gateway_shared.pem
├── private_encryption.pem
├── private_identity.pem
├── public_encryption.pem
└── public_identity.pem
```
The `config.toml` file contains client configuration options, while the two `pem` files contain client key information.
The generated files contain the client name, public/private keypairs, and gateway address. The name `<client_id>` in the example above is just a local identifier so that you can name your clients.
### Configuring your client for Docker
By default, the native client listens to host `127.0.0.1`. However this can be an issue if you wish to run a client in a Dockerized environment, where it can be convenenient to listen on a different host such as `0.0.0.0`.
You can set this via the `--host` flag during either the `init` or `run` commands.
Alternatively, a custom host can be set in the `config.toml` file under the `socket` section. If you do this, remember to restart your client process.
@@ -0,0 +1,15 @@
# Examples
The Nym monorepo includes websocket client example code for Rust, Go, Javacript, and Python, all of which can be found [here](https://github.com/nymtech/nym/tree/master/clients/native/examples).
> Rust users can run the examples with `cargo run --example <rust_file>.rs`, as the examples are not organised in the same way as the other examples, due to already being inside a Cargo project.
All of these code examples will do the following:
* connect to a running websocket client on port `1977`
* format a message to send in either JSON or Binary format. Nym messages have defined JSON formats.
* send the message into the websocket. The native client packages the message into a Sphinx packet and sends it to the mixnet
* wait for confirmation that the message hit the native client
* wait to receive messages from other Nym apps
By varying the message content, you can easily build sophisticated service provider apps. For example, instead of printing the response received from the mixnet, your service provider might take some action on behalf of the user - perhaps initiating a network request, a blockchain transaction, or writing to a local data store.
> You can find an example of building both frontend and service provider code with the websocket client in the [Simple Service Provider Tutorial](https://nymtech.net/developers/tutorials/simple-service-provider/simple-service-provider.html) in the Developer Portal.
@@ -0,0 +1,59 @@
# Setup & Run
## Viewing command help
You can check that your binaries are properly compiled with:
```
./nym-client --help
```
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../../target/release/nym-client --help -->
```
~~~
The two most important commands you will issue to the client are:
* `init` - initalise a new client instance.
* `run` - run a mixnet client process.
You can check the necessary parameters for the available commands by running:
```
./nym-client <command> --help
```
## Initialising your client
Before you can use the client, you need to initalise a new instance of it. Each instance of the client has its own public/private keypair, and connects to its own gateway node. Taken together, these 3 things (public/private keypair + gateway node identity key) make up an app's identity.
Initialising a new client instance can be done with the following command:
```
./nym-client init --id example-client
```
~~~admonish example collapsible=true title="Console output"
```
<!-- cmdrun ../../../../../target/release/nym-client init --id example-client -->
```
~~~
The `--id` in the example above is a local identifier so that you can name your clients; it is **never** transmitted over the network.
There is an optional `--gateway` flag that you can use if you want to use a specific gateway. The supplied argument is the `Identity Key` of the gateway you wish to use, which can be found on the [mainnet Network Explorer](https://explorer.nymtech.net/network-components/gateways) or [Sandbox Testnet Explorer](https://sandbox-explorer.nymtech.net/network-components/gateways) depending on which network you are on.
Not passing this argument will randomly select a gateway for your client.
## Running your client
You can run the initalised client by doing this:
```
./nym-client run --id example-client
```
When you run the client, it immediately starts generating (fake) cover traffic and sending it to the mixnet.
When the client is first started, it will reach out to the Nym network's validators, and get a list of available Nym nodes (gateways, mixnodes, and validators). We call this list of nodes the network _topology_. The client does this so that it knows how to connect, register itself with the network, and know which mixnodes it can route Sphinx packets through.
@@ -0,0 +1,117 @@
# Using Your Client
The Nym native client exposes a websocket interface that your code connects to. The **default** websocket port is `1977`, you can override that in the client config if you want.
Once you have a websocket connection, interacting with the client involves piping messages down the socket and listening for incoming messages.
# Message Requests
There are a number of message types that you can send up the websocket as defined [here](https://github.com/nymtech/nym/blob/develop/clients/native/websocket-requests/src/requests.rs):
```rust,noplayground
{{#include ../../../../../clients/native/websocket-requests/src/requests.rs:55:97}}
```
## Getting your own address
When you start your app, it is best practice to ask the native client to tell you what your own address is (from the generated configuration files - see [here](../addressing-system.md) for more on Nym's addressing scheme). If you are running a service, you need to do this in order to know what address to give others. In a client-side piece of code you can also use this as a test to make sure your websocket connection is running smoothly. To do this, send:
```json
{
"type": "selfAddress"
}
```
You'll receive a response of the format:
```json
{
"type": "selfAddress",
"address": "your address" // e.g. "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
See [here](https://github.com/nymtech/nym/blob/93cc281abc2cc951023b51746fa6f2ead1f56c46/clients/native/examples/python-examples/websocket/textsend.py#L16C9-L16C9) for an example of this being used.
> Note that all the pieces of native client example code begin with printing the selfAddress. Examples exist for Rust, Go, Javascript, and Python.
## Sending text
If you want to send text information through the mixnet, format a message like this one and poke it into the websocket:
```json
{
"type": "send",
"message": "the message",
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
In some applications, e.g. where people are chatting with friends who they know, you might want to include unencrypted reply information in the message field. This provides an easy way for the receiving chat to then turn around and send a reply message:
```json
{
"type": "send",
"message": {
"sender": "198427b63ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm",
"chatMessage": "hi julia!"
},
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm"
}
```
**If that fits your security model, good. However, will probably be the case that you want to send anonymous replies using Single Use Reply Blocks (SURBs)**.
You can read more about SURBs [here](../../architecture/traffic-flow.md#private-replies-using-surbs) but in short they are ways for the receiver of this message to anonymously reply to you - the sender - **without them having to know your client address**.
Your client will send along a number of `replySurbs` to the recipient of the message. These are pre-addressed Sphinx packets that the recipient can write to the payload of (i.e. write response data to), but not view the final destination of. If the recipient is unable to fit the response data into the bucket of SURBs sent to it, it will use a SURB to request more SURBs be sent to it from your client.
```json
{
"type": "sendAnonymous",
"message": "something you want to keep secret",
"recipient": "71od3ZAupdCdxeFNg8sdonqfZTnZZy1E86WYKEjxD4kj@FWYoUrnKuXryysptnCZgUYRTauHq4FnEFu2QGn5LZWbm",
"replySurbs": 20 // however many reply SURBs to send along with your message
}
```
See ['Replying to SURB Messages'](#replying-to-surb-messages) below for an example of how to deal with incoming messages that have SURBs attached.
Deciding on the amount of SURBs to generate and send along with outgoing messages depends on the expected size of the reply. You might want to send a lot of SURBs in order to make sure you get your response as quickly as possible (but accept the minor additional latency when sending, as your client has to generate and encrypt the packets), or you might just send a few (e.g. 20) and then if your response requires more SURBs, send them along, accepting the additional latency in getting your response.
## Sending binary data
You can also send bytes instead of JSON. For that you have to send a binary websocket frame containing a binary encoded
Nym [`ClientRequest`](https://github.com/nymtech/nym/blob/develop/clients/native/websocket-requests/src/requests.rs#L25) containing the same information.
> As a response the `native-client` will send a `ServerResponse` to be decoded. See [Message Responses](#message-responses) below for more.
You can find examples of sending and receiving binary data in the [code examples](https://github.com/nymtech/nym/tree/master/clients/native/examples), and an example project from the Nym community [BTC-BC](https://github.com/sgeisler/btcbc-rs/): Bitcoin transaction transmission via Nym, a client and service provider written in Rust.
## Replying to SURB messages
Each bucket of `replySURBs`, when received as part of an incoming message, has a unique session identifier, which **only identifies the bucket of pre-addressed packets**. This is necessary to make sure that your app is replying to the correct people with the information meant for them in a situation where multiple clients are sending requests to a single service.
Constructing a reply with SURBs looks something like this (where `senderTag` was parsed from the incoming message)
```json
{
"type": "reply",
"message": "reply you also want to keep secret",
"senderTag": "the sender tag you parsed from the incoming message"
}
```
## Error messages
Errors from the app's client, or from the gateway, will be sent down the websocket to your code in the following format:
```json
{
"type": "error",
"message": "string message"
}
```
## LaneQueueLength
This is currently only used in the [Socks Client](../socks5-client.md) to keep track of the number of Sphinx packets waiting to be sent to the mixnet via being slotted amongst cover traffic. As this value becomes larger, the client signals to the application it should slow down the speed with which it writes to the proxy. This is to stop situations arising whereby an app connected to the client appears as if it has sent (e.g.) a bunch of messages and is awaiting a reply, when they in fact have not been sent through the mixnet yet.
# Message Responses
Responses to your messages are defined [here](https://github.com/nymtech/nym/blob/develop/clients/native/websocket-requests/src/responses.rs):
```rust,noplayground
{{#include ../../../../../clients/native/websocket-requests/src/responses.rs:48:53}}
```
+1 -1
View File
@@ -3,7 +3,7 @@
[package]
name = "nym-mixnode"
version = "1.1.32"
version = "1.1.33"
authors = [
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
"Jędrzej Stuczyński <andrew@nymtech.net>",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "extension-storage"
version = "1.2.1"
version = "1.2.4-rc.1"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/nymtech/nym"
+15 -14
View File
@@ -1,24 +1,24 @@
{
"name": "@nymproject/nym-wallet-app",
"version": "1.2.10",
"main": "index.js",
"version": "1.2.12-rc.1",
"license": "MIT",
"main": "index.js",
"scripts": {
"prewebpack:dev": "yarn --cwd .. build",
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
"webpack:prod": "yarn webpack --progress --config webpack.prod.js",
"tauri:dev": "yarn tauri dev",
"tauri:build": "yarn tauri build",
"tsc": "tsc --noEmit true",
"tsc:watch": "tsc --noEmit true --watch",
"dev": "run-p tauri:dev webpack:dev",
"prebuild": "yarn --cwd .. build",
"build": "run-s webpack:prod tauri:build",
"dev": "run-p tauri:dev webpack:dev",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prebuild": "yarn --cwd .. build",
"prestorybook": "yarn --cwd .. build",
"prewebpack:dev": "yarn --cwd .. build",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
"storybook:build": "build-storybook",
"tauri:build": "yarn tauri build",
"tauri:dev": "yarn tauri dev",
"tsc": "tsc --noEmit true",
"tsc:watch": "tsc --noEmit true --watch",
"webpack:dev": "yarn webpack serve --config webpack.dev.js",
"webpack:prod": "yarn webpack --progress --config webpack.prod.js"
},
"dependencies": {
"@emotion/react": "^11.7.0",
@@ -29,7 +29,7 @@
"@mui/styles": "^5.2.2",
"@mui/utils": "^5.7.0",
"@nymproject/mui-theme": "^1.0.0",
"@nymproject/node-tester": "^1.0.0",
"@nymproject/node-tester": "^1.2.3",
"@nymproject/react": "^1.0.0",
"@nymproject/types": "^1.0.0",
"@storybook/react": "^6.5.15",
@@ -123,5 +123,6 @@
"webpack-dev-server": "^4.5.0",
"webpack-favicons": "^1.3.8",
"webpack-merge": "^5.8.0"
}
},
"private": false
}
+1 -8
View File
@@ -10,13 +10,6 @@
"sdk/typescript/packages/mui-theme",
"sdk/typescript/packages/react-components",
"sdk/typescript/packages/validator-client",
"sdk/typescript/packages/mix-fetch-node",
"sdk/typescript/packages/nodejs-client",
"sdk/typescript/packages/node-tester",
"sdk/typescript/packages/sdk-react",
"sdk/typescript/packages/mix-fetch",
"sdk/typescript/packages/sdk",
"sdk/typescript/codegen/contract-clients",
"ts-packages/*",
"nym-wallet",
"nym-connect/**",
@@ -61,4 +54,4 @@
"node-gyp": "^9.3.1",
"tslog": "3.3.3"
}
}
}
@@ -1,6 +1,6 @@
{
"name": "@nymproject/contract-clients",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"description": "A client for all Nym smart contracts",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -27,4 +27,4 @@
},
"private": false,
"types": "./dist/index.d.ts"
}
}
@@ -7,23 +7,11 @@ import Box from '@mui/material/Box';
import { mixFetch } from '@nymproject/mix-fetch-full-fat';
import Stack from '@mui/material/Stack';
import Paper from '@mui/material/Paper';
import type { SetupMixFetchOps } from '@nymproject/mix-fetch';
import type { SetupMixFetchOps } from '@nymproject/mix-fetch-full-fat';
const defaultUrl = 'https://nymtech.net/favicon.svg';
const args = { mode: 'unsafe-ignore-cors' };
const extra = {
hiddenGateways: [
{
owner: 'n1kymvkx6vsq7pvn6hfurkpg06h3j4gxj4em7tlg',
host: 'gateway1.nymtech.net',
explicitIp: '213.219.38.119',
identityKey: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM',
sphinxKey: 'CYcrjoJ8GT7Dp54zViUyyRUfegeRCyPifWQZHRgMZrfX',
},
],
};
const mixFetchOptions: SetupMixFetchOps = {
preferredGateway: 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM', // with WSS
preferredNetworkRequester:
@@ -32,7 +20,6 @@ const mixFetchOptions: SetupMixFetchOps = {
requestTimeoutMs: 60_000,
},
forceTls: true, // force WSS
extra, // manually set the gateway details for WSS so certificates will work for hostname
};
export const MixFetch = () => {
@@ -26,6 +26,7 @@ export const Traffic = () => {
await client?.client.start({
clientId: crypto.randomUUID(),
nymApiUrl,
forceTls: true, // force WSS
});
// check when is connected and set the self address
+4 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/ts-sdk-docs",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"description": "Nym Typescript SDK Docs",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -28,10 +28,9 @@
"@mui/icons-material": "^5.14.9",
"@mui/lab": "^5.0.0-alpha.145",
"@mui/material": "^5.14.8",
"@nymproject/contract-clients": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.2.1-rc.0 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.1-rc.0 || ^1",
"@nymproject/contract-clients": ">=1.2.4-rc.1 || ^1",
"@nymproject/mix-fetch-full-fat": ">=1.2.4-rc.1 || ^1",
"@nymproject/sdk-full-fat": ">=1.2.4-rc.1 || ^1",
"chain-registry": "^1.19.0",
"cosmjs-types": "^0.8.0",
"next": "^13.4.19",
@@ -70,6 +70,7 @@ export function MixnetClient() {
await client?.client.start({
clientId: crypto.randomUUID(),
nymApiUrl,
forceTls: true, // force WSS
});
// Check when is connected and set the self address
@@ -1,6 +1,6 @@
# mixFetch
import { MixFetch } from '../../components/mixfetch'
import { MixFetch } from '../../components/mix-fetch'
import Box from '@mui/material/Box';
import FormattedMixFetchExampleCode from '../../code-examples/mixfetch-example-code.mdx';
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-plain-html-parcel",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM and plain HTML bundled with Parcel v2",
"license": "Apache-2.0",
"scripts": {
@@ -15,7 +15,7 @@
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@types/jest": "^27.0.1",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-plain-html",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM and plain HTML",
"license": "Apache-2.0",
"scripts": {
@@ -16,7 +16,7 @@
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-react-webpack-wasm",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM, React, Webpack, Typescript and the Nym theme + components library",
"license": "Apache-2.0",
"scripts": {
@@ -20,7 +20,7 @@
"@mui/lab": "^5.0.0-alpha.72",
"@mui/material": "^5.0.1",
"@mui/styles": "^5.0.1",
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1",
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-chrome-extension",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "This is an example of how Nym can be used within the context of a Chrome extension.",
"license": "ISC",
"author": "",
@@ -9,7 +9,7 @@
"build": "webpack"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-firefox-extension",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "This is an example of how Nym can be used within the context of a Firefox extension.",
"license": "ISC",
"author": "",
@@ -9,7 +9,7 @@
"build": "yarn webpack"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-example-parcel",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"license": "Apache-2.0",
"scripts": {
"build": "parcel build --no-cache --no-content-hash",
@@ -8,7 +8,7 @@
"start": "parcel --no-cache"
},
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch": ">=1.2.2-rc.0 || ^1",
"parcel": "^2.9.3"
},
"private": false,
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-node-tester-plain-html-parcel",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM and plain HTML bundled with Parcel v2",
"license": "Apache-2.0",
"scripts": {
@@ -15,7 +15,7 @@
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@types/jest": "^27.0.1",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-node-tester-plain-html",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM node tester and plain HTML",
"license": "Apache-2.0",
"scripts": {
@@ -16,7 +16,7 @@
"tsc:watch": "tsc --watch"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@babel/core": "^7.15.0",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-example-node-tester-react",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"description": "An example project that uses WASM node tester and React",
"license": "Apache-2.0",
"scripts": {
@@ -11,7 +11,7 @@
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1",
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1",
"parcel": "^2.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-node",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"description": "This package is a drop-in replacement for `fetch` in NodeJS to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -28,7 +28,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/mix-fetch-wasm-node": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-wasm-node": ">=1.2.4-rc.1 || ^1",
"comlink": "^4.3.1",
"fake-indexeddb": "^5.0.0",
"node-fetch": "^3.3.2",
@@ -68,4 +68,4 @@
},
"private": false,
"types": "./dist/cjs/index.d.ts"
}
}
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-tester-webpack",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"license": "Apache-2.0",
"scripts": {
"build": "webpack build --progress --config webpack.prod.js",
@@ -8,7 +8,7 @@
"start": "webpack serve --progress --port 3000"
},
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1"
"@nymproject/mix-fetch": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@babel/core": "^7.22.10",
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch-tester-parcel",
"version": "1.0.1",
"version": "1.0.4-rc.1",
"license": "Apache-2.0",
"scripts": {
"build": "npx parcel build --no-cache --no-content-hash",
@@ -8,7 +8,7 @@
"start": "npx parcel --no-cache"
},
"dependencies": {
"@nymproject/mix-fetch": ">=1.2.1-rc.0 || ^1"
"@nymproject/mix-fetch": ">=1.2.2-rc.0 || ^1"
},
"private": false,
"source": "../src/index.html"
@@ -1,6 +1,6 @@
{
"name": "@nymproject/mix-fetch",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"description": "This package is a drop-in replacement for `fetch` to send HTTP requests over the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -27,13 +27,14 @@
"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/mix-fetch-wasm": ">=1.2.1-rc.0 || ^1",
"@nymproject/mix-fetch-wasm": ">=1.2.4-rc.1 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
@@ -81,4 +82,4 @@
"private": false,
"type": "module",
"types": "./dist/esm/index.d.ts"
}
}
@@ -0,0 +1,8 @@
const fs = require('fs');
const packageName = '@nymproject/mix-fetch-wasm';
const packageJsonPath = require.resolve(packageName + '/package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
console.log(`🟢🟢🟢 ${packageName} is at ${packageJsonPath} is version ${packageJson.version}`);
@@ -1,6 +1,6 @@
{
"name": "@nymproject/node-tester",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"description": "This package provides a tester that can send test packets to mixnode that is part of the Nym Mixnet.",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
@@ -25,7 +25,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/nym-node-tester-wasm": ">=1.2.1-rc.0 || ^1",
"@nymproject/nym-node-tester-wasm": ">=1.2.4-rc.1 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
@@ -71,4 +71,4 @@
"private": false,
"type": "module",
"types": "./dist/esm/index.d.ts"
}
}
@@ -1,6 +1,6 @@
{
"name": "@nymproject/nodejs-client",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -25,7 +25,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/nym-client-wasm-node": ">=1.2.1-rc.0 || ^1",
"@nymproject/nym-client-wasm-node": ">=1.2.4-rc.1 || ^1",
"comlink": "^4.3.1",
"fake-indexeddb": "^4.0.2",
"rollup-plugin-polyfill": "^4.2.0",
@@ -66,4 +66,4 @@
},
"private": false,
"types": "./dist/index.d.ts"
}
}
@@ -1,4 +1,4 @@
import type { DebugWasm } from '@nymproject/nym-client-wasm-node';
import type { ClientOpts } from '@nymproject/nym-client-wasm-node';
/**
* Options for the Nym mixnet client.
@@ -32,7 +32,7 @@ export interface NymMixnetClient {
* @internal
*/
export interface IWebWorker {
start: (config: NymClientConfig) => void;
start: (opts?: ClientOpts) => void;
stop: () => void;
selfAddress: () => string | undefined;
setTextMimeTypes: (mimeTypes: string[]) => void;
@@ -55,7 +55,7 @@ export interface Client {
* });
*
*/
start: (config: NymClientConfig) => Promise<void>;
start: (opts?: ClientOpts) => Promise<void>;
/**
* Stop the client.
* @example
@@ -155,36 +155,6 @@ export interface Client {
rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => Promise<void>;
}
/**
* The configuration passed to the {@link Client.start} method of the {@link Client}
*/
export interface NymClientConfig {
/**
* A human-readable id for the client.
*/
clientId: string;
/**
* The URL of a validator API to query for the network topology.
*/
nymApiUrl: string;
/**
* Optional. The identity key of the preferred gateway to connect to.
*/
preferredGatewayIdentityKey?: string;
/**
* Optional. The listener websocket of the preferred gateway to connect to.
*/
gatewayListener?: string;
/**
* Optional. Settings for the WASM client.
*/
debug?: DebugWasm;
}
export interface Events {
/**
* @see {@link LoadedEvent}
@@ -5,13 +5,12 @@ import { parentPort } from 'worker_threads';
import '@nymproject/nym-client-wasm-node/nym_client_wasm_bg.wasm';
import {
ClientConfig,
NymClient,
NymClientBuilder,
decode_payload,
encode_payload_with_headers,
parse_utf8_string,
utf8_string_to_byte_array,
ClientOpts,
} from '@nymproject/nym-client-wasm-node';
import type {
@@ -19,7 +18,6 @@ import type {
ConnectedEvent,
IWebWorker,
LoadedEvent,
NymClientConfig,
OnRawPayloadFn,
RawMessageReceivedEvent,
StringMessageReceivedEvent,
@@ -44,28 +42,8 @@ const postMessageWithType = <E>(event: E) => parentPort?.postMessage(event);
class ClientWrapper {
client: NymClient | null = null;
builder: NymClientBuilder | null = null;
mimeTypes: string[] = [MimeTypes.TextPlain, MimeTypes.ApplicationJson];
/**
* Creates the WASM client and initialises it.
*/
init = (config: any, onRawPayloadHandler?: OnRawPayloadFn) => {
const onMessageHandler = (message: Uint8Array) => {
try {
if (onRawPayloadHandler) {
onRawPayloadHandler(message);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Unhandled exception in `ClientWrapper.onRawPayloadHandler`: ', e);
}
};
this.builder = new NymClientBuilder(config, onMessageHandler);
};
/**
* Sets the mime-types that will be parsed for UTF-8 string content.
*
@@ -95,16 +73,22 @@ class ClientWrapper {
/**
* Connects to the gateway and starts the client sending traffic.
*/
start = async () => {
if (!this.builder) {
// eslint-disable-next-line no-console
console.error('Client config has not been initialised. Please call `init` first.');
return;
}
start = async (opts?: ClientOpts, onRawPayloadHandler?: OnRawPayloadFn) => {
const onMessageHandler = (message: Uint8Array) => {
try {
if (onRawPayloadHandler) {
onRawPayloadHandler(message);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Unhandled exception in `ClientWrapper.onRawPayloadHandler`: ', e);
}
};
// this is current limitation of wasm in rust - for async methods you can't take self by reference...
// I'm trying to figure out if I can somehow hack my way around it, but for time being you have to re-assign
// the object (it's the same one)
this.client = await this.builder.start_client();
this.client = await new NymClient(onMessageHandler, opts);
};
/**
@@ -142,9 +126,9 @@ class ClientWrapper {
// this wrapper handles any state that the wasm-pack interop needs, e.g. holding an instance of the instantiated WASM code
const wrapper = new ClientWrapper();
const startHandler = async (config: NymClientConfig) => {
const startHandler = async (opts?: ClientOpts) => {
// create the client, passing handlers for events
wrapper.init(new ClientConfig(config), async (message) => {
await wrapper.start(opts, async (message) => {
// fire an event with the raw message
postMessageWithType<RawMessageReceivedEvent>({
kind: EventKinds.RawMessageReceived,
@@ -175,8 +159,7 @@ const startHandler = async (config: NymClientConfig) => {
console.error('Failed to parse binary message', e);
}
});
// start the client sending traffic
await wrapper.start();
// get the address
const address = wrapper.selfAddress();
postMessageWithType<ConnectedEvent>({ kind: EventKinds.Connected, args: { address } });
@@ -184,9 +167,9 @@ const startHandler = async (config: NymClientConfig) => {
// implement the public logic of this web worker (message exchange between the worker and caller is done by https://www.npmjs.com/package/comlink)
const webWorker: IWebWorker = {
start(config) {
start(opts?: ClientOpts) {
// eslint-disable-next-line no-console
startHandler(config).catch((e) => console.error('[Nym WASM client] Failed to start', e));
startHandler(opts).catch((e) => console.error('[Nym WASM client] Failed to start', e));
},
stop() {
wrapper.stop();
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk-react",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -20,7 +20,7 @@
"tsc": "tsc --noEmit true"
},
"dependencies": {
"@nymproject/sdk": ">=1.2.1-rc.0 || ^1"
"@nymproject/sdk": ">=1.2.2-rc.0 || ^1"
},
"devDependencies": {
"@babel/core": "^7.17.5",
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@nymproject/sdk",
"version": "1.2.1",
"version": "1.2.4-rc.1",
"license": "Apache-2.0",
"author": "Nym Technologies SA",
"files": [
@@ -24,13 +24,14 @@
"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.1-rc.0 || ^1",
"@nymproject/nym-client-wasm": ">=1.2.4-rc.1 || ^1",
"comlink": "^4.3.1"
},
"devDependencies": {
@@ -80,4 +81,4 @@
"private": false,
"type": "module",
"types": "./dist/esm/index.d.ts"
}
}
@@ -0,0 +1,8 @@
const fs = require('fs');
const packageName = '@nymproject/nym-client-wasm';
const packageJsonPath = require.resolve(packageName + '/package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
console.log(`🟢🟢🟢 ${packageName} is at ${packageJsonPath} is version ${packageJson.version}`);
@@ -1,4 +1,4 @@
import type { DebugWasm } from '@nymproject/nym-client-wasm';
import type { ClientOpts } from '@nymproject/nym-client-wasm';
/**
*
@@ -7,7 +7,7 @@ import type { DebugWasm } from '@nymproject/nym-client-wasm';
* @internal
*/
export interface IWebWorker {
start: (config: NymClientConfig) => void;
start: (opts?: ClientOpts) => void;
stop: () => void;
selfAddress: () => string | undefined;
setTextMimeTypes: (mimeTypes: string[]) => void;
@@ -30,7 +30,7 @@ export interface Client {
* });
*
*/
start: (config: NymClientConfig) => Promise<void>;
start: (opts?: ClientOpts) => Promise<void>;
/**
* Stop the client.
* @example
@@ -130,36 +130,6 @@ export interface Client {
rawSend: (args: { payload: Uint8Array; recipient: string; replySurbs?: number }) => Promise<void>;
}
/**
* The configuration passed to the {@link Client.start} method of the {@link Client}
*/
export interface NymClientConfig {
/**
* A human-readable id for the client.
*/
clientId: string;
/**
* The URL of a validator API to query for the network topology.
*/
nymApiUrl: string;
/**
* Optional. The identity key of the preferred gateway to connect to.
*/
preferredGatewayIdentityKey?: string;
/**
* Optional. The listener websocket of the preferred gateway to connect to.
*/
gatewayListener?: string;
/**
* Optional. Settings for the WASM client.
*/
debug?: DebugWasm;
}
export interface Events {
/**
* @see {@link LoadedEvent}
@@ -17,12 +17,11 @@ import wasmBytes from '@nymproject/nym-client-wasm/nym_client_wasm_bg.wasm';
// eslint-disable-next-line import/no-extraneous-dependencies
import init, {
NymClient,
NymClientBuilder,
ClientConfig,
decode_payload,
parse_utf8_string,
utf8_string_to_byte_array,
encode_payload_with_headers,
ClientOpts,
} from '@nymproject/nym-client-wasm';
import type {
@@ -30,7 +29,6 @@ import type {
ConnectedEvent,
IWebWorker,
LoadedEvent,
NymClientConfig,
OnRawPayloadFn,
RawMessageReceivedEvent,
StringMessageReceivedEvent,
@@ -56,27 +54,8 @@ const postMessageWithType = <E>(event: E) => self.postMessage(event);
class ClientWrapper {
client: NymClient | null = null;
builder: NymClientBuilder | null = null;
mimeTypes: string[] = [MimeTypes.TextPlain, MimeTypes.ApplicationJson];
/**
* Creates the WASM client and initialises it.
*/
init = (config: ClientConfig, onRawPayloadHandler?: OnRawPayloadFn) => {
const onMessageHandler = (message: Uint8Array) => {
try {
if (onRawPayloadHandler) {
onRawPayloadHandler(message);
}
} catch (e) {
console.error('Unhandled exception in `ClientWrapper.onRawPayloadHandler`: ', e);
}
};
this.builder = new NymClientBuilder(config, onMessageHandler);
};
/**
* Sets the mime-types that will be parsed for UTF-8 string content.
*
@@ -106,16 +85,22 @@ class ClientWrapper {
/**
* Connects to the gateway and starts the client sending traffic.
*/
start = async () => {
if (!this.builder) {
console.error('Client config has not been initialised. Please call `init` first.');
return;
}
start = async (opts?: ClientOpts, onRawPayloadHandler?: OnRawPayloadFn) => {
const onMessageHandler = (message: Uint8Array) => {
try {
if (onRawPayloadHandler) {
onRawPayloadHandler(message);
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Unhandled exception in `ClientWrapper.onRawPayloadHandler`: ', e);
}
};
// this is current limitation of wasm in rust - for async methods you can't take self by reference...
// I'm trying to figure out if I can somehow hack my way around it, but for time being you have to re-assign
// the object (it's the same one)
this.client = await this.builder.start_client();
this.client = await new NymClient(onMessageHandler, opts);
};
/**
@@ -158,9 +143,9 @@ init(wasmBytes())
// this wrapper handles any state that the wasm-pack interop needs, e.g. holding an instance of the instantiated WASM code
const wrapper = new ClientWrapper();
const startHandler = async (config: NymClientConfig) => {
const startHandler = async (opts?: ClientOpts) => {
// create the client, passing handlers for events
wrapper.init(new ClientConfig(config), async (message) => {
await wrapper.start(opts, async (message) => {
// fire an event with the raw message
postMessageWithType<RawMessageReceivedEvent>({
kind: EventKinds.RawMessageReceived,
@@ -195,9 +180,6 @@ init(wasmBytes())
}
});
// start the client sending traffic
await wrapper.start();
// get the address
const address = wrapper.selfAddress();
postMessageWithType<ConnectedEvent>({ kind: EventKinds.Connected, args: { address } });
+7
View File
@@ -13,8 +13,15 @@ rm -rf dist || true
# use wasm-pack to build packages
yarn build:wasm
# enable dev mode and then install dev packages
yarn dev:on
yarn
# build the Typescript SDK packages
yarn build:ci:sdk
# build documentation
yarn docs:prod:build
# turn dev mode off
yarn dev:off
+1 -1
View File
@@ -2,7 +2,7 @@ import fs from 'fs';
const packageJson = JSON.parse(fs.readFileSync('package.json').toString());
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**'];
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**', 'sdk/typescript/codegen/**'];
if (!packageJson.workspaces.includes(devWorkspace)) {
// add
packageJson.workspaces.push(...devWorkspace);
+1 -1
View File
@@ -2,7 +2,7 @@ import fs from 'fs';
const packageJson = JSON.parse(fs.readFileSync('package.json').toString());
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**'];
const devWorkspace = ['sdk/typescript/packages/**', 'sdk/typescript/examples/**', 'sdk/typescript/codegen/**'];
// remove
packageJson.workspaces = packageJson.workspaces.filter((w) => !devWorkspace.includes(w));
@@ -21,6 +21,11 @@ pub use crate::config::Config;
pub mod config;
pub mod error;
// The interface used to route traffic
pub const TUN_BASE_NAME: &str = "nymtun";
pub const TUN_DEVICE_ADDRESS: &str = "10.0.0.1";
pub const TUN_DEVICE_NETMASK: &str = "255.255.255.0";
pub struct OnStartData {
// to add more fields as required
pub address: Recipient,
@@ -123,8 +128,14 @@ impl IpPacketRouterBuilder {
let self_address = *mixnet_client.nym_address();
// Create the TUN device that we interact with the rest of the world with
let config = nym_wireguard::tun_device::TunDeviceConfig {
base_name: TUN_BASE_NAME.to_string(),
ip: TUN_DEVICE_ADDRESS.parse().unwrap(),
netmask: TUN_DEVICE_NETMASK.parse().unwrap(),
};
let (tun, tun_task_tx, tun_task_response_rx) = nym_wireguard::tun_device::TunDevice::new(
nym_wireguard::tun_device::RoutingMode::new_nat(),
config,
);
tun.start();
@@ -29,7 +29,12 @@ pub(crate) trait ReleasePackage: Sized {
fn bump_version(&mut self, pre_release: bool) -> anyhow::Result<()> {
let version = self.get_current_version()?;
let updated = if pre_release {
version.try_bump_prerelease()?
if version.pre.is_empty() {
let patch_updated = version.try_bump_patch_without_pre()?;
patch_updated.set_initial_release_candidate()?
} else {
version.try_bump_prerelease()?
}
} else {
version.try_bump_patch_without_pre()?
};
+11 -1
View File
@@ -170,7 +170,7 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
packages.register_cargo("wasm/mix-fetch");
packages.register_cargo("wasm/client");
packages.register_cargo("wasm/node-tester");
packages.register_cargo("wasm/full-nym-wasm");
// packages.register_cargo("wasm/full-nym-wasm");
packages.register_cargo("nym-browser-extension/storage");
// js packages that will have their package.json modified
@@ -196,6 +196,16 @@ fn initialise_internal_packages<P: AsRef<Path>>(root: P) -> InternalPackages {
packages.register_json("sdk/typescript/codegen/contract-clients");
// dependencies that will have their versions adjusted in the above packages
// WASM NodeJS
packages.register_known_js_dependency("@nymproject/nym-client-wasm-node");
packages.register_known_js_dependency("@nymproject/mix-fetch-wasm-node");
// WASM
packages.register_known_js_dependency("@nymproject/nym-node-tester-wasm");
packages.register_known_js_dependency("@nymproject/nym-client-wasm");
packages.register_known_js_dependency("@nymproject/mix-fetch-wasm");
packages.register_known_js_dependency("@nymproject/mix-fetch");
packages.register_known_js_dependency("@nymproject/mix-fetch-full-fat");
packages.register_known_js_dependency("@nymproject/mui-theme");
@@ -0,0 +1,2 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
+2 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.4-rc.1"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
@@ -25,6 +25,7 @@ wasm-bindgen-futures = { workspace = true }
thiserror = { workspace = true }
tsify = { workspace = true, features = ["js"] }
nym-bin-common = { path = "../../common/bin-common" }
wasm-client-core = { path = "../../common/wasm/client-core" }
wasm-utils = { path = "../../common/wasm/utils" }
+25 -6
View File
@@ -63,6 +63,7 @@ pub struct NymClient {
#[wasm_bindgen]
pub struct NymClientBuilder {
config: ClientConfig,
force_tls: bool,
custom_topology: Option<NymTopology>,
preferred_gateway: Option<IdentityKey>,
@@ -72,15 +73,16 @@ pub struct NymClientBuilder {
#[wasm_bindgen]
impl NymClientBuilder {
#[wasm_bindgen(constructor)]
pub fn new(
fn new(
config: ClientConfig,
on_message: js_sys::Function,
force_tls: bool,
preferred_gateway: Option<IdentityKey>,
storage_passphrase: Option<String>,
) -> Self {
NymClientBuilder {
config,
force_tls,
custom_topology: None,
storage_passphrase,
on_message,
@@ -109,6 +111,7 @@ impl NymClientBuilder {
Ok(NymClientBuilder {
config: full_config,
force_tls: false,
custom_topology: Some(topology.try_into()?),
on_message,
storage_passphrase: None,
@@ -149,9 +152,15 @@ impl NymClientBuilder {
// if we provided hardcoded topology, get gateway from it, otherwise get it the 'standard' way
let init_res = if let Some(topology) = &self.custom_topology {
setup_from_topology(user_chosen, false, topology, &client_store).await?
setup_from_topology(user_chosen, self.force_tls, topology, &client_store).await?
} else {
setup_gateway_from_api(&client_store, false, user_chosen, &nym_api_endpoints).await?
setup_gateway_from_api(
&client_store,
self.force_tls,
user_chosen,
&nym_api_endpoints,
)
.await?
};
let packet_type = self.config.base.debug.traffic.packet_type;
@@ -205,6 +214,9 @@ pub struct ClientOptsSimple {
#[tsify(optional)]
pub(crate) storage_passphrase: Option<String>,
#[tsify(optional)]
pub(crate) force_tls: Option<bool>,
}
#[derive(Tsify, Debug, Default, Clone, Serialize, Deserialize)]
@@ -249,9 +261,16 @@ impl NymClient {
if let Some(opts) = opts {
let preferred_gateway = opts.preferred_gateway;
let storage_passphrase = opts.storage_passphrase;
NymClientBuilder::new(config, on_message, preferred_gateway, storage_passphrase)
let force_tls = opts.force_tls.unwrap_or_default();
NymClientBuilder::new(
config,
on_message,
force_tls,
preferred_gateway,
storage_passphrase,
)
} else {
NymClientBuilder::new(config, on_message, None, None)
NymClientBuilder::new(config, on_message, false, None, None)
}
.start_client_async()
.await
+67 -6
View File
@@ -1,6 +1,7 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use anyhow::bail;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
@@ -86,15 +87,25 @@ pub fn decode_payload(message: Vec<u8>) -> Result<IEncodedPayload, JsValue> {
pub(crate) fn parse_payload(message: &[u8]) -> anyhow::Result<(EncodedPayloadMetadata, &[u8])> {
// 1st 8 bytes are the size (as u64 big endian)
let mut size = [0u8; 8];
if message.len() < 8 {
bail!("Message is too short to contain size information")
}
size.clone_from_slice(&message[0..8]);
let size = u64::from_be_bytes(size) as usize;
let metadata_size = u64::from_be_bytes(size) as usize;
// then the metadata
let metadata = String::from_utf8_lossy(&message[8..8 + size]).into_owned();
let metadata: EncodedPayloadMetadata = serde_json::from_str(metadata.as_str())?;
if metadata_size + 8 != message.len() {
return Err(anyhow::anyhow!(format!(
"Metadata size: {}, exceeds message with length of: {}",
metadata_size,
message.len()
),));
}
// finally the payload
let payload = &message[8 + size..];
//then the metadata
let metadata: EncodedPayloadMetadata = serde_json::from_slice(&message[8..8 + metadata_size])?;
//finally the payload
let payload = &message[8 + metadata_size..];
Ok((metadata, payload))
}
@@ -235,4 +246,54 @@ mod tests {
assert_eq!(res.0.headers, None);
assert_eq!(res.1, empty);
}
#[wasm_bindgen_test]
async fn test_parse_payload_too_short() {
let message = vec![0u8; 7];
let result = parse_payload(&message);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error.to_string(),
"Message is too short to contain size information"
);
}
#[wasm_bindgen_test]
async fn test_parse_payload_size_exceeds_length() {
let mut message = vec![0u8; 8];
message.extend(vec![1u8; 10]);
message[0..8].copy_from_slice(&(20u64.to_be_bytes()));
let result = parse_payload(&message);
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(
error.to_string(),
"Metadata size: 20, exceeds message with length of: 18"
);
}
#[wasm_bindgen_test]
async fn test_parse_payload_valid() {
let metadata = EncodedPayloadMetadata {
mime_type: "text/plain".to_string(),
headers: Some("test headers".to_string()),
};
let payload_data = vec![2u8, 3u8, 5u8];
let serialized_metadata = serde_json::to_string(&metadata).unwrap();
let metadata_length = serialized_metadata.len() as u64;
let mut message = metadata_length.to_be_bytes().to_vec();
message.extend_from_slice(serialized_metadata.as_bytes());
message.extend_from_slice(&payload_data);
let (parsed_metadata, parsed_payload) = parse_payload(&message).unwrap();
assert_eq!(parsed_metadata.mime_type, metadata.mime_type);
assert!(parsed_metadata.headers.is_some());
assert_eq!(parsed_metadata.headers.unwrap(), "test headers");
assert_eq!(parsed_payload, payload_data.as_slice());
}
}
+13
View File
@@ -16,3 +16,16 @@ mod response_pusher;
#[cfg(target_arch = "wasm32")]
pub use wasm_client_core::set_panic_hook;
#[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!(
"wasm client version used: {:#?}",
nym_bin_common::bin_info!()
);
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-wasm-sdk"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.2"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
+2 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "mix-fetch-wasm"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.4-rc.1"
edition = "2021"
keywords = ["nym", "fetch", "wasm", "webassembly", "privacy"]
license = "Apache-2.0"
@@ -27,6 +27,7 @@ wasm-bindgen-futures = { workspace = true }
thiserror = { workspace = true }
tsify = { workspace = true, features = ["js"] }
nym-bin-common = { path = "../../common/bin-common" }
http-api-client = { path = "../../common/http-api-client" }
nym-socks5-requests = { path = "../../common/socks5/requests" }
nym-ordered-buffer = { path = "../../common/socks5/ordered-buffer" }
@@ -8,74 +8,12 @@ package main
import (
"go-mix-conn/internal/bridge/go_bridge"
"go-mix-conn/internal/state"
"syscall/js"
)
var done chan struct{}
func init() {
println("[go init]: go module init")
q := js.Global().Get("location")
if q.IsUndefined() {
println("location undefined")
} else {
println("location ok")
}
a := js.Global().Get("Error")
if a.IsUndefined() {
println("Error undefined")
} else {
println("Error ok")
}
b := js.Global().Get("Promise")
if b.IsUndefined() {
println("Promise undefined")
} else {
println("Promise ok")
}
c := js.Global().Get("Reflect")
if c.IsUndefined() {
println("Reflect undefined")
} else {
println("Reflect ok")
}
d := js.Global().Get("Object")
if d.IsUndefined() {
println("Object undefined")
} else {
println("Object ok")
}
e := js.Global().Get("Response")
if e.IsUndefined() {
println("Response undefined")
} else {
println("Response ok")
}
f := js.Global().Get("Request")
if f.IsUndefined() {
println("Request undefined")
} else {
println("Request ok")
}
g := js.Global().Get("Proxy")
if g.IsUndefined() {
println("Proxy undefined")
} else {
println("Proxy ok")
}
h := js.Global().Get("Headers")
if h.IsUndefined() {
println("Headers undefined")
} else {
println("Headers ok")
}
i := js.Global().Get("Uint8Array")
if i.IsUndefined() {
println("Uint8Array undefined")
} else {
println("Uint8Array ok")
}
done = make(chan struct{})
state.InitialiseGlobalState()
@@ -223,23 +223,24 @@ func parseHeaders(headers js.Value, reqOpts types.RequestOptions, method string)
func parseBody(request *js.Value) (io.Reader, error) {
jsBody := request.Get(fieldRequestBody)
var bodyReader io.Reader
if !jsBody.IsUndefined() && !jsBody.IsNull() {
if jsBody.InstanceOf(js.Global().Get("ReadableStream")) && jsBody.Get("getReader").Type() == js.TypeFunction {
// Check to see if getReader is a function
log.Debug("stream body - getReader")
bodyReader = external.NewStreamReader(jsBody.Call("getReader"))
} else {
log.Debug("unstremable body - fallback to ArrayBuffer")
// Fall back to using ArrayBuffer
// https://developer.mozilla.org/en-US/docs/Web/API/Body/arrayBuffer
log.Debug("unstreamable body - fallback to ArrayBuffer")
bodyReader = external.NewArrayReader(request.Call("arrayBuffer"))
}
bodyBytes, err := io.ReadAll(bodyReader)
if err != nil {
return nil, err
}
// Leaving historical notes as reference points
// TODO: that seems super awkward. we're constructing a reader only to fully consume it
// and create it all over again so that we the recipient wouldn't complain about content-length
// surely there must be a better way?
return bytes.NewReader(bodyBytes), nil
}
-13
View File
@@ -160,18 +160,6 @@ async function nativeSetup() {
requestTimeoutMs: 10000
}
const extra = {
hiddenGateways: [
{
owner: "LoveIslandEnjoyer",
host: "gateway1.nymtech.net",
explicitIp: "213.219.38.119",
identityKey: "E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM",
sphinxKey: "CYcrjoJ8GT7Dp54zViUyyRUfegeRCyPifWQZHRgMZrfX"
}
]
}
await setupMixFetch({
// preferredNetworkRequester,
preferredGateway: "E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM",
@@ -181,7 +169,6 @@ async function nativeSetup() {
clientId: "my-client",
clientOverride: noCoverTrafficOverride,
mixFetchOverride,
extra
})
}
+2 -1
View File
@@ -30,5 +30,6 @@ 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!("[rust main]: rust module loaded");
wasm_utils::console_log!("mix fetch version used: {:#?}", nym_bin_common::bin_info!());
}
+1 -1
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-node-tester-wasm"
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.2.1"
version = "1.2.4-rc.1"
edition = "2021"
keywords = ["nym", "sphinx", "webassembly", "privacy", "tester"]
license = "Apache-2.0"
+24 -1075
View File
File diff suppressed because it is too large Load Diff