Compare commits

..

31 Commits

Author SHA1 Message Date
Tommy Verrall 34b8a0706d Update package.json 2023-07-25 16:49:45 +02:00
pierre 275417c186 fix workflows (try) 2023-07-25 16:05:01 +02:00
pierre 40a92e07d6 fix workflows 2023-07-25 15:47:24 +02:00
benedetta davico e83a12bd91 Update CHANGELOG.md 2023-07-25 15:36:59 +02:00
Tommy Verrall 381162554e Update nym-connect-publish-ubuntu.yml (#3706)
* Update nym-connect-publish-ubuntu.yml

* Update nym-connect-publish-ubuntu.yml

* Update nym-connect-publish-macos.yml

* Update nym-connect-publish-windows10.yml
2023-07-25 14:40:46 +02:00
Tommy Verrall 7a740c06fd Update nym-connect-publish-macos.yml (#3705)
* Update nym-connect-publish-macos.yml

install wasm and build

* Update nym-connect-publish-macos.yml

use the correct download
2023-07-25 13:45:32 +02:00
Tommy Verrall e65285ac7b bump versions for NC 2023-07-25 10:52:17 +02:00
mx 715a3bd687 Merge pull request #3696 from nymtech/feature/sdk-surb-example
Feature/sdk surb example
2023-07-24 07:16:03 +00:00
mfahampshire 858f1ac13c fixed clippy warning 2023-07-23 12:00:39 +02:00
pierre 3ee1328626 add alephium to supported wallets 2023-07-21 16:15:39 +02:00
pierre 36ac825b43 build(nc-desktop): sentry dsn as env var 2023-07-21 16:00:39 +02:00
mfahampshire 165f189115 ran fmt 2023-07-21 14:11:22 +02:00
mfahampshire b5fcfbe2fe added reply with surbs example to rust sdk examples dir 2023-07-21 14:04:33 +02:00
mfahampshire 7a38f1f469 added rust sdk surb example 2023-07-21 13:59:52 +02:00
pierre 5faca46235 ci: fix connect-desktop-ci workflow 2023-07-21 13:20:18 +02:00
Pierre Dommerc d780ac55b1 feat(nc-desktop): add sentry to backend (#3652) 2023-07-21 12:48:56 +02:00
Pierre Dommerc c5f7d066b0 refactor(nc-desktop): add privacy level user settings (#3664) 2023-07-21 12:48:30 +02:00
mx 8cc90be8c6 Merge pull request #3685 from nymtech/feature/network-requester-updates
Feature/network requester updates
2023-07-19 12:07:21 +00:00
mfahampshire aae96e7537 included url 2023-07-19 13:56:47 +02:00
mfahampshire 39b521bc1f updated NR guide with list explainer + info on comments in local allow list 2023-07-19 13:51:55 +02:00
mx 7339695ce8 Merge pull request #3677 from nymtech/feature/v1-1-24-docs
Feature/v1 1 25 docs (outdated branch name due to lack of release)
2023-07-19 11:43:11 +00:00
mfahampshire 0e1c9853aa version update 2023-07-19 13:16:11 +02:00
mfahampshire 76b9c669d7 * added serinko + alexia to book authors
* version bumps for next release
2023-07-19 12:45:30 +02:00
mfahampshire 553cfd098b updated sdk documentation with surb example 2023-07-19 12:41:29 +02:00
mfahampshire 40e1243f3c Merge branch 'feature/v1-1-24-docs' of github.com:nymtech/nym into feature/v1-1-24-docs 2023-07-11 15:52:59 +02:00
mfahampshire 50d2ca0a12 updated validator docs: upgrade to 0.32.0 instructions 2023-07-11 15:52:35 +02:00
mx 32d9baaf02 Merge pull request #3614 from twofaktor/twofaktor-requester-no-incoming-connection
[UPDATE] Fix + update network-requester-setup doc
2023-07-10 08:30:37 +00:00
mfahampshire 0179f7648c version bumps 2023-07-10 09:56:22 +02:00
mfahampshire 45c04d63e2 removed command information from mix node + gateway guide 2023-07-10 09:44:33 +02:00
⚡️2FakTor⚡️ 0f8ac1506b Update network-requester-setup.md 2023-06-28 16:07:32 +02:00
⚡️2FakTor⚡️ efdf27d1e9 Update network-requester-setup.md 2023-06-28 13:59:51 +02:00
62 changed files with 231 additions and 2113 deletions
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets[0].published_at) }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
@@ -31,10 +31,20 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: wasm32-unknown-unknown
- name: Install wasm-pack
run: |
export WASM_PACK_VERSION="v0.12.1"
curl -LO https://github.com/rustwasm/wasm-pack/releases/download/${WASM_PACK_VERSION}/wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz
tar xvzf wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz -C $HOME/.cargo/bin --strip-components=1
rm wasm-pack-${WASM_PACK_VERSION}-x86_64-apple-darwin.tar.gz
- name: Install the Apple developer certificate for code signing
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
@@ -105,9 +115,10 @@ jobs:
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_x64.dmg" >> "$GITHUB_OUTPUT"
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/dmg/nym-connect_*_x64.dmg') }}" >> "$GITHUB_OUTPUT"
push-release-data:
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets[0].published_at) }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
@@ -82,11 +82,11 @@ jobs:
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_amd64.AppImage" >> "$GITHUB_OUTPUT"
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${version}_amd64.AppImage" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/appimage/nym-connect_*_amd64.AppImage') }}" >> "$GITHUB_OUTPUT"
push-release-data:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-connect-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/push-release-data.yml
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets[0].published_at) }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
@@ -101,9 +101,10 @@ jobs:
- id: release-info
name: Prepare release info
run: |
semver="${${{ github.ref_name }}##nym-connect-}" && semver="${semver##v}"
echo "version=$semver" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_$version_x64_en-US.msi" >> "$GITHUB_OUTPUT"
ref=${{ github.ref_name }}
semver="${ref##nym-connect-}" && semver="${semver##v}"
echo "version=${semver}" >> "$GITHUB_OUTPUT"
echo "filename=nym-connect_${version}_x64_en-US.msi" >> "$GITHUB_OUTPUT"
echo "file_hash=${{ hashFiles('nym-connect/desktop/target/release/bundle/msi/nym-connect_*_x64_en-US.msi') }}" >> "$GITHUB_OUTPUT"
push-release-data:
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
@@ -19,7 +19,7 @@ jobs:
outputs:
release_id: ${{ steps.create-release.outputs.id }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].published_at }}
release_date: ${{ fromJSON(steps.create-release.outputs.assets)[0].created_at }}
version: ${{ steps.release-info.outputs.version }}
filename: ${{ steps.release-info.outputs.filename }}
file_hash: ${{ steps.release-info.outputs.file_hash }}
+4 -4
View File
@@ -1,6 +1,6 @@
[book]
title = "Nym Developer Portal"
authors = ["Max Hampshire"]
authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"]
language = "en"
multilingual = false
src = "src"
@@ -47,10 +47,10 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
# variables preprocessor: import variables into files
# https://gitlab.com/tglman/mdbook-variables/
[preprocessor.variables.variables]
# code prerequisites versions
minimum_rust_version = "1.66"
# TODO remove this in place of develop in next release
platform_release_version = "v1.1.21"
# vars for links: TODO think on how to streamline updating
platform_release_version = "v1.1.25"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
+3 -3
View File
@@ -1,6 +1,6 @@
[book]
title = "Nym Docs"
authors = ["Max Hampshire"]
authors = ["Max Hampshire, Serinko, Alexia Lorenza Martinel"]
description = "Nym technical documentation"
language = "en"
multilingual = false # for the moment - ideally work on chinese, brazillian, spanish next
@@ -49,8 +49,8 @@ assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
[preprocessor.variables.variables]
minimum_rust_version = "1.66"
# vars for links: TODO think on how to streamline updating
platform_release_version = "v1.1.22"
wallet_release_version = "v1.2.5"
platform_release_version = "v1.1.25"
wallet_release_version = "v1.2.7"
[preprocessor.last-changed]
command = "mdbook-last-changed"
@@ -153,8 +153,6 @@ Follow these steps to upgrade your binary and update its config file:
* re-run `init` with the same values as you used initially. **This will just update the config file, it will not overwrite existing keys**.
* restart your gateway process with the new binary.
> Do **not** use the `upgrade` command: there is a known error with the command that will be fixed in a subsequent release.
#### Step 2: updating your node information in the smart contract
Follow these steps to update the information about your node which is publically avaliable from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net).
@@ -191,8 +191,6 @@ Follow these steps to upgrade your mix node binary and update its config file:
* re-run `init` with the same values as you used initially. **This will just update the config file, it will not overwrite existing keys**.
* restart your mix node process with the new binary.
> Do **not** use the `upgrade` command: there is a known error with the command that will be fixed in a subsequent release.
#### Step 2: updating your node information in the smart contract
Follow these steps to update the information about your mix node which is publically avaliable from the [Nym API](https://validator.nymtech.net/api/swagger/index.html) and information displayed on the [mixnet explorer](https://explorer.nymtech.net).
@@ -8,7 +8,6 @@
<!-- cmdrun ../../../../target/release/nym-network-requester --version | grep "Build Version" | cut -b 21-26 -->
```
## Network Requester Whitelist
If you have access to a server, you can run the network requester, which allows Nym users to send outbound requests from their local machine through the mixnet to a server, which then makes the request on their behalf, shielding them (and their metadata) from clearnet, untrusted and unknown infrastructure, such as email or message client servers.
@@ -65,6 +64,34 @@ p2pify.com
2001:67c:4e8::/48
2001:b28:f23c::/48
2a0a:f280::/32
# nym matrix server
nymtech.chat
# generic matrix server backends
vector.im
matrix.org
# monero desktop - mainnet
212.83.175.67
212.83.172.165
176.9.0.187
88.198.163.90
95.217.25.101
136.244.105.131
104.238.221.81
66.85.74.134
88.99.173.38
51.79.173.165
# monero desktop - stagenet
162.210.173.150
176.9.0.187
88.99.173.38
51.79.173.165
# alephium
alephium.org
```
## Network Requester Directory
@@ -193,25 +220,32 @@ sudo ufw enable
sudo ufw status
```
Finally open your requester's p2p port, as well as ports for ssh and incoming traffic connections:
Finally open your requester's ssh port to incoming administration connections:
```
sudo ufw allow 22,9000/tcp
sudo ufw allow 22/tcp
# check the status of the firewall
sudo ufw status
```
For more information about your requester's port configuration, check the [requester port reference table](./network-requester-setup.md#requester-port-reference) below.
## Using your network requester
The next thing to do is use your requester, share its address with friends (or whoever you want to help privacy-enhance their app traffic). Is this safe to do? If it was an open proxy, this would be unsafe, because any Nym user could make network requests to any system on the internet.
To make things a bit less stressful for administrators, the Network Requester drops all incoming requests by default. In order for it to make requests, you need to add specific domains to the `allowed.list` file at `$HOME/.nym/service-providers/network-requester/allowed.list`.
### Global vs local allow lists
Your Network Requester will check for a domain against 2 lists before allowing traffic through for a particular domain or IP.
* The first list is the default list on the [nymtech.net server](https://nymtech.net/.wellknown/network-requester/standard-allowed-list.txt). Your Requester will not check against this list every time, but instead will keep a record of accepted domains in memory.
* The second is the local `allowed.list` file.
### Supporting custom domains with your network requester
It is easy to add new domains and services to your network requester - simply find out which endpoints (both URLs and raw IP addresses are supported) you need to whitelist, and then add these endpoints to your `allowed.list`.
> In order to keep things more organised, you can now use comments in the `allow.list` like the example at the top of this page.
How to go about this? Have a look in your nym-network-requester config directory:
```
@@ -249,7 +283,3 @@ This command should return the following:
### Requester port reference
All network-requester-specific port configuration can be found in `$HOME/.nym/service-providers/network-requester/<YOUR_ID>/config/config.toml`. If you do edit any port configs, remember to restart your client and requester processes.
| Default port | Use |
|--------------|---------------------------|
| 9000 | Listen for Client traffic |
+7 -2
View File
@@ -56,14 +56,19 @@ If you're integrating mixnet functionality into an existing app and want to inte
### Anonymous replies with SURBs
Both functions used to send messages through the mixnet (`send_str` and `send_bytes`) send a pre-determined number of SURBs along with their messages by default.
The number of SURBs is set [here](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/sdk/rust/nym-sdk/src/mixnet/client.rs#L35):
The number of SURBs is set [here](https://github.com/nymtech/nym/blob/release/{{platform_release_version}}/sdk/rust/nym-sdk/src/mixnet/client.rs#L34):
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/src/mixnet/client.rs:30}}
{{#include ../../../../sdk/rust/nym-sdk/src/mixnet/client.rs:34}}
```
You can read more about how SURBs function under the hood [here](../architecture/traffic-flow.md#private-replies-using-surbs).
In order to reply to an incoming message using SURBs, you can construct a `recipient` from the `sender_tag` sent along with the message you wish to reply to:
```rust,noplayground
{{#include ../../../../sdk/rust/nym-sdk/examples/surb-reply.rs}}
```
### Importing and using a custom network topology
If you want to send traffic through a sub-set of nodes (for instance, ones you control, or a small test setup) when developing, debugging, or peforming research, you will need to import these nodes as a custom network topology, instead of grabbing it from the [`Mainnet Nym-API`](https://validator.nymtech.net/api/swagger/index.html) (`examples/custom_topology_provider.rs`).
+8
View File
@@ -2,6 +2,14 @@
## [Unreleased]
## [v1.1.15] (2023-07-25)
- NC Desktop - remove sentry DSN from code ([#3694])
- NC - Add Alephium wallet in the supported app list ([#3681])
[#3694]: https://github.com/nymtech/nym/issues/3694
[#3681]: https://github.com/nymtech/nym/issues/3681
## [v1.1.14] (2023-07-04)
- Nym connect fails to start when encountering an old config version ([#3588])
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@nym/nym-connect",
"version": "1.1.13",
"version": "1.1.15",
"main": "index.js",
"license": "MIT",
"scripts": {
@@ -115,4 +115,4 @@
"webpack-merge": "^5.8.0",
"yaml-loader": "^0.8.0"
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "nym-connect"
version = "1.1.13"
version = "1.1.15"
description = "nym-connect"
authors = ["Nym Technologies SA"]
license = ""
@@ -1,7 +1,7 @@
{
"package": {
"productName": "nym-connect",
"version": "1.1.14"
"version": "1.1.15"
},
"build": {
"distDir": "../dist",
@@ -86,4 +86,4 @@
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'"
}
}
}
}
+1 -2
View File
@@ -30,8 +30,7 @@
"types:lint:fix": "lerna run lint:fix --scope @nymproject/types --scope @nymproject/nym-wallet-app",
"audit:fix": "npm_config_yes=true npx yarn-audit-fix -- --dry-run",
"preinstall": "yarn install:copy-placeholders && yarn install:copy-storage-placeholders",
"install:copy-placeholders": "cp scripts/build/yarn/wasm-placeholder/package.json sdk/typescript/packages/nym-client-wasm",
"install:copy-storage-placeholders": "mkdir -p nym-browser-extension/storage/pkg && cp scripts/build/yarn/storage-placeholder/package.json nym-browser-extension/storage/pkg"
"install:copy-placeholders": "cp scripts/build/yarn/wasm-placeholder/package.json sdk/typescript/packages/nym-client-wasm"
},
"devDependencies": {
"lerna": "^6.6.2",
+66
View File
@@ -0,0 +1,66 @@
use nym_sdk::mixnet::{
AnonymousSenderTag, MixnetClientBuilder, ReconstructedMessage, StoragePaths,
};
use std::path::PathBuf;
#[tokio::main]
async fn main() {
nym_bin_common::logging::setup_logging();
// Specify some config options
let config_dir = PathBuf::from("/tmp/surb-example");
let storage_paths = StoragePaths::new_from_dir(&config_dir).unwrap();
// Create the client with a storage backend, and enable it by giving it some paths. If keys
// exists at these paths, they will be loaded, otherwise they will be generated.
let client = MixnetClientBuilder::new_with_default_storage(storage_paths)
.await
.unwrap()
.build()
.await
.unwrap();
// Now we connect to the mixnet, using keys now stored in the paths provided.
let mut client = client.connect_to_mixnet().await.unwrap();
// Be able to get our client address
let our_address = client.nym_address();
println!("\nOur client nym address is: {our_address}");
// Send a message through the mixnet to ourselves using our nym address
client.send_str(*our_address, "hello there").await;
// we're going to parse the sender_tag (AnonymousSenderTag) from the incoming message and use it to 'reply' to ourselves instead of our Nym address.
// we know there will be a sender_tag since the sdk sends SURBs along with messages by default.
println!("Waiting for message\n");
// get the actual message - discard the empty vec sent along with a potential SURB topup request
let mut message: Vec<ReconstructedMessage> = Vec::new();
while let Some(new_message) = client.wait_for_messages().await {
if new_message.is_empty() {
continue;
}
message = new_message;
break;
}
let mut parsed = String::new();
if let Some(r) = message.first() {
parsed = String::from_utf8(r.message.clone()).unwrap();
}
// parse sender_tag: we will use this to reply to sender without needing their Nym address
let return_recipient: AnonymousSenderTag = message[0].sender_tag.unwrap();
println!(
"\nReceived the following message: {} \nfrom sender with surb bucket {}",
parsed, return_recipient
);
// reply to self with it: note we use `send_str_reply` instead of `send_str`
println!("Replying with using SURBs");
client.send_str_reply(return_recipient, "hi an0n!").await;
println!("Waiting for message (once you see it, ctrl-c to exit)\n");
client
.on_messages(|msg| println!("\nReceived: {}", String::from_utf8_lossy(&msg.message)))
.await;
}
@@ -1,22 +0,0 @@
# Nym Chrome Extension Example
This is an example of how Nym can be used within the context of a Chrome extension.
## Running the example
1. Copy a build of the Nym TypeScript SDK (ESM version) into `./sdk`.
2. Navigate to `chrome://extensions` in Google Chrome.
3. Enable "Developer mode" (top right of the page).
4. Click on "Load unpacked" (top left of the page).
5. Load this extension folder.
## How does it work?
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
![Sphinx packet](../docs/worker.svg)
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
![Sphinx packet](../docs/sphinx.svg)
@@ -1,17 +0,0 @@
{
"name": "Nym Chrome Extension Example",
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Google Chrome browser extension.",
"version": "1.0",
"manifest_version": 3,
"icons": {
"48": "icon.png"
},
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"action": {
"default_title": "Nym Chrome Extension Example",
"default_icon": "icon.png",
"default_popup": "popup.html"
}
}
@@ -1,20 +0,0 @@
{
"name": "chrome-extension",
"version": "1.0.0",
"description": "This is an example of how Nym can be used within the context of a Chrome extension.",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@nymproject/sdk": "^1.1.8"
}
}
@@ -1,8 +0,0 @@
body {
width: 800px;
min-height: 400px;
}
#editdialog input {
width: 100%;
}
@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script type="module" src="main.js"></script>
</head>
<body>
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value="" /></p>
<p><label>Recipient:</label><input size="85" id="recipient" value="" /></p>
<p><label>Message:</label><input id="message" value="Hello mixnet!" /></p>
<p><button id="send-button">Send</button></p>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p>
<span style="color: blue">Sent</span> messages show in blue, <span style="color: green">received</span> messages
show in green.
</p>
<hr />
<p></p>
<div id="output"></div>
<p></p>
</body>
</html>
@@ -1,66 +0,0 @@
// dom-utils.js
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
*
* Message and recipient are taken from the values in the user interface.
*
* @param {Client} nymClient the nym client to use for message sending
*/
async function sendMessageTo(nym) {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
await nym.client.send({
payload: {
message,
mimeType: 'text/plain'
},
recipient
});
displaySend(message);
}
/**
* Display messages that have been sent up the websocket. Colours them blue.
*
* @param {string} message
*/
function displaySend(message) {
const timestamp = new Date().toISOString().substr(11, 12);
const sendDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: blue');
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
paragraph.appendChild(paragraphContent);
sendDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(sendDiv);
}
/**
* Display received text messages in the browser. Colour them green.
*
* @param {string} message
*/
function displayReceived(message) {
const content = message;
const timestamp = new Date().toLocaleTimeString();
const receivedDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: green');
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
paragraph.appendChild(paragraphContent);
receivedDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(receivedDiv);
}
/**
* Display the nymClient's sender address in the user interface
*
* @param {Client} nymClient
*/
function displaySenderAddress(address) {
document.getElementById('sender').value = address;
}
export { sendMessageTo, displaySend, displayReceived , displaySenderAddress }
@@ -1,53 +0,0 @@
// main.js
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
// Look at dom-utils.js for the DOM utility functionality referenced here.
// Import the Nym mixnet ESM module.
import { createNymMixnetClient } from '@nymproject/sdk';
// Import the DOM utility functionality.
import { displaySenderAddress, displayReceived, sendMessageTo } from './dom-utils.js';
async function main() {
// Initialize the Nym mixnet client.
let nymClient = await createNymMixnetClient();
if (!nymClient) {
console.error('Oh no! Could not create client');
return;
}
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nymClient.events.subscribeToConnected((e) => {
if (e.args.address) {
displaySenderAddress(e.args.address);
}
});
// subscribe to message received events and show any string messages received
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
displayReceived(e.args.payload);
});
const sendButton = document.querySelector('#send-button');
if (sendButton) {
sendButton.onclick = function () {
if (nymClient) {
sendMessageTo(nymClient);
}
};
}
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
await nymClient.client.start({
clientId: 'My awesome client',
nymApiUrl,
preferredGatewayIdentityKey,
});
}
window.addEventListener('DOMContentLoaded', () => {
main();
});
@@ -1,25 +0,0 @@
// Webpack configuration for the Chrome extension example
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
main: './src/main.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
'manifest.json',
'popup.html',
{ from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' },
],
}),
],
};
@@ -1 +0,0 @@
sdk/index.js
@@ -1,35 +0,0 @@
# Nym Firefox Extension Example
This is an example of how Nym can be used within the context of a Mozilla Firefox extension.
## Running the example
First, build the Nym SDK:
From the SDK directory `sdk/typescript/packages/sdk` run:
```js
npm run build:local
```
Then, from the example directory `sdk/typescript/examples/firefox-extension` run:
```js
npm install
npm run build
```
## Workers
Firefox browser extensions cannot run inline web workers. In order to overcome this limitation, the Nym Firefox Extension Example imports workers from the SDK and uses Webpack's `worker-loader` to allow the worker's to be bundled inline into the extension. In order for webpack to include the workers in the build, they are imported as modules in the `src/index.js` file:
## How does it work?
The Nym Mixnet Client runs a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) that wraps
a WASM library that builds and encrypts Sphinx packets in the browser to send over the Nym mixnet:
![Sphinx packet](../docs/worker.svg)
The WASM code encrypts each layer of the Sphinx packet in the browser, before sending the Sphinx packet over a websocket to the ingress gateway:
![Sphinx packet](../docs/sphinx.svg)
@@ -1,23 +0,0 @@
{
"manifest_version": 3,
"name": "Nym Firefox Extension Example",
"version": "1.0",
"description": "An example demonstrating how to integrate the Nym TypeScript SDK in the context of a Mozilla Firefox browser extension.",
"icons": {
"48": "icon.png"
},
"permissions": [],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval';"
},
"background": {
"scripts": ["background.js"]
},
"action": {
"default_icon": {
"32": "icon.png"
},
"default_title": "Nym Firefox Extension Example",
"default_popup": "popup.html"
}
}
@@ -1,19 +0,0 @@
{
"name": "firefox-extension",
"version": "1.0.0",
"description": "This is an example of how Nym can be used within the context of a Firefox extension.",
"main": "index.js",
"author": "",
"license": "ISC",
"devDependencies": {
"copy-webpack-plugin": "^11.0.0",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"scripts": {
"build": "yarn webpack"
},
"dependencies": {
"worker-loader": "^3.0.8"
}
}
@@ -1,8 +0,0 @@
body {
width: 800px;
min-height: 400px;
}
#editdialog input {
width: 100%;
}
@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="popup.css" />
<script type="module" src="popup.js"></script>
</head>
<body>
<p><label>Sender:</label><input disabled="true" size="85" id="sender" value="" /></p>
<p><label>Recipient:</label><input size="85" id="recipient" value="" /></p>
<p><label>Message:</label><input id="message" value="Hello mixnet!" /></p>
<p><button id="send-button">Send</button></p>
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
<p>
<span style="color: blue">Sent</span> messages show in blue, <span style="color: green">received</span> messages
show in green.
</p>
<hr />
<p></p>
<div id="output"></div>
<p></p>
</body>
</html>
@@ -1,107 +0,0 @@
// main.js
// Simple example of how to load Nym's TypeScript SDK and bind it to a DOM.
// Look at dom-utils.js for the DOM utility functionality referenced here.
// Import the Nym mixnet ESM module.
// Import The web workers for the Nym mixnet ESM module.These are required for to run the Nym mixnet client.
import { createNymMixnetClient } from '../../../packages/sdk/dist/full-fat/index.js';
import '../../../packages/sdk/dist/full-fat/web-worker-0.js';
import '../../../packages/sdk/dist/full-fat/web-worker-1.js';
const backgroundState = {
isReady: false,
address: '',
recipient: '',
messageLog: [],
};
async function initBackground() {
// Initialize the Nym mixnet client.
let nymClient = await createNymMixnetClient().catch((err) => {
console.log(err);
});
if (!nymClient) {
console.error('Oh no! Could not create client');
return;
}
const nymApiUrl = 'https://validator.nymtech.net/api';
const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
// subscribe to connect event, so that we can show the client's address
nymClient.events.subscribeToConnected((e) => {
if (e.args.address) {
backgroundState.address = e.args.address;
browser.runtime.sendMessage({
type: 'displaySenderAddress',
message: backgroundState.address,
});
}
});
// subscribe to message received events and show any string messages received
nymClient.events.subscribeToTextMessageReceivedEvent((e) => {
backgroundState.messageLog.push({
type: 'received',
message: e.args.payload,
});
browser.runtime.sendMessage({
type: 'displayReceived',
message: e.args.payload,
});
});
nymClient.events.subscribeToRawMessageReceivedEvent((e) => console.log('Received: ', e.args.payload));
await nymClient.client.start({
clientId: 'My awesome client',
nymApiUrl,
preferredGatewayIdentityKey,
});
browser.runtime.onMessage.addListener(async (data) => {
switch (data.type) {
case 'nymClientSendMessage':
if (nymClient) {
await nymClient.client.send({
payload: {
message: data.message,
mimeType: 'text/plain',
},
recipient: data.recipient,
});
backgroundState.messageLog.push({
type: 'sent',
message: data.message,
});
break;
}
}
});
backgroundState.isReady = true;
}
window.addEventListener('DOMContentLoaded', () => {
browser.runtime.onMessage.addListener((data) => {
switch (data.type) {
case 'popupReady':
if (backgroundState.isReady) {
browser.runtime.sendMessage({
type: 'displaySenderAddress',
message: backgroundState.address,
});
browser.runtime.sendMessage({
type: 'displayMessageLog',
message: backgroundState.messageLog,
});
browser.runtime.sendMessage({
type: 'updateRecipient',
message: backgroundState.recipient,
});
} else {
initBackground();
}
break;
case 'updateRecipient':
backgroundState.recipient = data.message;
}
});
});
@@ -1,74 +0,0 @@
// dom-utils.js
// Contains utility functionality to help manipulate the DOM elements necessary to demonstrate the Nym example.
/**
* Create a Sphinx packet and send it to the mixnet through the gateway node.
*
* Message and recipient are taken from the values in the user interface.
*
* @param {Client} nymClient the nym client to use for message sending
*/
async function sendMessageTo() {
const message = document.getElementById('message').value;
const recipient = document.getElementById('recipient').value;
browser.runtime.sendMessage({
type: 'nymClientSendMessage',
message,
recipient,
});
displaySend(message);
}
/**
* Display messages that have been sent up the websocket. Colours them blue.
*
* @param {string} message
*/
function displaySend(message) {
const timestamp = new Date().toISOString().substr(11, 12);
const sendDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: blue');
const paragraphContent = document.createTextNode(`${timestamp} sent >>> ${message}`);
paragraph.appendChild(paragraphContent);
sendDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(sendDiv);
}
/**
* Display received text messages in the browser. Colour them green.
*
* @param {string} message
*/
function displayReceived(message) {
const content = message;
const timestamp = new Date().toLocaleTimeString();
const receivedDiv = document.createElement('div');
const paragraph = document.createElement('p');
paragraph.setAttribute('style', 'color: green');
const paragraphContent = document.createTextNode(`${timestamp} received >>> ${content}`);
paragraph.appendChild(paragraphContent);
receivedDiv.appendChild(paragraph);
document.getElementById('output')?.appendChild(receivedDiv);
}
/**
* Display the nymClient's sender address in the user interface
*
* @param {Client} nymClient
*/
function displaySenderAddress(address) {
document.getElementById('sender').value = address;
}
function displayMessageLog(messageLog) {
for (let i = 0; i < messageLog.length; i++) {
if (messageLog[i].type === 'sent') {
displaySend(messageLog[i].message);
} else if (messageLog[i].type === 'received') {
displayReceived(messageLog[i].message);
}
}
}
export { sendMessageTo, displaySend, displayReceived, displaySenderAddress, displayMessageLog };
@@ -1,40 +0,0 @@
// Import the DOM utility functionality.
import { displaySenderAddress, displayReceived, sendMessageTo, displayMessageLog } from './dom-utils.js';
window.addEventListener('DOMContentLoaded', () => {
const sendButton = document.querySelector('#send-button');
if (sendButton) {
sendButton.onclick = function () {
sendMessageTo();
};
}
const recipient = document.getElementById('recipient');
recipient.onchange = () => {
browser.runtime.sendMessage({
type: 'updateRecipient',
message: recipient.value,
});
};
browser.runtime.onMessage.addListener((data) => {
switch (data.type) {
case 'displaySenderAddress':
displaySenderAddress(data.message);
break;
case 'displayReceived':
displayReceived(data.message);
break;
case 'sendMessageTo':
sendMessageTo(data.message);
break;
case 'displayMessageLog':
displayMessageLog(data.message);
break;
case 'updateRecipient':
recipient.value = data.message;
}
});
browser.runtime.sendMessage({
type: 'popupReady',
message: '',
});
});
@@ -1,38 +0,0 @@
// Webpack configuration for the Firefox extension example
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
background: './src/background.js',
popup: './src/popup.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
'manifest.json',
'popup.html',
{ from: path.resolve(__dirname, '../../../../assets/favicon/favicon.png'), to: 'icon.png' },
],
}),
],
module: {
rules: [
{
test: /web-worker.*\.js$/,
loader: 'worker-loader',
options: {
filename: '[name].js',
inline: 'fallback',
},
},
],
},
};
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nym Node Tester Demo</title>
<title>Nym WebAssembly Demo</title>
</head>
<body>
@@ -0,0 +1,3 @@
{
"presets": ["@babel/env", "@babel/react"]
}
@@ -0,0 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nym Node Tester Demo</title>
<title>Nym WebAssembly Demo</title>
</head>
<body>
@@ -1,14 +0,0 @@
# Nym Node Tester - React
This is an example of using the Nym Mixnet node tester.
You can use this example as a seed for a new project.
## Running the example
Try out the node tester app by running:
```
npm install
npm start
```
@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="../../../../../assets/favicon/favicon.png" />
<title>Nym Node Tester Example</title>
</head>
<body>
<div id="root"></div>
<script src="src/index.tsx" type="module"></script>
</body>
</html>
@@ -1,19 +0,0 @@
{
"name": "@nymproject/sdk-example-node-tester-react",
"description": "An example project that uses WASM node tester and React",
"version": "1.0.0",
"license": "Apache-2.0",
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.0",
"@mui/material": "^5.14.0",
"@nymproject/sdk": "1",
"parcel": "^2.9.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"scripts": {
"start": "parcel index.html"
}
}
@@ -1,127 +0,0 @@
import React, { useState } from 'react';
import {
Button,
Card,
CardActions,
CardContent,
CardHeader,
CircularProgress,
Grid,
List,
ListItem,
ListItemText,
TextField,
Typography,
} from '@mui/material';
import { NodeTestResultResponse } from '@nymproject/sdk';
import { ScoreIndicator } from 'src/components/ScoreIndicator';
import { useNodeTesterClient } from 'src/hooks/useNodeTesterClient';
import { BasicPageLayout } from 'src/layouts';
import { TestStatusLabel } from 'src/components/TestStatusLabel';
import Icon from '../../../../../../assets/appicon/appicon.png';
export const App = () => {
const { testState, error, testNode, disconnectFromGateway, reconnectToGateway } = useNodeTesterClient();
const [mixnodeIdentity, setMixnodeIdentity] = useState<string>('');
const [results, setResults] = React.useState<NodeTestResultResponse>();
console.log({ testState, error, testNode });
const handleTestNode = async () => {
setResults(undefined);
try {
const result = await testNode(mixnodeIdentity);
setResults(result);
} catch (e) {
console.error(e);
}
};
return (
<BasicPageLayout>
<Card variant="outlined" sx={{ mt: 15, p: 4 }}>
<CardHeader
title={<Typography variant="h6">Nym Mixnode Testnet Node Tester</Typography>}
action={<TestStatusLabel state={testState} />}
avatar={<img src={Icon} width={40} />}
/>
<CardContent sx={{ mb: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<ScoreIndicator score={results?.score || 0} />
</Grid>
<Grid item xs={12} sm={6}>
<List>
<ListItem>
<ListItemText primary="Packets sent" secondary={results?.sentPackets.toString() || '-'} />
</ListItem>
<ListItem>
<ListItemText primary="Packets received" secondary={results?.receivedPackets.toString() || '-'} />
</ListItem>
<ListItem>
<ListItemText
primary="Duplicate packets received"
secondary={results?.duplicatePackets.toString() || '-'}
/>
</ListItem>
</List>
</Grid>
</Grid>
</CardContent>
<CardActions>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
label="Enter a Mixnode Identity to test"
value={mixnodeIdentity}
onChange={(e) => {
setMixnodeIdentity(e.target.value);
}}
fullWidth
/>
</Grid>
<Grid item xs={12} sm={4}>
<Button
disabled={!disconnectFromGateway || testState === 'Disconnected' || testState === 'Testing'}
onClick={disconnectFromGateway}
variant="outlined"
disableElevation
size="large"
fullWidth
sx={{ mr: 2 }}
>
Disconnect
</Button>
</Grid>
<Grid item xs={12} sm={4}>
<Button
disabled={!reconnectToGateway || testState === 'Ready' || testState === 'Testing'}
onClick={reconnectToGateway}
variant="outlined"
disableElevation
size="large"
fullWidth
sx={{ mr: 2 }}
>
Reconnect
</Button>
</Grid>
<Grid item xs={12} sm={4}>
<Button
disabled={!testNode || !mixnodeIdentity || testState === 'Testing' || testState === 'Disconnected'}
onClick={handleTestNode}
variant="contained"
disableElevation
fullWidth
size="large"
endIcon={testState === 'Testing' && <CircularProgress size={25} />}
>
Start test
</Button>
</Grid>
</Grid>
</CardActions>
</Card>
</BasicPageLayout>
);
};
@@ -1,61 +0,0 @@
import React from 'react';
import { Box, CircularProgress, CircularProgressProps, Stack, Typography } from '@mui/material';
const getPerformanceDescriptionAndColor = (score: number) => {
const res: { description: string; color: CircularProgressProps['color'] } = { description: '', color: 'warning' };
if (score >= 90) {
res.description = 'Reliable node';
res.color = 'success';
}
if (score >= 75 && score < 90) {
res.description = 'Average node';
res.color = 'warning';
}
if (score > 0 && score < 75) {
res.description = 'Unreliable node';
res.color = 'error';
}
return res;
};
export const ScoreIndicator = ({ score }) => {
const { color } = getPerformanceDescriptionAndColor(score);
return (
<Box
sx={{
display: 'flex',
position: 'relative',
width: 250,
height: 250,
justifyContent: 'center',
alignItems: 'center',
mx: 'auto',
mt: 4,
}}
>
<CircularProgress
variant="determinate"
value={100}
size={250}
sx={{ position: 'absolute', top: 0, left: 0, color: 'grey.200' }}
/>
<CircularProgress
variant="determinate"
value={score}
size={250}
sx={{ position: 'absolute', top: 0, left: 0 }}
color={color}
/>
<Stack alignItems="center" gap={1}>
<Typography fontWeight="bold" variant="h4">
{Math.round(score)}%
</Typography>
<Typography>Performance Score</Typography>
</Stack>
</Box>
);
};
@@ -1,34 +0,0 @@
import React from 'react';
import { Chip } from '@mui/material';
import { HourglassTop, ErrorOutline, CheckCircleOutline, WarningAmber } from '@mui/icons-material';
import { TestState } from 'src/hooks/useNodeTesterClient';
const getColor = (state: TestState) => {
switch (state) {
case 'Connecting':
return 'warning';
case 'Error':
return 'error';
case 'Ready':
return 'success';
default:
return 'warning';
}
};
const getIcon = (state: TestState) => {
switch (state) {
case 'Connecting':
return <HourglassTop />;
case 'Error':
return <ErrorOutline />;
case 'Ready':
return <CheckCircleOutline />;
default:
return <WarningAmber />;
}
};
export const TestStatusLabel = ({ state }: { state: TestState }) => (
<Chip label={state} color={getColor(state)} icon={getIcon(state)} sx={{ color: 'white' }} />
);
@@ -1,71 +0,0 @@
import { useState, useEffect } from 'react';
import { createNodeTesterClient, NodeTester } from '@nymproject/sdk';
export type TestState = 'Ready' | 'Connecting' | 'Disconnected' | 'Disconnecting' | 'Error' | 'Testing' | 'Stopped';
export const useNodeTesterClient = () => {
const [client, setClient] = useState<NodeTester>();
const [error, setError] = useState<string>();
const [testState, setTestState] = useState<TestState>('Disconnected');
const createClient = async () => {
setTestState('Connecting');
try {
const validator = 'https://validator.nymtech.net/api';
const nodeTesterClient = await createNodeTesterClient();
await nodeTesterClient.tester.init(validator);
setClient(nodeTesterClient);
} catch (e) {
console.log(e);
setError('Failed to load node tester client, please try again');
} finally {
setTestState('Ready');
}
};
useEffect(() => {
createClient();
}, []);
const testNode = !client
? undefined
: async (mixnodeIdentity: string) => {
try {
setTestState('Testing');
const result = await client.tester.startTest(mixnodeIdentity);
setTestState('Ready');
return result;
} catch (e) {
console.log(e);
setError('Failed to test node, please try again');
setTestState('Error');
}
};
const disconnectFromGateway = !client
? undefined
: async () => {
setTestState('Disconnecting');
await client.tester.disconnectFromGateway();
setTestState('Disconnected');
};
const reconnectToGateway = !client
? undefined
: async () => {
setTestState('Connecting');
await client.tester.reconnectToGateway();
setTestState('Ready');
};
const terminateWorker = !client
? undefined
: async () => {
setTestState('Disconnecting');
await client.terminate();
setTestState('Disconnected');
};
return { testNode, disconnectFromGateway, reconnectToGateway, terminateWorker, testState, error };
};
@@ -1,14 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
import { NymThemeProvider } from './theme/theme';
const rootDOMElem = document.getElementById('root');
if (!rootDOMElem) throw new Error('Root element not found');
const root = createRoot(rootDOMElem);
root.render(
<NymThemeProvider>
<App />
</NymThemeProvider>,
);
@@ -1,6 +0,0 @@
import React from 'react';
import { Container } from '@mui/material';
export const BasicPageLayout = ({ children }: { children: React.ReactNode }) => (
<Container maxWidth="md">{children}</Container>
);
@@ -1,28 +0,0 @@
import * as React from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
export const NymThemeProvider = ({ children }: { children: React.ReactNode }) => {
const theme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#FB6E4E',
},
success: {
main: '#21D073',
},
background: {
default: '#1D2125',
paper: '#292E34',
},
},
});
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
);
};
@@ -1,4 +0,0 @@
declare module '*.png' {
const content: any;
export default content;
}
@@ -1,16 +0,0 @@
{
"compilerOptions": {
"jsx": "react",
"baseUrl": ".",
"paths": {
"react": ["node_modules/react"],
"react-dom": ["node_modules/react-dom"]
},
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"include": ["src"],
"paths": {
"@assets/*": ["../../../../../../assets"]
}
}
-3
View File
@@ -1,3 +0,0 @@
{
"presets": ["@babel/env"]
}
@@ -1,15 +0,0 @@
import preset from 'ts-jest/presets/index.js'
/** @type {import('ts-jest').JestConfigWithTsJest} */
export default {
...preset.defaults,
transform: {
'^.+\\.(ts|tsx)$': [
'ts-jest',
{
tsconfig: 'tsconfig.jest.json',
useESM: true,
},
],
},
}
+3 -14
View File
@@ -27,22 +27,16 @@
"prebuild:dev": "yarn build:dependencies",
"build:dev": "yarn build:dev:only-this",
"build:dev:only-this": "scripts/build.sh",
"build:local": "run-s build:dependencies:nym-client-wasm build:dev:only-this",
"test": "node --experimental-vm-modules --no-warnings node_modules/jest/bin/jest.js -c=jest.config.mjs --no-cache"
"build:local": "run-s build:dependencies:nym-client-wasm build:dev:only-this"
},
"dependencies": {
"@npmcli/node-gyp": "^3.0.0",
"@nymproject/nym-client-wasm": "1.0.0",
"@nymproject/nym-client-wasm": "1",
"comlink": "^4.3.1",
"lerna": "^6.6.2",
"node-gyp": "^9.3.1"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/plugin-transform-async-to-generator": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-inject": "^5.0.3",
@@ -54,8 +48,6 @@
"@rollup/plugin-wasm": "^6.1.1",
"@typescript-eslint/eslint-plugin": "^5.13.0",
"@typescript-eslint/parser": "^5.13.0",
"@types/jest": "^27.0.1",
"@types/node": "^16.7.13",
"eslint": "^8.10.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
@@ -71,9 +63,6 @@
"rollup": "^3.9.1",
"rollup-plugin-base64": "^1.0.1",
"rollup-plugin-web-worker-loader": "^1.6.1",
"typescript": "^4.8.4",
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2"
"typescript": "^4.8.4"
}
}
@@ -1,28 +0,0 @@
// This is the rollup config for the full-fat SDK package.
// The config is similar to the esm config, but exports web workers as separate files.
// This can be necessary for implentations that do not support inline web workers.
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import webWorkerLoader from 'rollup-plugin-web-worker-loader';
const extensions = ['.js', '.jsx', '.ts', '.tsx'];
export default {
input: 'src/index.ts',
output: {
dir: 'dist/full-fat',
format: 'es',
},
plugins: [
webWorkerLoader({
targetPlatform: 'browser',
inline: false,
}),
resolve({ extensions }),
typescript({
exclude: ['mixnet/wasm/worker.ts', 'mixnet/node-tester/worker.ts'],
compilerOptions: { outDir: 'dist/full-fat' },
}),
],
};
@@ -48,13 +48,6 @@ rollup -c rollup-esm.config.mjs
# build the SDK as a CommonJS bundle
rollup -c rollup-cjs.config.mjs
#-------------------------------------------------------
# FULL FAT
#-------------------------------------------------------
# build the SDK as a ESM bundle
rollup -c rollup-full-fat.config.mjs
#-------------------------------------------------------
# CLEAN UP
#-------------------------------------------------------
@@ -3,16 +3,16 @@ import InlineWasmWebWorker from 'web-worker:./worker';
import {
BinaryMessageReceivedEvent,
ConnectedEvent,
EventHandlerFn,
EventKinds,
IWebWorker,
IWebWorkerAsync,
IWebWorkerEvents,
LoadedEvent,
MimeTypes,
RawMessageReceivedEvent,
StringMessageReceivedEvent,
RawMessageReceivedEvent,
} from './types';
import { createSubscriptions } from './subscriptions';
/**
* Client for the Nym mixnet.
@@ -33,13 +33,25 @@ export const createNymMixnetClient = async (options?: {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
const worker = await createWorker();
const subscriptions = createSubscriptions();
const { getSubscriptions, addSubscription } = subscriptions;
// stores the subscriptions for events
const subscriptions: {
[key: string]: Array<EventHandlerFn<unknown>>;
} = {};
/**
* Helper method to get typed subscriptions
*/
const getSubscriptions = <E>(key: EventKinds): Array<EventHandlerFn<E>> => {
if (!subscriptions[key]) {
subscriptions[key] = [];
}
return subscriptions[key] as Array<EventHandlerFn<E>>;
};
// listen to messages from the worker, parse them and let the subscribers handle them, catching any unhandled exceptions
worker.addEventListener('message', (msg) => {
if (msg.data && msg.data.kind) {
const subscribers = getSubscriptions(msg.data.kind);
const subscribers = subscriptions[msg.data.kind];
(subscribers || []).forEach((s) => {
try {
// let the subscriber handle the message
@@ -54,14 +66,36 @@ export const createNymMixnetClient = async (options?: {
// manage the subscribers, returning self-unsubscribe methods
const events: IWebWorkerEvents = {
subscribeToConnected: (handler) => addSubscription<ConnectedEvent>(EventKinds.Connected, handler),
subscribeToLoaded: (handler) => addSubscription<LoadedEvent>(EventKinds.Loaded, handler),
subscribeToTextMessageReceivedEvent: (handler) =>
addSubscription<StringMessageReceivedEvent>(EventKinds.StringMessageReceived, handler),
subscribeToBinaryMessageReceivedEvent: (handler) =>
addSubscription<BinaryMessageReceivedEvent>(EventKinds.BinaryMessageReceived, handler),
subscribeToRawMessageReceivedEvent: (handler) =>
addSubscription<RawMessageReceivedEvent>(EventKinds.RawMessageReceived, handler),
subscribeToConnected: (handler) => {
getSubscriptions<ConnectedEvent>(EventKinds.Connected).push(handler);
return () => {
getSubscriptions<ConnectedEvent>(EventKinds.Connected).unshift(handler);
};
},
subscribeToLoaded: (handler) => {
getSubscriptions<LoadedEvent>(EventKinds.Loaded).push(handler);
return () => {
getSubscriptions<LoadedEvent>(EventKinds.Loaded).unshift(handler);
};
},
subscribeToTextMessageReceivedEvent: (handler) => {
getSubscriptions<StringMessageReceivedEvent>(EventKinds.StringMessageReceived).push(handler);
return () => {
getSubscriptions<StringMessageReceivedEvent>(EventKinds.StringMessageReceived).unshift(handler);
};
},
subscribeToBinaryMessageReceivedEvent: (handler) => {
getSubscriptions<BinaryMessageReceivedEvent>(EventKinds.BinaryMessageReceived).push(handler);
return () => {
getSubscriptions<BinaryMessageReceivedEvent>(EventKinds.BinaryMessageReceived).unshift(handler);
};
},
subscribeToRawMessageReceivedEvent: (handler) => {
getSubscriptions<RawMessageReceivedEvent>(EventKinds.RawMessageReceived).push(handler);
return () => {
getSubscriptions<RawMessageReceivedEvent>(EventKinds.RawMessageReceived).unshift(handler);
};
},
};
// let comlink handle interop with the web worker
@@ -1,98 +0,0 @@
import { createSubscriptions } from './subscriptions';
import { EventKinds, MimeTypes, StringMessageReceivedEvent } from './types';
describe('wasm subscription manager', () => {
test('works with default values', () => {
const { getSubscriptions, fireEvent, addSubscription } = createSubscriptions();
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0);
// the event should fire and not fail
fireEvent(EventKinds.StringMessageReceived, {});
// mock a handler, fire events and check that it was called
const mockHandler = jest.fn();
addSubscription(EventKinds.StringMessageReceived, mockHandler);
fireEvent(EventKinds.StringMessageReceived, {});
expect(mockHandler).toHaveBeenCalled();
});
test('adding and removing subscriptions works as expected', () => {
const { addSubscription, getSubscriptions, fireEvent } = createSubscriptions();
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0);
const callStats: number[] = [0, 0, 0];
const showDebug = false;
const handler1 = (e: StringMessageReceivedEvent) => {
if (showDebug) {
console.log('handler1', e);
}
callStats[0] += 1;
};
const handler2 = (e: StringMessageReceivedEvent) => {
if (showDebug) {
console.log('handler2', e);
}
callStats[1] += 1;
};
const handler3 = (e: StringMessageReceivedEvent) => {
if (showDebug) {
console.log('handler3', e);
}
callStats[2] += 1;
};
const unsubcribeFn1 = addSubscription(EventKinds.StringMessageReceived, handler1);
const unsubcribeFn2 = addSubscription(EventKinds.StringMessageReceived, handler2);
const unsubcribeFn3 = addSubscription(EventKinds.StringMessageReceived, handler3);
const event: StringMessageReceivedEvent = {
kind: EventKinds.StringMessageReceived,
args: {
payload: 'Testing',
mimeType: MimeTypes.TextPlain,
payloadRaw: new Uint8Array(),
},
};
// fire and expect all handlers to get message
fireEvent(EventKinds.StringMessageReceived, event);
expect(callStats[0]).toBe(1);
expect(callStats[1]).toBe(1);
expect(callStats[2]).toBe(1);
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(3);
// unscribe and fire again
unsubcribeFn2();
fireEvent(EventKinds.StringMessageReceived, event);
expect(callStats[0]).toBe(2);
expect(callStats[1]).toBe(1);
expect(callStats[2]).toBe(2);
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(2);
// unscribe and fire again
unsubcribeFn3();
fireEvent(EventKinds.StringMessageReceived, event);
expect(callStats[0]).toBe(3);
expect(callStats[1]).toBe(1);
expect(callStats[2]).toBe(2);
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(1);
// unscribe and fire again
unsubcribeFn1();
fireEvent(EventKinds.StringMessageReceived, event);
expect(callStats[0]).toBe(3);
expect(callStats[1]).toBe(1);
expect(callStats[2]).toBe(2);
expect(getSubscriptions(EventKinds.StringMessageReceived)).toHaveLength(0);
// nothing is subscribed, so fire again and check
fireEvent(EventKinds.StringMessageReceived, event);
expect(callStats[0]).toBe(3);
expect(callStats[1]).toBe(1);
expect(callStats[2]).toBe(2);
});
});
@@ -1,69 +0,0 @@
import type { EventHandlerFn } from './types';
import { EventKinds } from './types';
type ISubscriptions = {
[key: string]: Array<EventHandlerFn<unknown>>;
};
/**
* Creates a subscription manager.
*/
export const createSubscriptions = () => {
// stores the subscriptions for events
const subscriptions: ISubscriptions = {};
/**
* Helper method to get typed subscriptions.
*/
const getSubscriptions = <E>(key: EventKinds): Array<EventHandlerFn<E>> => {
if (!subscriptions[key]) {
subscriptions[key] = [];
}
return subscriptions[key] as Array<EventHandlerFn<E>>;
};
/**
* Remove a subscription.
*/
const removeSubscription = <E>(key: EventKinds, handler: EventHandlerFn<E>) => {
if (!subscriptions[key]) {
subscriptions[key] = [];
}
const items: Array<EventHandlerFn<unknown>> = (subscriptions[key] as Array<EventHandlerFn<unknown>>).filter(
(h) => h !== handler,
);
subscriptions[key] = items;
};
/**
* Add typed subscription.
*/
const addSubscription = <E>(key: EventKinds, handler: EventHandlerFn<E>) => {
getSubscriptions(key).push(handler as EventHandlerFn<unknown>);
return () => {
removeSubscription(key, handler);
};
};
/**
* Fires an event.
*/
const fireEvent = <E>(key: EventKinds, event: E) => {
getSubscriptions(key).forEach((handler) => {
try {
handler(event);
} catch (e: any) {
console.error(`Unhandled exception in handler for ${key}: `, e);
}
});
};
return {
getSubscriptions,
addSubscription,
removeSubscription,
fireEvent,
subscriptions,
};
};
@@ -1,42 +0,0 @@
{
"compileOnSave": false,
"compilerOptions": {
"lib": [
"es2021",
"dom",
"dom.iterable",
"esnext",
"webworker"
],
"module": "CommonJS",
"target": "es5",
"strict": true,
"moduleResolution": "node",
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"declaration": true,
"baseUrl": ".",
"esModuleInterop": true,
"allowJs": true
},
"include": [
"src/**/*.ts"
],
"exclude": [
"jest.*",
"webpack.config.js",
"webpack.prod.js",
"webpack.common.js",
"node_modules",
"**/node_modules",
"dist",
"**/dist",
"scripts",
"jest",
"__tests__",
"**/__tests__",
"__jest__",
"**/__jest__",
"config/*"
]
}
+10 -768
View File
File diff suppressed because it is too large Load Diff